Commit b9f92504 by Ken Russell Committed by Commit Bot

Update stencil validation rules for WebGL

Based on kbr's patch: https://chromium-review.googlesource.com/c/angle/angle/+/890605 Implements new rules in this revised WebGL conformance test: https://github.com/KhronosGroup/WebGL/pull/2583 BUG=chromium:806557 Change-Id: I84701dd7156f0bc4a45ba68e63cb962d2d54c2e5 Reviewed-on: https://chromium-review.googlesource.com/952567 Commit-Queue: Kai Ninomiya <kainino@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent e79d0f86
...@@ -1838,18 +1838,24 @@ bool Framebuffer::formsRenderingFeedbackLoopWith(const State &state) const ...@@ -1838,18 +1838,24 @@ bool Framebuffer::formsRenderingFeedbackLoopWith(const State &state) const
} }
} }
// Note: we assume the front and back masks are the same for WebGL.
const FramebufferAttachment *stencil = getStencilbuffer(); const FramebufferAttachment *stencil = getStencilbuffer();
ASSERT(dsState.stencilBackWritemask == dsState.stencilWritemask); if (dsState.stencilTest && stencil)
if (stencil && stencil->type() == GL_TEXTURE && dsState.stencilTest && {
dsState.stencilWritemask != 0) GLuint stencilSize = stencil->getStencilSize();
{ ASSERT(stencilSize <= 8);
// Skip the feedback loop check if depth/stencil point to the same resource. GLuint maxStencilValue = (1 << stencilSize) - 1;
if (!depth || *stencil != *depth) // We assume the front and back masks are the same for WebGL.
ASSERT((dsState.stencilBackWritemask & maxStencilValue) ==
(dsState.stencilWritemask & maxStencilValue));
if (stencil->type() == GL_TEXTURE && dsState.stencilWritemask != 0)
{ {
if (program->samplesFromTexture(state, stencil->id())) // Skip the feedback loop check if depth/stencil point to the same resource.
if (!depth || *stencil != *depth)
{ {
return true; if (program->samplesFromTexture(state, stencil->id()))
{
return true;
}
} }
} }
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "libANGLE/renderer/d3d/d3d11/StateManager11.h" #include "libANGLE/renderer/d3d/d3d11/StateManager11.h"
#include "common/bitset_utils.h" #include "common/bitset_utils.h"
#include "common/mathutil.h"
#include "common/utilities.h" #include "common/utilities.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Query.h" #include "libANGLE/Query.h"
...@@ -1154,7 +1155,8 @@ gl::Error StateManager11::syncDepthStencilState(const gl::State &glState) ...@@ -1154,7 +1155,8 @@ gl::Error StateManager11::syncDepthStencilState(const gl::State &glState)
} }
ASSERT((mCurDepthStencilState.stencilWritemask & maxStencil) == ASSERT((mCurDepthStencilState.stencilWritemask & maxStencil) ==
(mCurDepthStencilState.stencilBackWritemask & maxStencil)); (mCurDepthStencilState.stencilBackWritemask & maxStencil));
ASSERT(mCurStencilRef == mCurStencilBackRef); ASSERT(gl::clamp(mCurStencilRef, 0, static_cast<int>(maxStencil)) ==
gl::clamp(mCurStencilBackRef, 0, static_cast<int>(maxStencil)));
ASSERT((mCurDepthStencilState.stencilMask & maxStencil) == ASSERT((mCurDepthStencilState.stencilMask & maxStencil) ==
(mCurDepthStencilState.stencilBackMask & maxStencil)); (mCurDepthStencilState.stencilBackMask & maxStencil));
...@@ -1170,11 +1172,20 @@ gl::Error StateManager11::syncDepthStencilState(const gl::State &glState) ...@@ -1170,11 +1172,20 @@ gl::Error StateManager11::syncDepthStencilState(const gl::State &glState)
if (mCurDisableStencil.value()) if (mCurDisableStencil.value())
{ {
modifiedGLState.stencilWritemask = 0; modifiedGLState.stencilTest = false;
}
if (!modifiedGLState.stencilTest)
{
modifiedGLState.stencilWritemask = 0;
modifiedGLState.stencilBackWritemask = 0; modifiedGLState.stencilBackWritemask = 0;
modifiedGLState.stencilTest = false;
} }
// If STENCIL_TEST is disabled in glState, stencil testing and writing should be disabled.
// Verify that's true in the modifiedGLState so it is propagated to d3dState.
ASSERT(glState.getDepthStencilState().stencilTest ||
(!modifiedGLState.stencilTest && modifiedGLState.stencilWritemask == 0 &&
modifiedGLState.stencilBackWritemask == 0));
const d3d11::DepthStencilState *d3dState = nullptr; const d3d11::DepthStencilState *d3dState = nullptr;
ANGLE_TRY(mRenderer->getDepthStencilState(modifiedGLState, &d3dState)); ANGLE_TRY(mRenderer->getDepthStencilState(modifiedGLState, &d3dState));
ASSERT(d3dState); ASSERT(d3dState);
...@@ -1187,7 +1198,7 @@ gl::Error StateManager11::syncDepthStencilState(const gl::State &glState) ...@@ -1187,7 +1198,7 @@ gl::Error StateManager11::syncDepthStencilState(const gl::State &glState)
"Unexpected value of D3D11_DEFAULT_STENCIL_READ_MASK"); "Unexpected value of D3D11_DEFAULT_STENCIL_READ_MASK");
static_assert(D3D11_DEFAULT_STENCIL_WRITE_MASK == 0xFF, static_assert(D3D11_DEFAULT_STENCIL_WRITE_MASK == 0xFF,
"Unexpected value of D3D11_DEFAULT_STENCIL_WRITE_MASK"); "Unexpected value of D3D11_DEFAULT_STENCIL_WRITE_MASK");
UINT dxStencilRef = std::min<UINT>(mCurStencilRef, 0xFFu); UINT dxStencilRef = static_cast<UINT>(gl::clamp(mCurStencilRef, 0, 0xFF));
mRenderer->getDeviceContext()->OMSetDepthStencilState(d3dState->get(), dxStencilRef); mRenderer->getDeviceContext()->OMSetDepthStencilState(d3dState->get(), dxStencilRef);
......
...@@ -2515,28 +2515,35 @@ bool ValidateDrawBase(Context *context, GLenum mode, GLsizei count) ...@@ -2515,28 +2515,35 @@ bool ValidateDrawBase(Context *context, GLenum mode, GLsizei count)
Framebuffer *framebuffer = state.getDrawFramebuffer(); Framebuffer *framebuffer = state.getDrawFramebuffer();
if (context->getLimitations().noSeparateStencilRefsAndMasks || extensions.webglCompatibility) if (context->getLimitations().noSeparateStencilRefsAndMasks || extensions.webglCompatibility)
{ {
ASSERT(framebuffer);
const FramebufferAttachment *dsAttachment = const FramebufferAttachment *dsAttachment =
framebuffer->getStencilOrDepthStencilAttachment(); framebuffer->getStencilOrDepthStencilAttachment();
GLuint stencilBits = dsAttachment ? dsAttachment->getStencilSize() : 0; const GLuint stencilBits = dsAttachment ? dsAttachment->getStencilSize() : 0;
GLuint minimumRequiredStencilMask = (1 << stencilBits) - 1; ASSERT(stencilBits <= 8);
const DepthStencilState &depthStencilState = state.getDepthStencilState(); const DepthStencilState &depthStencilState = state.getDepthStencilState();
if (depthStencilState.stencilTest && stencilBits > 0)
{
GLuint maxStencilValue = (1 << stencilBits) - 1;
bool differentRefs = state.getStencilRef() != state.getStencilBackRef(); bool differentRefs =
bool differentWritemasks = clamp(state.getStencilRef(), 0, static_cast<GLint>(maxStencilValue)) !=
(depthStencilState.stencilWritemask & minimumRequiredStencilMask) != clamp(state.getStencilBackRef(), 0, static_cast<GLint>(maxStencilValue));
(depthStencilState.stencilBackWritemask & minimumRequiredStencilMask); bool differentWritemasks = (depthStencilState.stencilWritemask & maxStencilValue) !=
bool differentMasks = (depthStencilState.stencilMask & minimumRequiredStencilMask) != (depthStencilState.stencilBackWritemask & maxStencilValue);
(depthStencilState.stencilBackMask & minimumRequiredStencilMask); bool differentMasks = (depthStencilState.stencilMask & maxStencilValue) !=
(depthStencilState.stencilBackMask & maxStencilValue);
if (differentRefs || differentWritemasks || differentMasks) if (differentRefs || differentWritemasks || differentMasks)
{
if (!extensions.webglCompatibility)
{ {
ERR() << "This ANGLE implementation does not support separate front/back stencil " if (!extensions.webglCompatibility)
"writemasks, reference values, or stencil mask values."; {
ERR() << "This ANGLE implementation does not support separate front/back "
"stencil writemasks, reference values, or stencil mask values.";
}
ANGLE_VALIDATION_ERR(context, InvalidOperation(), StencilReferenceMaskOrMismatch);
return false;
} }
ANGLE_VALIDATION_ERR(context, InvalidOperation(), StencilReferenceMaskOrMismatch);
return false;
} }
} }
......
...@@ -197,6 +197,7 @@ class WebGLCompatibilityTest : public ANGLETest ...@@ -197,6 +197,7 @@ class WebGLCompatibilityTest : public ANGLETest
glCheckFramebufferStatus(GL_FRAMEBUFFER)); glCheckFramebufferStatus(GL_FRAMEBUFFER));
return; return;
} }
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
const std::string renderingVs = const std::string renderingVs =
...@@ -225,6 +226,8 @@ class WebGLCompatibilityTest : public ANGLETest ...@@ -225,6 +226,8 @@ class WebGLCompatibilityTest : public ANGLETest
0, 0, GLColor32F(floatData[0], floatData[1], floatData[2], floatData[3]), 1.0f); 0, 0, GLColor32F(floatData[0], floatData[1], floatData[2], floatData[3]), 1.0f);
} }
void TestDifferentStencilMaskAndRef(GLenum errIfMismatch);
// Called from RenderingFeedbackLoopWithDrawBuffersEXT. // Called from RenderingFeedbackLoopWithDrawBuffersEXT.
void drawBuffersEXTFeedbackLoop(GLuint program, void drawBuffersEXTFeedbackLoop(GLuint program,
const std::array<GLenum, 2> &drawBuffers, const std::array<GLenum, 2> &drawBuffers,
...@@ -1182,8 +1185,9 @@ TEST_P(WebGLCompatibilityTest, NullPixelDataForSubImage) ...@@ -1182,8 +1185,9 @@ TEST_P(WebGLCompatibilityTest, NullPixelDataForSubImage)
} }
} }
// Tests the WebGL requirement of having the same stencil mask, writemask and ref for fron and back // Tests the WebGL requirement of having the same stencil mask, writemask and ref for front and back
TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef) // (when stencil testing is enabled)
void WebGLCompatibilityTest::TestDifferentStencilMaskAndRef(GLenum errIfMismatch)
{ {
// Run the test in an FBO to make sure we have some stencil bits. // Run the test in an FBO to make sure we have some stencil bits.
GLRenderbuffer renderbuffer; GLRenderbuffer renderbuffer;
...@@ -1209,7 +1213,7 @@ TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef) ...@@ -1209,7 +1213,7 @@ TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef)
// Having a different front - back write mask generates an error. // Having a different front - back write mask generates an error.
glStencilMaskSeparate(GL_FRONT, 1); glStencilMaskSeparate(GL_FRONT, 1);
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION); EXPECT_GL_ERROR(errIfMismatch);
// Setting both write masks separately to the same value is valid. // Setting both write masks separately to the same value is valid.
glStencilMaskSeparate(GL_BACK, 1); glStencilMaskSeparate(GL_BACK, 1);
...@@ -1219,7 +1223,7 @@ TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef) ...@@ -1219,7 +1223,7 @@ TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef)
// Having a different stencil front - back mask generates an error // Having a different stencil front - back mask generates an error
glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 1); glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 1);
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION); EXPECT_GL_ERROR(errIfMismatch);
// Setting both masks separately to the same value is valid. // Setting both masks separately to the same value is valid.
glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 1); glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 1);
...@@ -1229,7 +1233,7 @@ TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef) ...@@ -1229,7 +1233,7 @@ TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef)
// Having a different stencil front - back reference generates an error // Having a different stencil front - back reference generates an error
glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 255, 1); glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 255, 1);
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION); EXPECT_GL_ERROR(errIfMismatch);
// Setting both references separately to the same value is valid. // Setting both references separately to the same value is valid.
glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 255, 1); glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 255, 1);
...@@ -1241,6 +1245,16 @@ TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef) ...@@ -1241,6 +1245,16 @@ TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef)
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
TEST_P(WebGLCompatibilityTest, StencilTestEnabledDisallowsDifferentStencilMaskAndRef)
{
glEnable(GL_STENCIL_TEST);
TestDifferentStencilMaskAndRef(GL_INVALID_OPERATION);
}
TEST_P(WebGLCompatibilityTest, StencilTestDisabledAllowsDifferentStencilMaskAndRef)
{
glDisable(GL_STENCIL_TEST);
TestDifferentStencilMaskAndRef(GL_NO_ERROR);
}
// Test that GL_FIXED is forbidden // Test that GL_FIXED is forbidden
TEST_P(WebGLCompatibilityTest, ForbidsGLFixed) TEST_P(WebGLCompatibilityTest, ForbidsGLFixed)
......
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