Commit d51fbe34 by Jamie Madill Committed by Commit Bot

Fold buffer access validation into extensions.

We only need to perform vertex array buffer validation if the WebGL compatiblity extension is enabled and robust access is not available. Although sometimes the range checks are useful for determining undefined behaviour they are not required by the OpenGL spec. They also slow down state updates significantly. This migrates the OOR tests into specific WebGL tests. It also requires a change to a Chromium test on the passthrough decoder. Improves perf by about 10% in the Vulkan VBO state change test. Also fixes some robust resource access cases for D3D11. Bug: angleproject:3000 Change-Id: Ice37f38f01c2f27bf32ed55657a30e69d8508335 Reviewed-on: https://chromium-review.googlesource.com/c/1390362Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 5904ee3f
......@@ -306,6 +306,7 @@ Context::Context(rx::EGLImplFactory *implFactory,
mCurrentSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)),
mCurrentDisplay(static_cast<egl::Display *>(EGL_NO_DISPLAY)),
mWebGLContext(GetWebGLContext(attribs)),
mBufferAccessValidationEnabled(false),
mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)),
mMemoryProgramCache(memoryProgramCache),
mVertexArrayObserverBinding(this, kVertexArraySubjectIndex),
......@@ -3434,6 +3435,13 @@ void Context::updateCaps()
mComputeDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES_INIT, robustInit);
mComputeDirtyObjects.set(State::DIRTY_OBJECT_IMAGES_INIT, robustInit);
// We need to validate buffer bounds if we are in a WebGL or robust access context and the
// back-end does not support robust buffer access behaviour.
if (!mSupportedExtensions.robustBufferAccessBehavior && (mState.isWebGL() || mRobustAccess))
{
mBufferAccessValidationEnabled = true;
}
// Reinitialize state cache after extension changes.
mStateCache.initialize(this);
}
......@@ -8097,6 +8105,14 @@ StateCache::StateCache()
StateCache::~StateCache() = default;
ANGLE_INLINE void StateCache::updateVertexElementLimits(Context *context)
{
if (context->isBufferAccessValidationEnabled())
{
updateVertexElementLimitsImpl(context);
}
}
void StateCache::initialize(Context *context)
{
updateValidDrawModes(context);
......@@ -8136,8 +8152,10 @@ void StateCache::updateActiveAttribsMask(Context *context)
mCachedHasAnyEnabledClientAttrib = (clientAttribs & enabledAttribs).any();
}
void StateCache::updateVertexElementLimits(Context *context)
void StateCache::updateVertexElementLimitsImpl(Context *context)
{
ASSERT(context->isBufferAccessValidationEnabled());
const VertexArray *vao = context->getState().getVertexArray();
mCachedNonInstancedVertexElementLimit = std::numeric_limits<GLint64>::max();
......
......@@ -238,6 +238,7 @@ class StateCache final : angle::NonCopyable
// Cache update functions.
void updateActiveAttribsMask(Context *context);
void updateVertexElementLimits(Context *context);
void updateVertexElementLimitsImpl(Context *context);
void updateValidDrawModes(Context *context);
void updateValidBindTextureTypes(Context *context);
void updateValidDrawElementsTypes(Context *context);
......@@ -1805,6 +1806,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
void onSamplerUniformChange(size_t textureUnitIndex);
bool isBufferAccessValidationEnabled() const { return mBufferAccessValidationEnabled; }
private:
void initialize();
......@@ -1913,6 +1916,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
egl::Surface *mCurrentSurface;
egl::Display *mCurrentDisplay;
const bool mWebGLContext;
bool mBufferAccessValidationEnabled;
const bool mExtensionsEnabled;
MemoryProgramCache *mMemoryProgramCache;
......
......@@ -86,7 +86,9 @@ AttributesMask VertexArrayState::getBindingToAttributesMask(GLuint bindingIndex)
}
// Set an attribute using a new binding.
void VertexArrayState::setAttribBinding(size_t attribIndex, GLuint newBindingIndex)
void VertexArrayState::setAttribBinding(const Context *context,
size_t attribIndex,
GLuint newBindingIndex)
{
ASSERT(attribIndex < MAX_VERTEX_ATTRIBS && newBindingIndex < MAX_VERTEX_ATTRIB_BINDINGS);
......@@ -107,7 +109,11 @@ void VertexArrayState::setAttribBinding(size_t attribIndex, GLuint newBindingInd
// Set the attribute using the new binding.
attrib.bindingIndex = newBindingIndex;
attrib.updateCachedElementLimit(newBinding);
if (context->isBufferAccessValidationEnabled())
{
attrib.updateCachedElementLimit(newBinding);
}
bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
mCachedMappedArrayBuffers.set(attribIndex, isMapped);
......@@ -212,8 +218,12 @@ ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex,
mDirtyBindingBits[bindingIndex].set(dirtyBindingBit);
}
ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *binding)
ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(const Context *context,
VertexBinding *binding)
{
if (!context->isBufferAccessValidationEnabled())
return;
for (size_t boundAttribute : binding->getBoundAttributesMask())
{
mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
......@@ -259,7 +269,7 @@ void VertexArray::bindVertexBufferImpl(const Context *context,
binding->setStride(stride);
updateObserverBinding(bindingIndex);
updateCachedBufferBindingSize(binding);
updateCachedBufferBindingSize(context, binding);
updateCachedTransformFeedbackBindingValidation(bindingIndex, boundBuffer);
updateCachedMappedArrayBuffers(binding);
......@@ -295,7 +305,7 @@ void VertexArray::setVertexAttribBinding(const Context *context,
// In ES 3.0 contexts, the binding cannot change, hence the code below is unreachable.
ASSERT(context->getClientVersion() >= ES_3_1);
mState.setAttribBinding(attribIndex, bindingIndex);
mState.setAttribBinding(context, attribIndex, bindingIndex);
setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_BINDING);
......@@ -501,7 +511,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
case angle::SubjectMessage::STORAGE_CHANGED:
if (!IsElementArrayBufferSubjectIndex(index))
{
updateCachedBufferBindingSize(&mState.mVertexBindings[index]);
updateCachedBufferBindingSize(context, &mState.mVertexBindings[index]);
}
setDependentDirtyBit(context, false, index);
break;
......
......@@ -63,7 +63,7 @@ class VertexArrayState final : angle::NonCopyable
return mVertexAttributes[attribIndex].bindingIndex;
}
void setAttribBinding(size_t attribIndex, GLuint newBindingIndex);
void setAttribBinding(const Context *context, size_t attribIndex, GLuint newBindingIndex);
// Extra validation performed on the Vertex Array.
bool hasEnabledNullPointerClientArray() const;
......@@ -294,7 +294,7 @@ class VertexArray final : public angle::ObserverInterface,
angle::SubjectIndex index);
// These are used to optimize draw call validation.
void updateCachedBufferBindingSize(VertexBinding *binding);
void updateCachedBufferBindingSize(const Context *context, VertexBinding *binding);
void updateCachedTransformFeedbackBindingValidation(size_t bindingIndex, const Buffer *buffer);
void updateCachedMappedArrayBuffers(VertexBinding *binding);
......
......@@ -345,7 +345,11 @@ angle::Result VertexDataManager::StoreStaticAttrib(const gl::Context *context,
const int offset = static_cast<int>(ComputeVertexAttributeOffset(attrib, binding));
ANGLE_TRY(bufferD3D->getData(context, &sourceData));
sourceData += offset;
if (sourceData)
{
sourceData += offset;
}
unsigned int streamOffset = 0;
......@@ -363,8 +367,11 @@ angle::Result VertexDataManager::StoreStaticAttrib(const gl::Context *context,
ElementsInBuffer(attrib, binding, static_cast<unsigned int>(bufferD3D->getSize()));
int startIndex = offset / static_cast<int>(ComputeVertexAttributeStride(attrib, binding));
ANGLE_TRY(staticBuffer->storeStaticAttribute(context, attrib, binding, -startIndex,
totalCount, 0, sourceData));
if (totalCount > 0)
{
ANGLE_TRY(staticBuffer->storeStaticAttribute(context, attrib, binding, -startIndex,
totalCount, 0, sourceData));
}
}
unsigned int firstElementOffset =
......
......@@ -1047,6 +1047,11 @@ angle::Result Buffer11::NativeStorage::copyFromStorage(const gl::Context *contex
clampedSize = std::min(clampedSize, mBufferSize - destOffset);
}
if (clampedSize == 0)
{
return angle::Result::Continue;
}
if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK ||
source->getUsage() == BUFFER_USAGE_SYSTEM_MEMORY)
{
......@@ -1087,6 +1092,13 @@ angle::Result Buffer11::NativeStorage::resize(const gl::Context *context,
size_t size,
bool preserveData)
{
if (size == 0)
{
mBuffer.reset();
mBufferSize = 0;
return angle::Result::Continue;
}
D3D11_BUFFER_DESC bufferDesc;
FillBufferDesc(&bufferDesc, mRenderer, mUsage, static_cast<unsigned int>(size));
......
......@@ -689,7 +689,11 @@ egl::Error Renderer11::initialize()
if (SUCCEEDED(result))
{
D3D11_MESSAGE_ID hideMessages[] = {
D3D11_MESSAGE_ID_DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET};
D3D11_MESSAGE_ID_DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET,
// Robust access behaviour makes out of bounds messages safe
D3D11_MESSAGE_ID_DEVICE_DRAW_VERTEX_BUFFER_TOO_SMALL,
};
D3D11_INFO_QUEUE_FILTER filter = {};
filter.DenyList.NumIDs = static_cast<unsigned int>(ArraySize(hideMessages));
......
......@@ -59,7 +59,14 @@ angle::Result Buffer9::setData(const gl::Context *context,
angle::Result Buffer9::getData(const gl::Context *context, const uint8_t **outData)
{
*outData = mMemory.data();
if (mMemory.empty())
{
*outData = nullptr;
}
else
{
*outData = mMemory.data();
}
return angle::Result::Continue;
}
......
......@@ -282,7 +282,7 @@ egl::Error DisplayWGL::initializeImpl(egl::Display *display)
mHasRobustness = functionsGL->getGraphicsResetStatus != nullptr;
if (mHasWGLCreateContextRobustness != mHasRobustness)
{
WARN() << "WGL_ARB_create_context_robustness exists but unable to OpenGL context with "
WARN() << "WGL_ARB_create_context_robustness exists but unable to create a context with "
"robustness.";
}
......@@ -885,7 +885,7 @@ HGLRC DisplayWGL::createContextAttribs(const gl::Version &version,
egl::Error DisplayWGL::createRenderer(std::shared_ptr<RendererWGL> *outRenderer)
{
HGLRC context = nullptr;
HGLRC context = nullptr;
HGLRC sharedContext = nullptr;
std::vector<int> workerContextAttribs;
......
......@@ -697,6 +697,11 @@ ANGLE_INLINE bool ValidateDrawAttribs(Context *context, int64_t maxVertex)
ANGLE_INLINE bool ValidateDrawArraysAttribs(Context *context, GLint first, GLsizei count)
{
if (!context->isBufferAccessValidationEnabled())
{
return true;
}
// Check the computation of maxVertex doesn't overflow.
// - first < 0 has been checked as an error condition.
// - if count <= 0, skip validating no-op draw calls.
......@@ -715,6 +720,11 @@ ANGLE_INLINE bool ValidateDrawArraysAttribs(Context *context, GLint first, GLsiz
ANGLE_INLINE bool ValidateDrawInstancedAttribs(Context *context, GLint primcount)
{
if (!context->isBufferAccessValidationEnabled())
{
return true;
}
if ((primcount - 1) > context->getStateCache().getInstancedVertexElementLimit())
{
RecordDrawAttribsError(context);
......@@ -897,7 +907,7 @@ ANGLE_INLINE bool ValidateDrawElementsCommon(Context *context,
}
}
if (!context->getExtensions().robustBufferAccessBehavior && primcount > 0)
if (context->isBufferAccessValidationEnabled() && primcount > 0)
{
// Use the parameter buffer to retrieve and cache the index range.
IndexRange indexRange{IndexRange::Undefined()};
......
......@@ -327,6 +327,244 @@ TEST_P(RobustBufferAccessBehaviorTest, NoBufferData)
ASSERT_GL_NO_ERROR();
}
constexpr char kWebGLVS[] = R"(attribute vec2 position;
attribute vec4 aOne;
attribute vec4 aTwo;
varying vec4 v;
uniform vec2 comparison;
bool isRobust(vec4 value) {
// The valid buffer range is filled with this value.
if (value.xy == comparison)
return true;
// Checking the w value is a bit complex.
return (value.xyz == vec3(0, 0, 0));
}
void main() {
gl_Position = vec4(position, 0, 1);
if (isRobust(aOne) && isRobust(aTwo)) {
v = vec4(0, 1, 0, 1);
} else {
v = vec4(1, 0, 0, 1);
}
})";
constexpr char kWebGLFS[] = R"(precision mediump float;
varying vec4 v;
void main() {
gl_FragColor = v;
})";
// Test buffer with interleaved (3+2) float vectors. Adapted from WebGL test
// conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest, InterleavedAttributes)
{
ANGLE_SKIP_TEST_IF(!initExtension());
ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
glUseProgram(program);
constexpr GLint kPosLoc = 0;
constexpr GLint kOneLoc = 1;
constexpr GLint kTwoLoc = 2;
ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
// Create a buffer of 200 valid sets of quad lists.
constexpr size_t kNumQuads = 200;
using QuadVerts = std::array<Vector3, 6>;
std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
GLBuffer positionBuf;
glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(kPosLoc);
constexpr GLfloat kDefaultFloat = 0.2f;
std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
GLBuffer vbo;
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// enough for 9 vertices, so 3 triangles
glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_STATIC_DRAW);
// bind first 3 elements, with a stride of 5 float elements
glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
// bind 2 elements, starting after the first 3; same stride of 5 float elements
glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
reinterpret_cast<const GLvoid *>(3 * 4));
glEnableVertexAttribArray(kOneLoc);
glEnableVertexAttribArray(kTwoLoc);
// set test uniform
GLint uniLoc = glGetUniformLocation(program, "comparison");
ASSERT_NE(-1, uniLoc);
glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
// Draw out of bounds.
glDrawArrays(GL_TRIANGLES, 0, 10000);
GLenum err = glGetError();
if (err == GL_NO_ERROR)
{
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
else
{
EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
}
glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
err = glGetError();
if (err == GL_NO_ERROR)
{
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
else
{
EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
}
}
// Tests redefining an empty buffer. Adapted from WebGL test
// conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest, EmptyBuffer)
{
ANGLE_SKIP_TEST_IF(!initExtension());
// AMD GL does not support robustness. http://anglebug.com/3099
ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
glUseProgram(program);
constexpr GLint kPosLoc = 0;
constexpr GLint kOneLoc = 1;
constexpr GLint kTwoLoc = 2;
ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
// Create a buffer of 200 valid sets of quad lists.
constexpr size_t kNumQuads = 200;
using QuadVerts = std::array<Vector3, 6>;
std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
GLBuffer positionBuf;
glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(kPosLoc);
// set test uniform
GLint uniLoc = glGetUniformLocation(program, "comparison");
ASSERT_NE(-1, uniLoc);
glUniform2f(uniLoc, 0, 0);
// Define empty buffer.
GLBuffer buffer;
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(kOneLoc);
glDrawArrays(GL_TRIANGLES, 0, 3);
GLenum err = glGetError();
if (err == GL_NO_ERROR)
{
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
else
{
EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
}
// Redefine buffer with 3 float vectors.
constexpr GLfloat kFloats[] = {0, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0};
glBufferData(GL_ARRAY_BUFFER, sizeof(kFloats), kFloats, GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, 3);
ASSERT_GL_NO_ERROR();
}
// Tests robust buffer access with dynamic buffer usage.
TEST_P(RobustBufferAccessBehaviorTest, DynamicBuffer)
{
ANGLE_SKIP_TEST_IF(!initExtension());
ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
glUseProgram(program);
constexpr GLint kPosLoc = 0;
constexpr GLint kOneLoc = 1;
constexpr GLint kTwoLoc = 2;
ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
// Create a buffer of 200 valid sets of quad lists.
constexpr size_t kNumQuads = 200;
using QuadVerts = std::array<Vector3, 6>;
std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
GLBuffer positionBuf;
glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(kPosLoc);
constexpr GLfloat kDefaultFloat = 0.2f;
std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
GLBuffer vbo;
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// enough for 9 vertices, so 3 triangles
glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_DYNAMIC_DRAW);
// bind first 3 elements, with a stride of 5 float elements
glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
// bind 2 elements, starting after the first 3; same stride of 5 float elements
glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
reinterpret_cast<const GLvoid *>(3 * 4));
glEnableVertexAttribArray(kOneLoc);
glEnableVertexAttribArray(kTwoLoc);
// set test uniform
GLint uniLoc = glGetUniformLocation(program, "comparison");
ASSERT_NE(-1, uniLoc);
glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
// Draw out of bounds.
glDrawArrays(GL_TRIANGLES, 0, 10000);
GLenum err = glGetError();
if (err == GL_NO_ERROR)
{
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
else
{
EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
}
glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
err = glGetError();
if (err == GL_NO_ERROR)
{
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
else
{
EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
}
}
ANGLE_INSTANTIATE_TEST(RobustBufferAccessBehaviorTest,
ES2_D3D9(),
ES2_D3D11_FL9_3(),
......
......@@ -2480,6 +2480,12 @@ class WebGL2ValidationStateChangeTest : public ValidationStateChangeTest
class ValidationStateChangeTestES31 : public ANGLETest
{};
class WebGLComputeValidationStateChangeTest : public ANGLETest
{
public:
WebGLComputeValidationStateChangeTest() { setWebGLCompatibilityEnabled(true); }
};
// Tests that mapping and unmapping an array buffer in various ways causes rendering to fail.
// This isn't guaranteed to produce an error by GL. But we assume ANGLE always errors.
TEST_P(ValidationStateChangeTest, MapBufferAndDraw)
......@@ -2626,7 +2632,7 @@ TEST_P(ValidationStateChangeTestES31, MapBufferAndDrawWithDivisor)
}
// Tests that changing a vertex binding with glVertexAttribDivisor updates the buffer size check.
TEST_P(ValidationStateChangeTestES31, DrawPastEndOfBufferWithDivisor)
TEST_P(WebGLComputeValidationStateChangeTest, DrawPastEndOfBufferWithDivisor)
{
// Initialize program and set up state.
ANGLE_GL_PROGRAM(program, kColorVS, kColorFS);
......@@ -3379,3 +3385,4 @@ ANGLE_INSTANTIATE_TEST(SimpleStateChangeTestES31, ES31_OPENGL(), ES31_D3D11());
ANGLE_INSTANTIATE_TEST(ValidationStateChangeTest, ES3_D3D11(), ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(WebGL2ValidationStateChangeTest, ES3_D3D11(), ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(ValidationStateChangeTestES31, ES31_OPENGL(), ES31_D3D11());
ANGLE_INSTANTIATE_TEST(WebGLComputeValidationStateChangeTest, ES31_D3D11(), ES31_OPENGL());
......@@ -763,9 +763,19 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation)
EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1);
}
class VertexAttributeOORTest : public VertexAttributeTest
{
public:
VertexAttributeOORTest()
{
setWebGLCompatibilityEnabled(true);
setRobustAccess(false);
}
};
// Verify that drawing with a large out-of-range offset generates INVALID_OPERATION.
// Requires an ANGLE or similar implementation that checks buffer bounds.
TEST_P(VertexAttributeTest, ANGLEDrawArraysBufferTooSmall)
// Requires WebGL compatibility with robust access behaviour disabled.
TEST_P(VertexAttributeOORTest, ANGLEDrawArraysBufferTooSmall)
{
// Test skipped due to supporting GL_KHR_robust_buffer_access_behavior
ANGLE_SKIP_TEST_IF(extensionEnabled("GL_KHR_robust_buffer_access_behavior"));
......@@ -783,8 +793,8 @@ TEST_P(VertexAttributeTest, ANGLEDrawArraysBufferTooSmall)
}
// Verify that index draw with an out-of-range offset generates INVALID_OPERATION.
// Requires an ANGLE or similar implementation that checks buffer bounds.
TEST_P(VertexAttributeTest, ANGLEDrawElementsBufferTooSmall)
// Requires WebGL compatibility with robust access behaviour disabled.
TEST_P(VertexAttributeOORTest, ANGLEDrawElementsBufferTooSmall)
{
// Test skipped due to supporting GL_KHR_robust_buffer_access_behavior
ANGLE_SKIP_TEST_IF(extensionEnabled("GL_KHR_robust_buffer_access_behavior"));
......@@ -801,7 +811,9 @@ TEST_P(VertexAttributeTest, ANGLEDrawElementsBufferTooSmall)
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
TEST_P(VertexAttributeTest, ANGLEDrawArraysOutOfBoundsCases)
// Verify that DrawArarys with an out-of-range offset generates INVALID_OPERATION.
// Requires WebGL compatibility with robust access behaviour disabled.
TEST_P(VertexAttributeOORTest, ANGLEDrawArraysOutOfBoundsCases)
{
// Test skipped due to supporting GL_KHR_robust_buffer_access_behavior
ANGLE_SKIP_TEST_IF(extensionEnabled("GL_KHR_robust_buffer_access_behavior"));
......@@ -1977,6 +1989,13 @@ ANGLE_INSTANTIATE_TEST(VertexAttributeTest,
ES3_OPENGLES(),
ES2_VULKAN());
ANGLE_INSTANTIATE_TEST(VertexAttributeOORTest,
ES2_D3D9(),
ES2_D3D11(),
ES2_OPENGL(),
ES2_OPENGLES(),
ES2_VULKAN());
ANGLE_INSTANTIATE_TEST(VertexAttributeTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(VertexAttributeTestES31, ES31_D3D11(), ES31_OPENGL(), ES31_OPENGLES());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment