Commit ae2d0a0e by Geoff Lang

Fix incorrect dirty bit state tracking on D3D9 AMD with color masks.

When using the zero color mask workaround in StateManager9, the blend state and blend equation were being modified. This caused inconsistant state if the workaround stopped being used in subsequent draw calls. To deal with this issue, dirty the BLEND_ENABLED and BLEND_EQUATION states when the COLOR_MASK changes and dirty the COLOR_MASK state wheneither BLEND_ENABLED or BLEND_EQUATION changes. BUG=597107 Change-Id: I03de934b419b7593e4863838720ced1e5773c092 Reviewed-on: https://chromium-review.googlesource.com/339280Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Tryjob-Request: Geoff Lang <geofflang@chromium.org>
parent 5858f7e3
...@@ -367,6 +367,8 @@ void Renderer9::initializeDevice() ...@@ -367,6 +367,8 @@ void Renderer9::initializeDevice()
mIndexDataManager = new IndexDataManager(this, getRendererClass()); mIndexDataManager = new IndexDataManager(this, getRendererClass());
mTranslatedAttribCache.resize(getRendererCaps().maxVertexAttributes); mTranslatedAttribCache.resize(getRendererCaps().maxVertexAttributes);
mStateManager.initialize();
} }
D3DPRESENT_PARAMETERS Renderer9::getDefaultPresentParameters() D3DPRESENT_PARAMETERS Renderer9::getDefaultPresentParameters()
......
...@@ -18,7 +18,8 @@ namespace rx ...@@ -18,7 +18,8 @@ namespace rx
{ {
StateManager9::StateManager9(Renderer9 *renderer9) StateManager9::StateManager9(Renderer9 *renderer9)
: mCurBlendState(), : mUsingZeroColorMaskWorkaround(false),
mCurBlendState(),
mCurBlendColor(0, 0, 0, 0), mCurBlendColor(0, 0, 0, 0),
mCurSampleMask(0), mCurSampleMask(0),
mCurRasterState(), mCurRasterState(),
...@@ -67,6 +68,11 @@ StateManager9::~StateManager9() ...@@ -67,6 +68,11 @@ StateManager9::~StateManager9()
{ {
} }
void StateManager9::initialize()
{
mUsingZeroColorMaskWorkaround = mRenderer9->getVendorId() == VENDOR_ID_AMD;
}
void StateManager9::forceSetBlendState() void StateManager9::forceSetBlendState()
{ {
mDirtyBits |= mBlendStateDirtyBits; mDirtyBits |= mBlendStateDirtyBits;
...@@ -125,6 +131,12 @@ void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits ...@@ -125,6 +131,12 @@ void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits
// BlendColor and funcs and equations has to be set if blend is enabled // BlendColor and funcs and equations has to be set if blend is enabled
mDirtyBits.set(DIRTY_BIT_BLEND_COLOR); mDirtyBits.set(DIRTY_BIT_BLEND_COLOR);
mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS); mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS);
// The color mask may have to be updated if the blend state changes
if (mUsingZeroColorMaskWorkaround)
{
mDirtyBits.set(DIRTY_BIT_COLOR_MASK);
}
} }
break; break;
case gl::State::DIRTY_BIT_BLEND_FUNCS: case gl::State::DIRTY_BIT_BLEND_FUNCS:
...@@ -138,6 +150,12 @@ void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits ...@@ -138,6 +150,12 @@ void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits
mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS); mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS);
// BlendColor depends on the values of blend funcs // BlendColor depends on the values of blend funcs
mDirtyBits.set(DIRTY_BIT_BLEND_COLOR); mDirtyBits.set(DIRTY_BIT_BLEND_COLOR);
// The color mask may have to be updated if the blend funcs change
if (mUsingZeroColorMaskWorkaround)
{
mDirtyBits.set(DIRTY_BIT_COLOR_MASK);
}
} }
break; break;
} }
...@@ -148,6 +166,12 @@ void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits ...@@ -148,6 +166,12 @@ void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits
blendState.blendEquationAlpha != mCurBlendState.blendEquationAlpha) blendState.blendEquationAlpha != mCurBlendState.blendEquationAlpha)
{ {
mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS); mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS);
// The color mask may have to be updated if the blend funcs change
if (mUsingZeroColorMaskWorkaround)
{
mDirtyBits.set(DIRTY_BIT_COLOR_MASK);
}
} }
break; break;
} }
...@@ -167,6 +191,14 @@ void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits ...@@ -167,6 +191,14 @@ void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits
blendState.colorMaskAlpha != mCurBlendState.colorMaskAlpha) blendState.colorMaskAlpha != mCurBlendState.colorMaskAlpha)
{ {
mDirtyBits.set(DIRTY_BIT_COLOR_MASK); mDirtyBits.set(DIRTY_BIT_COLOR_MASK);
// The color mask can cause the blend state to get out of sync when using the
// zero color mask workaround
if (mUsingZeroColorMaskWorkaround)
{
mDirtyBits.set(DIRTY_BIT_BLEND_ENABLED);
mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS_EQUATIONS);
}
} }
break; break;
} }
...@@ -800,12 +832,6 @@ void StateManager9::setColorMask(const gl::Framebuffer *framebuffer, ...@@ -800,12 +832,6 @@ void StateManager9::setColorMask(const gl::Framebuffer *framebuffer,
bool alpha) bool alpha)
{ {
// Set the color mask // Set the color mask
bool zeroColorMaskAllowed = mRenderer9->getVendorId() != VENDOR_ID_AMD;
// Apparently some ATI cards have a bug where a draw with a zero color
// write mask can cause later draws to have incorrect results. Instead,
// set a nonzero color write mask but modify the blend state so that no
// drawing is done.
// http://code.google.com/p/angleproject/issues/detail?id=169
const gl::FramebufferAttachment *attachment = framebuffer->getFirstColorbuffer(); const gl::FramebufferAttachment *attachment = framebuffer->getFirstColorbuffer();
GLenum internalFormat = attachment ? attachment->getInternalFormat() : GL_NONE; GLenum internalFormat = attachment ? attachment->getInternalFormat() : GL_NONE;
...@@ -816,26 +842,44 @@ void StateManager9::setColorMask(const gl::Framebuffer *framebuffer, ...@@ -816,26 +842,44 @@ void StateManager9::setColorMask(const gl::Framebuffer *framebuffer,
formatInfo.redBits > 0 && red, formatInfo.greenBits > 0 && green, formatInfo.redBits > 0 && red, formatInfo.greenBits > 0 && green,
formatInfo.blueBits > 0 && blue, formatInfo.alphaBits > 0 && alpha); formatInfo.blueBits > 0 && blue, formatInfo.alphaBits > 0 && alpha);
if (colorMask == 0 && !zeroColorMaskAllowed) // Apparently some ATI cards have a bug where a draw with a zero color write mask can cause
// later draws to have incorrect results. Instead, set a nonzero color write mask but modify the
// blend state so that no drawing is done.
// http://anglebug.com/169
if (colorMask == 0 && mUsingZeroColorMaskWorkaround)
{ {
IDirect3DDevice9 *device = mRenderer9->getDevice(); IDirect3DDevice9 *device = mRenderer9->getDevice();
// Enable green channel, but set blending so nothing will be drawn. // Enable green channel, but set blending so nothing will be drawn.
device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_GREEN); device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_GREEN);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
mCurBlendState.colorMaskRed = false;
mCurBlendState.colorMaskGreen = true;
mCurBlendState.colorMaskBlue = false;
mCurBlendState.colorMaskAlpha = false;
mCurBlendState.blend = true;
mCurBlendState.sourceBlendRGB = GL_ZERO;
mCurBlendState.sourceBlendAlpha = GL_ZERO;
mCurBlendState.destBlendRGB = GL_ONE;
mCurBlendState.destBlendAlpha = GL_ONE;
mCurBlendState.blendEquationRGB = GL_FUNC_ADD;
mCurBlendState.blendEquationAlpha = GL_FUNC_ADD;
} }
else else
{ {
mRenderer9->getDevice()->SetRenderState(D3DRS_COLORWRITEENABLE, colorMask); mRenderer9->getDevice()->SetRenderState(D3DRS_COLORWRITEENABLE, colorMask);
}
mCurBlendState.colorMaskRed = red; mCurBlendState.colorMaskRed = red;
mCurBlendState.colorMaskGreen = green; mCurBlendState.colorMaskGreen = green;
mCurBlendState.colorMaskBlue = blue; mCurBlendState.colorMaskBlue = blue;
mCurBlendState.colorMaskAlpha = alpha; mCurBlendState.colorMaskAlpha = alpha;
}
} }
void StateManager9::setSampleMask(unsigned int sampleMask) void StateManager9::setSampleMask(unsigned int sampleMask)
......
...@@ -39,6 +39,8 @@ class StateManager9 final : angle::NonCopyable ...@@ -39,6 +39,8 @@ class StateManager9 final : angle::NonCopyable
StateManager9(Renderer9 *renderer9); StateManager9(Renderer9 *renderer9);
~StateManager9(); ~StateManager9();
void initialize();
void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits); void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits);
gl::Error setBlendDepthRasterStates(const gl::State &glState, unsigned int sampleMask); gl::Error setBlendDepthRasterStates(const gl::State &glState, unsigned int sampleMask);
...@@ -156,6 +158,8 @@ class StateManager9 final : angle::NonCopyable ...@@ -156,6 +158,8 @@ class StateManager9 final : angle::NonCopyable
typedef std::bitset<DIRTY_BIT_MAX> DirtyBits; typedef std::bitset<DIRTY_BIT_MAX> DirtyBits;
bool mUsingZeroColorMaskWorkaround;
// Currently applied blend state // Currently applied blend state
gl::BlendState mCurBlendState; gl::BlendState mCurBlendState;
gl::ColorF mCurBlendColor; gl::ColorF mCurBlendColor;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
'<(angle_path)/src/tests/gl_tests/BufferDataTest.cpp', '<(angle_path)/src/tests/gl_tests/BufferDataTest.cpp',
'<(angle_path)/src/tests/gl_tests/BuiltinVariableTest.cpp', '<(angle_path)/src/tests/gl_tests/BuiltinVariableTest.cpp',
'<(angle_path)/src/tests/gl_tests/ClearTest.cpp', '<(angle_path)/src/tests/gl_tests/ClearTest.cpp',
'<(angle_path)/src/tests/gl_tests/ColorMaskTest.cpp',
'<(angle_path)/src/tests/gl_tests/CompressedTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/CompressedTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/CopyTexImageTest.cpp', '<(angle_path)/src/tests/gl_tests/CopyTexImageTest.cpp',
'<(angle_path)/src/tests/gl_tests/CubeMapTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/CubeMapTextureTest.cpp',
......
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// ColorMaskTest.cpp: Test GLES functionality related to color masks, particularly an AMD D3D9
// driver bug.
#include "test_utils/ANGLETest.h"
namespace angle
{
class ColorMaskTest : public ANGLETest
{
protected:
ColorMaskTest() : mProgram(0)
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
}
void SetUp() override
{
ANGLETest::SetUp();
const std::string vsSource =
"precision highp float;\n"
"attribute vec4 position;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
"}\n";
const std::string fsSource =
"precision highp float;\n"
"uniform vec4 color;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor = color;\n"
"}\n";
mProgram = CompileProgram(vsSource, fsSource);
ASSERT_NE(0u, mProgram) << "shader compilation failed.";
mColorUniform = glGetUniformLocation(mProgram, "color");
}
void TearDown() override
{
glDeleteProgram(mProgram);
ANGLETest::TearDown();
}
GLuint mProgram = 0;
GLint mColorUniform = -1;
};
// Some ATI cards have a bug where a draw with a zero color write mask can cause later draws to have
// incorrect results. Test to make sure this bug is not exposed.
TEST_P(ColorMaskTest, AMDZeroColorMaskBug)
{
int x = getWindowWidth() / 2;
int y = getWindowHeight() / 2;
// Clear to blue
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(x, y, 0, 0, 255, 255);
// Draw a quad with all colors masked and blending disabled, should remain blue
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDisable(GL_BLEND);
glUseProgram(mProgram);
glUniform4f(mColorUniform, 1.0f, 0.0f, 0.0f, 0.0f);
EXPECT_GL_NO_ERROR();
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_EQ(x, y, 0, 0, 255, 255);
// Re-enable the color mask, should be red (with blend disabled, the red should overwrite
// everything)
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glUseProgram(mProgram);
glUniform4f(mColorUniform, 1.0f, 0.0f, 0.0f, 0.0f);
EXPECT_GL_NO_ERROR();
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_EQ(x, y, 255, 0, 0, 0);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. D3D11 Feature Level 9_3 uses different D3D formats for vertex
// attribs compared to Feature Levels 10_0+, so we should test them separately.
ANGLE_INSTANTIATE_TEST(ColorMaskTest,
ES2_D3D9(),
ES2_D3D11(),
ES2_D3D11_FL9_3(),
ES2_OPENGL(),
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES());
} // namespace angle
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