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, ...@@ -306,6 +306,7 @@ Context::Context(rx::EGLImplFactory *implFactory,
mCurrentSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)), mCurrentSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)),
mCurrentDisplay(static_cast<egl::Display *>(EGL_NO_DISPLAY)), mCurrentDisplay(static_cast<egl::Display *>(EGL_NO_DISPLAY)),
mWebGLContext(GetWebGLContext(attribs)), mWebGLContext(GetWebGLContext(attribs)),
mBufferAccessValidationEnabled(false),
mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)), mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)),
mMemoryProgramCache(memoryProgramCache), mMemoryProgramCache(memoryProgramCache),
mVertexArrayObserverBinding(this, kVertexArraySubjectIndex), mVertexArrayObserverBinding(this, kVertexArraySubjectIndex),
...@@ -3434,6 +3435,13 @@ void Context::updateCaps() ...@@ -3434,6 +3435,13 @@ void Context::updateCaps()
mComputeDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES_INIT, robustInit); mComputeDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES_INIT, robustInit);
mComputeDirtyObjects.set(State::DIRTY_OBJECT_IMAGES_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. // Reinitialize state cache after extension changes.
mStateCache.initialize(this); mStateCache.initialize(this);
} }
...@@ -8097,6 +8105,14 @@ StateCache::StateCache() ...@@ -8097,6 +8105,14 @@ StateCache::StateCache()
StateCache::~StateCache() = default; StateCache::~StateCache() = default;
ANGLE_INLINE void StateCache::updateVertexElementLimits(Context *context)
{
if (context->isBufferAccessValidationEnabled())
{
updateVertexElementLimitsImpl(context);
}
}
void StateCache::initialize(Context *context) void StateCache::initialize(Context *context)
{ {
updateValidDrawModes(context); updateValidDrawModes(context);
...@@ -8136,8 +8152,10 @@ void StateCache::updateActiveAttribsMask(Context *context) ...@@ -8136,8 +8152,10 @@ void StateCache::updateActiveAttribsMask(Context *context)
mCachedHasAnyEnabledClientAttrib = (clientAttribs & enabledAttribs).any(); mCachedHasAnyEnabledClientAttrib = (clientAttribs & enabledAttribs).any();
} }
void StateCache::updateVertexElementLimits(Context *context) void StateCache::updateVertexElementLimitsImpl(Context *context)
{ {
ASSERT(context->isBufferAccessValidationEnabled());
const VertexArray *vao = context->getState().getVertexArray(); const VertexArray *vao = context->getState().getVertexArray();
mCachedNonInstancedVertexElementLimit = std::numeric_limits<GLint64>::max(); mCachedNonInstancedVertexElementLimit = std::numeric_limits<GLint64>::max();
......
...@@ -238,6 +238,7 @@ class StateCache final : angle::NonCopyable ...@@ -238,6 +238,7 @@ class StateCache final : angle::NonCopyable
// Cache update functions. // Cache update functions.
void updateActiveAttribsMask(Context *context); void updateActiveAttribsMask(Context *context);
void updateVertexElementLimits(Context *context); void updateVertexElementLimits(Context *context);
void updateVertexElementLimitsImpl(Context *context);
void updateValidDrawModes(Context *context); void updateValidDrawModes(Context *context);
void updateValidBindTextureTypes(Context *context); void updateValidBindTextureTypes(Context *context);
void updateValidDrawElementsTypes(Context *context); void updateValidDrawElementsTypes(Context *context);
...@@ -1805,6 +1806,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -1805,6 +1806,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
void onSamplerUniformChange(size_t textureUnitIndex); void onSamplerUniformChange(size_t textureUnitIndex);
bool isBufferAccessValidationEnabled() const { return mBufferAccessValidationEnabled; }
private: private:
void initialize(); void initialize();
...@@ -1913,6 +1916,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -1913,6 +1916,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
egl::Surface *mCurrentSurface; egl::Surface *mCurrentSurface;
egl::Display *mCurrentDisplay; egl::Display *mCurrentDisplay;
const bool mWebGLContext; const bool mWebGLContext;
bool mBufferAccessValidationEnabled;
const bool mExtensionsEnabled; const bool mExtensionsEnabled;
MemoryProgramCache *mMemoryProgramCache; MemoryProgramCache *mMemoryProgramCache;
......
...@@ -86,7 +86,9 @@ AttributesMask VertexArrayState::getBindingToAttributesMask(GLuint bindingIndex) ...@@ -86,7 +86,9 @@ AttributesMask VertexArrayState::getBindingToAttributesMask(GLuint bindingIndex)
} }
// Set an attribute using a new binding. // 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); ASSERT(attribIndex < MAX_VERTEX_ATTRIBS && newBindingIndex < MAX_VERTEX_ATTRIB_BINDINGS);
...@@ -107,7 +109,11 @@ void VertexArrayState::setAttribBinding(size_t attribIndex, GLuint newBindingInd ...@@ -107,7 +109,11 @@ void VertexArrayState::setAttribBinding(size_t attribIndex, GLuint newBindingInd
// Set the attribute using the new binding. // Set the attribute using the new binding.
attrib.bindingIndex = newBindingIndex; attrib.bindingIndex = newBindingIndex;
attrib.updateCachedElementLimit(newBinding);
if (context->isBufferAccessValidationEnabled())
{
attrib.updateCachedElementLimit(newBinding);
}
bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped(); bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
mCachedMappedArrayBuffers.set(attribIndex, isMapped); mCachedMappedArrayBuffers.set(attribIndex, isMapped);
...@@ -212,8 +218,12 @@ ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex, ...@@ -212,8 +218,12 @@ ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex,
mDirtyBindingBits[bindingIndex].set(dirtyBindingBit); 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()) for (size_t boundAttribute : binding->getBoundAttributesMask())
{ {
mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding); mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
...@@ -259,7 +269,7 @@ void VertexArray::bindVertexBufferImpl(const Context *context, ...@@ -259,7 +269,7 @@ void VertexArray::bindVertexBufferImpl(const Context *context,
binding->setStride(stride); binding->setStride(stride);
updateObserverBinding(bindingIndex); updateObserverBinding(bindingIndex);
updateCachedBufferBindingSize(binding); updateCachedBufferBindingSize(context, binding);
updateCachedTransformFeedbackBindingValidation(bindingIndex, boundBuffer); updateCachedTransformFeedbackBindingValidation(bindingIndex, boundBuffer);
updateCachedMappedArrayBuffers(binding); updateCachedMappedArrayBuffers(binding);
...@@ -295,7 +305,7 @@ void VertexArray::setVertexAttribBinding(const Context *context, ...@@ -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. // In ES 3.0 contexts, the binding cannot change, hence the code below is unreachable.
ASSERT(context->getClientVersion() >= ES_3_1); ASSERT(context->getClientVersion() >= ES_3_1);
mState.setAttribBinding(attribIndex, bindingIndex); mState.setAttribBinding(context, attribIndex, bindingIndex);
setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_BINDING); setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_BINDING);
...@@ -501,7 +511,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context, ...@@ -501,7 +511,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
case angle::SubjectMessage::STORAGE_CHANGED: case angle::SubjectMessage::STORAGE_CHANGED:
if (!IsElementArrayBufferSubjectIndex(index)) if (!IsElementArrayBufferSubjectIndex(index))
{ {
updateCachedBufferBindingSize(&mState.mVertexBindings[index]); updateCachedBufferBindingSize(context, &mState.mVertexBindings[index]);
} }
setDependentDirtyBit(context, false, index); setDependentDirtyBit(context, false, index);
break; break;
......
...@@ -63,7 +63,7 @@ class VertexArrayState final : angle::NonCopyable ...@@ -63,7 +63,7 @@ class VertexArrayState final : angle::NonCopyable
return mVertexAttributes[attribIndex].bindingIndex; 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. // Extra validation performed on the Vertex Array.
bool hasEnabledNullPointerClientArray() const; bool hasEnabledNullPointerClientArray() const;
...@@ -294,7 +294,7 @@ class VertexArray final : public angle::ObserverInterface, ...@@ -294,7 +294,7 @@ class VertexArray final : public angle::ObserverInterface,
angle::SubjectIndex index); angle::SubjectIndex index);
// These are used to optimize draw call validation. // 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 updateCachedTransformFeedbackBindingValidation(size_t bindingIndex, const Buffer *buffer);
void updateCachedMappedArrayBuffers(VertexBinding *binding); void updateCachedMappedArrayBuffers(VertexBinding *binding);
......
...@@ -345,7 +345,11 @@ angle::Result VertexDataManager::StoreStaticAttrib(const gl::Context *context, ...@@ -345,7 +345,11 @@ angle::Result VertexDataManager::StoreStaticAttrib(const gl::Context *context,
const int offset = static_cast<int>(ComputeVertexAttributeOffset(attrib, binding)); const int offset = static_cast<int>(ComputeVertexAttributeOffset(attrib, binding));
ANGLE_TRY(bufferD3D->getData(context, &sourceData)); ANGLE_TRY(bufferD3D->getData(context, &sourceData));
sourceData += offset;
if (sourceData)
{
sourceData += offset;
}
unsigned int streamOffset = 0; unsigned int streamOffset = 0;
...@@ -363,8 +367,11 @@ angle::Result VertexDataManager::StoreStaticAttrib(const gl::Context *context, ...@@ -363,8 +367,11 @@ angle::Result VertexDataManager::StoreStaticAttrib(const gl::Context *context,
ElementsInBuffer(attrib, binding, static_cast<unsigned int>(bufferD3D->getSize())); ElementsInBuffer(attrib, binding, static_cast<unsigned int>(bufferD3D->getSize()));
int startIndex = offset / static_cast<int>(ComputeVertexAttributeStride(attrib, binding)); int startIndex = offset / static_cast<int>(ComputeVertexAttributeStride(attrib, binding));
ANGLE_TRY(staticBuffer->storeStaticAttribute(context, attrib, binding, -startIndex, if (totalCount > 0)
totalCount, 0, sourceData)); {
ANGLE_TRY(staticBuffer->storeStaticAttribute(context, attrib, binding, -startIndex,
totalCount, 0, sourceData));
}
} }
unsigned int firstElementOffset = unsigned int firstElementOffset =
......
...@@ -1047,6 +1047,11 @@ angle::Result Buffer11::NativeStorage::copyFromStorage(const gl::Context *contex ...@@ -1047,6 +1047,11 @@ angle::Result Buffer11::NativeStorage::copyFromStorage(const gl::Context *contex
clampedSize = std::min(clampedSize, mBufferSize - destOffset); clampedSize = std::min(clampedSize, mBufferSize - destOffset);
} }
if (clampedSize == 0)
{
return angle::Result::Continue;
}
if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK || if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK ||
source->getUsage() == BUFFER_USAGE_SYSTEM_MEMORY) source->getUsage() == BUFFER_USAGE_SYSTEM_MEMORY)
{ {
...@@ -1087,6 +1092,13 @@ angle::Result Buffer11::NativeStorage::resize(const gl::Context *context, ...@@ -1087,6 +1092,13 @@ angle::Result Buffer11::NativeStorage::resize(const gl::Context *context,
size_t size, size_t size,
bool preserveData) bool preserveData)
{ {
if (size == 0)
{
mBuffer.reset();
mBufferSize = 0;
return angle::Result::Continue;
}
D3D11_BUFFER_DESC bufferDesc; D3D11_BUFFER_DESC bufferDesc;
FillBufferDesc(&bufferDesc, mRenderer, mUsage, static_cast<unsigned int>(size)); FillBufferDesc(&bufferDesc, mRenderer, mUsage, static_cast<unsigned int>(size));
......
...@@ -689,7 +689,11 @@ egl::Error Renderer11::initialize() ...@@ -689,7 +689,11 @@ egl::Error Renderer11::initialize()
if (SUCCEEDED(result)) if (SUCCEEDED(result))
{ {
D3D11_MESSAGE_ID hideMessages[] = { 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 = {}; D3D11_INFO_QUEUE_FILTER filter = {};
filter.DenyList.NumIDs = static_cast<unsigned int>(ArraySize(hideMessages)); filter.DenyList.NumIDs = static_cast<unsigned int>(ArraySize(hideMessages));
......
...@@ -59,7 +59,14 @@ angle::Result Buffer9::setData(const gl::Context *context, ...@@ -59,7 +59,14 @@ angle::Result Buffer9::setData(const gl::Context *context,
angle::Result Buffer9::getData(const gl::Context *context, const uint8_t **outData) 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; return angle::Result::Continue;
} }
......
...@@ -282,7 +282,7 @@ egl::Error DisplayWGL::initializeImpl(egl::Display *display) ...@@ -282,7 +282,7 @@ egl::Error DisplayWGL::initializeImpl(egl::Display *display)
mHasRobustness = functionsGL->getGraphicsResetStatus != nullptr; mHasRobustness = functionsGL->getGraphicsResetStatus != nullptr;
if (mHasWGLCreateContextRobustness != mHasRobustness) 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."; "robustness.";
} }
...@@ -885,7 +885,7 @@ HGLRC DisplayWGL::createContextAttribs(const gl::Version &version, ...@@ -885,7 +885,7 @@ HGLRC DisplayWGL::createContextAttribs(const gl::Version &version,
egl::Error DisplayWGL::createRenderer(std::shared_ptr<RendererWGL> *outRenderer) egl::Error DisplayWGL::createRenderer(std::shared_ptr<RendererWGL> *outRenderer)
{ {
HGLRC context = nullptr; HGLRC context = nullptr;
HGLRC sharedContext = nullptr; HGLRC sharedContext = nullptr;
std::vector<int> workerContextAttribs; std::vector<int> workerContextAttribs;
......
...@@ -697,6 +697,11 @@ ANGLE_INLINE bool ValidateDrawAttribs(Context *context, int64_t maxVertex) ...@@ -697,6 +697,11 @@ ANGLE_INLINE bool ValidateDrawAttribs(Context *context, int64_t maxVertex)
ANGLE_INLINE bool ValidateDrawArraysAttribs(Context *context, GLint first, GLsizei count) ANGLE_INLINE bool ValidateDrawArraysAttribs(Context *context, GLint first, GLsizei count)
{ {
if (!context->isBufferAccessValidationEnabled())
{
return true;
}
// Check the computation of maxVertex doesn't overflow. // Check the computation of maxVertex doesn't overflow.
// - first < 0 has been checked as an error condition. // - first < 0 has been checked as an error condition.
// - if count <= 0, skip validating no-op draw calls. // - if count <= 0, skip validating no-op draw calls.
...@@ -715,6 +720,11 @@ ANGLE_INLINE bool ValidateDrawArraysAttribs(Context *context, GLint first, GLsiz ...@@ -715,6 +720,11 @@ ANGLE_INLINE bool ValidateDrawArraysAttribs(Context *context, GLint first, GLsiz
ANGLE_INLINE bool ValidateDrawInstancedAttribs(Context *context, GLint primcount) ANGLE_INLINE bool ValidateDrawInstancedAttribs(Context *context, GLint primcount)
{ {
if (!context->isBufferAccessValidationEnabled())
{
return true;
}
if ((primcount - 1) > context->getStateCache().getInstancedVertexElementLimit()) if ((primcount - 1) > context->getStateCache().getInstancedVertexElementLimit())
{ {
RecordDrawAttribsError(context); RecordDrawAttribsError(context);
...@@ -897,7 +907,7 @@ ANGLE_INLINE bool ValidateDrawElementsCommon(Context *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. // Use the parameter buffer to retrieve and cache the index range.
IndexRange indexRange{IndexRange::Undefined()}; IndexRange indexRange{IndexRange::Undefined()};
......
...@@ -327,6 +327,244 @@ TEST_P(RobustBufferAccessBehaviorTest, NoBufferData) ...@@ -327,6 +327,244 @@ TEST_P(RobustBufferAccessBehaviorTest, NoBufferData)
ASSERT_GL_NO_ERROR(); 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, ANGLE_INSTANTIATE_TEST(RobustBufferAccessBehaviorTest,
ES2_D3D9(), ES2_D3D9(),
ES2_D3D11_FL9_3(), ES2_D3D11_FL9_3(),
......
...@@ -2480,6 +2480,12 @@ class WebGL2ValidationStateChangeTest : public ValidationStateChangeTest ...@@ -2480,6 +2480,12 @@ class WebGL2ValidationStateChangeTest : public ValidationStateChangeTest
class ValidationStateChangeTestES31 : public ANGLETest 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. // 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. // This isn't guaranteed to produce an error by GL. But we assume ANGLE always errors.
TEST_P(ValidationStateChangeTest, MapBufferAndDraw) TEST_P(ValidationStateChangeTest, MapBufferAndDraw)
...@@ -2626,7 +2632,7 @@ TEST_P(ValidationStateChangeTestES31, MapBufferAndDrawWithDivisor) ...@@ -2626,7 +2632,7 @@ TEST_P(ValidationStateChangeTestES31, MapBufferAndDrawWithDivisor)
} }
// Tests that changing a vertex binding with glVertexAttribDivisor updates the buffer size check. // 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. // Initialize program and set up state.
ANGLE_GL_PROGRAM(program, kColorVS, kColorFS); ANGLE_GL_PROGRAM(program, kColorVS, kColorFS);
...@@ -3379,3 +3385,4 @@ ANGLE_INSTANTIATE_TEST(SimpleStateChangeTestES31, ES31_OPENGL(), ES31_D3D11()); ...@@ -3379,3 +3385,4 @@ ANGLE_INSTANTIATE_TEST(SimpleStateChangeTestES31, ES31_OPENGL(), ES31_D3D11());
ANGLE_INSTANTIATE_TEST(ValidationStateChangeTest, ES3_D3D11(), ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(ValidationStateChangeTest, ES3_D3D11(), ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(WebGL2ValidationStateChangeTest, ES3_D3D11(), ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(WebGL2ValidationStateChangeTest, ES3_D3D11(), ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(ValidationStateChangeTestES31, ES31_OPENGL(), ES31_D3D11()); ANGLE_INSTANTIATE_TEST(ValidationStateChangeTestES31, ES31_OPENGL(), ES31_D3D11());
ANGLE_INSTANTIATE_TEST(WebGLComputeValidationStateChangeTest, ES31_D3D11(), ES31_OPENGL());
...@@ -763,9 +763,19 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation) ...@@ -763,9 +763,19 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation)
EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1); 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. // Verify that drawing with a large out-of-range offset generates INVALID_OPERATION.
// Requires an ANGLE or similar implementation that checks buffer bounds. // Requires WebGL compatibility with robust access behaviour disabled.
TEST_P(VertexAttributeTest, ANGLEDrawArraysBufferTooSmall) TEST_P(VertexAttributeOORTest, ANGLEDrawArraysBufferTooSmall)
{ {
// Test skipped due to supporting GL_KHR_robust_buffer_access_behavior // Test skipped due to supporting GL_KHR_robust_buffer_access_behavior
ANGLE_SKIP_TEST_IF(extensionEnabled("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) ...@@ -783,8 +793,8 @@ TEST_P(VertexAttributeTest, ANGLEDrawArraysBufferTooSmall)
} }
// Verify that index draw with an out-of-range offset generates INVALID_OPERATION. // Verify that index draw with an out-of-range offset generates INVALID_OPERATION.
// Requires an ANGLE or similar implementation that checks buffer bounds. // Requires WebGL compatibility with robust access behaviour disabled.
TEST_P(VertexAttributeTest, ANGLEDrawElementsBufferTooSmall) TEST_P(VertexAttributeOORTest, ANGLEDrawElementsBufferTooSmall)
{ {
// Test skipped due to supporting GL_KHR_robust_buffer_access_behavior // Test skipped due to supporting GL_KHR_robust_buffer_access_behavior
ANGLE_SKIP_TEST_IF(extensionEnabled("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) ...@@ -801,7 +811,9 @@ TEST_P(VertexAttributeTest, ANGLEDrawElementsBufferTooSmall)
EXPECT_GL_ERROR(GL_INVALID_OPERATION); 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 // Test skipped due to supporting GL_KHR_robust_buffer_access_behavior
ANGLE_SKIP_TEST_IF(extensionEnabled("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, ...@@ -1977,6 +1989,13 @@ ANGLE_INSTANTIATE_TEST(VertexAttributeTest,
ES3_OPENGLES(), ES3_OPENGLES(),
ES2_VULKAN()); 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(VertexAttributeTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(VertexAttributeTestES31, ES31_D3D11(), ES31_OPENGL(), ES31_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