diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp index 7f53f84ef..a939d476f 100644 --- a/src/libANGLE/renderer/vulkan/ContextVk.cpp +++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp @@ -2624,10 +2624,10 @@ void ContextVk::updateColorMask(const gl::BlendState &blendState) mClearColorMask = gl_vk::GetColorComponentFlags(blendState.colorMaskRed, blendState.colorMaskGreen, blendState.colorMaskBlue, blendState.colorMaskAlpha); - FramebufferVk *framebufferVk = vk::GetImpl(mState.getDrawFramebuffer()); mGraphicsPipelineDesc->updateColorWriteMask(&mGraphicsPipelineTransition, mClearColorMask, - framebufferVk->getEmulatedAlphaAttachmentMask()); + framebufferVk->getEmulatedAlphaAttachmentMask(), + framebufferVk->getState().getEnabledDrawBuffers()); } void ContextVk::updateSampleMask(const gl::State &glState) diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp index 55f1d5226..5c51ea63c 100644 --- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp +++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp @@ -1477,9 +1477,10 @@ angle::Result FramebufferVk::invalidateImpl(ContextVk *contextVk, { // Set the appropriate storeOp for attachments. size_t attachmentIndexVk = 0; - for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) + for (size_t colorIndexGL : mAttachedColorBufferMask) { - if (invalidateColorBuffers.test(colorIndexGL)) + if (mState.getEnabledDrawBuffers()[colorIndexGL] && + invalidateColorBuffers.test(colorIndexGL)) { contextVk->getStartedRenderPassCommands().invalidateRenderPassColorAttachment( attachmentIndexVk); @@ -1576,16 +1577,19 @@ angle::Result FramebufferVk::updateColorAttachment(const gl::Context *context, updateActiveColorMasks(colorIndexGL, false, false, false, false); } - const bool enabledColor = renderTarget && mState.getEnabledDrawBuffers()[colorIndexGL]; + const bool enabledColor = + renderTarget && mState.getColorAttachments()[colorIndexGL].isAttached(); const bool enabledResolve = enabledColor && renderTarget->hasResolveAttachment(); if (enabledColor) { mCurrentFramebufferDesc.updateColor(colorIndexGL, renderTarget->getDrawSubresourceSerial()); + mAttachedColorBufferMask.set(colorIndexGL); } else { mCurrentFramebufferDesc.updateColor(colorIndexGL, vk::kInvalidImageViewSubresourceSerial); + mAttachedColorBufferMask.reset(colorIndexGL); } if (enabledResolve) @@ -1662,6 +1666,7 @@ angle::Result FramebufferVk::syncState(const gl::Context *context, // If we are notified that any attachment is dirty, but we have deferred clears for them, a // flushDeferredClears() call is missing somewhere. ASSERT this to catch these bugs. vk::ClearValuesArray previousDeferredClears = mDeferredClears; + bool shouldUpdateColorMask = false; // For any updated attachments we'll update their Serials below ASSERT(dirtyBits.any()); @@ -1681,22 +1686,7 @@ angle::Result FramebufferVk::syncState(const gl::Context *context, ANGLE_TRY(mRenderTargetCache.update(context, mState, dirtyBits)); break; case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS: - // Force update of serial for enabled draw buffers - mCurrentFramebufferDesc.reset(); - for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) - { - uint32_t colorIndex32 = static_cast(colorIndexGL); - - RenderTargetVk *renderTarget = mRenderTargetCache.getColors()[colorIndexGL]; - mCurrentFramebufferDesc.updateColor(colorIndex32, - renderTarget->getDrawSubresourceSerial()); - if (renderTarget->hasResolveAttachment()) - { - mCurrentFramebufferDesc.updateColorResolve( - colorIndex32, renderTarget->getResolveSubresourceSerial()); - } - } - updateDepthStencilAttachmentSerial(contextVk); + shouldUpdateColorMask = true; break; case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH: case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT: @@ -1724,13 +1714,18 @@ angle::Result FramebufferVk::syncState(const gl::Context *context, } ASSERT(!previousDeferredClears.test(colorIndexGL)); - ANGLE_TRY(updateColorAttachment(context, deferClears, colorIndexGL)); + shouldUpdateColorMask = true; break; } } } + if (shouldUpdateColorMask) + { + contextVk->updateColorMask(context->getState().getBlendState()); + } + // In some cases we'll need to force a flush of deferred clears. When we're syncing the read // framebuffer we might not get a RenderPass. Also when there are masked out cleared color // channels. @@ -1802,11 +1797,10 @@ void FramebufferVk::updateRenderPassDesc() mRenderPassDesc.setSamples(getSamples()); // Color attachments. - const auto &colorRenderTargets = mRenderTargetCache.getColors(); - const gl::DrawBufferMask enabledDrawBuffers = mState.getEnabledDrawBuffers(); - for (size_t colorIndexGL = 0; colorIndexGL < enabledDrawBuffers.size(); ++colorIndexGL) + const auto &colorRenderTargets = mRenderTargetCache.getColors(); + for (size_t colorIndexGL = 0; colorIndexGL < mAttachedColorBufferMask.size(); ++colorIndexGL) { - if (enabledDrawBuffers[colorIndexGL]) + if (mAttachedColorBufferMask[colorIndexGL]) { RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL]; ASSERT(colorRenderTarget); @@ -1880,7 +1874,7 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, // Color attachments. const auto &colorRenderTargets = mRenderTargetCache.getColors(); - for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) + for (size_t colorIndexGL : mAttachedColorBufferMask) { RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL]; ASSERT(colorRenderTarget); @@ -1920,7 +1914,7 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, else { // This Framebuffer owns all of the ImageViews, including its own resolve ImageViews. - for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) + for (size_t colorIndexGL : mAttachedColorBufferMask) { RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL]; ASSERT(colorRenderTarget); @@ -2124,18 +2118,16 @@ void FramebufferVk::clearWithLoadOp(ContextVk *contextVk, ASSERT(commands.getCommandBuffer().empty()); - // The clear colors are packed with no gaps. The draw buffers mask is unpacked - // and can have gaps. Thus we need to count the packed index explicitly in this loop. - size_t colorCount = 0; - for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) + uint32_t colorIndexVk = 0; + for (size_t colorIndexGL : mAttachedColorBufferMask) { - if (clearColorBuffers[colorIndexGL]) + if (mState.getEnabledDrawBuffers()[colorIndexGL] && clearColorBuffers[colorIndexGL]) { VkClearValue clearValue = getCorrectedColorClearValue(colorIndexGL, clearColorValue); - commands.updateRenderPassColorClear(colorCount, clearValue); + commands.updateRenderPassColorClear(colorIndexVk, clearValue); } - colorCount++; + ++colorIndexVk; } if (dsAspectFlags) @@ -2242,7 +2234,7 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, // Color attachments. const auto &colorRenderTargets = mRenderTargetCache.getColors(); uint32_t colorAttachmentCount = 0; - for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) + for (size_t colorIndexGL : mAttachedColorBufferMask) { RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL]; ASSERT(colorRenderTarget); @@ -2386,7 +2378,7 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, // Transition the images to the correct layout (through onColorDraw) after the // resolve-to-multisampled copies are done. - for (size_t colorIndexGL : mState.getEnabledDrawBuffers()) + for (size_t colorIndexGL : mAttachedColorBufferMask) { RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndexGL]; colorRenderTarget->onColorDraw(contextVk); diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.h b/src/libANGLE/renderer/vulkan/FramebufferVk.h index b4d115e84..c19c326fe 100644 --- a/src/libANGLE/renderer/vulkan/FramebufferVk.h +++ b/src/libANGLE/renderer/vulkan/FramebufferVk.h @@ -124,13 +124,6 @@ class FramebufferVk : public FramebufferImpl const vk::RenderPassDesc &getRenderPassDesc() const { return mRenderPassDesc; } - // We only support depth/stencil packed format and depthstencil attachment always follow all - // color attachments - size_t getDepthStencilAttachmentIndexVk() const - { - return getState().getEnabledDrawBuffers().count(); - } - angle::Result getFramebuffer(ContextVk *contextVk, vk::Framebuffer **framebufferOut, const vk::ImageView *resolveImageViewIn); @@ -257,6 +250,8 @@ class FramebufferVk : public FramebufferImpl vk::FramebufferDesc mCurrentFramebufferDesc; std::unordered_map mFramebufferCache; + // Tracks all the color buffers attached to this FramebufferDesc + gl::DrawBufferMask mAttachedColorBufferMask; vk::ClearValuesArray mDeferredClears; }; diff --git a/src/libANGLE/renderer/vulkan/UtilsVk.cpp b/src/libANGLE/renderer/vulkan/UtilsVk.cpp index 40ebeef32..c227e544d 100644 --- a/src/libANGLE/renderer/vulkan/UtilsVk.cpp +++ b/src/libANGLE/renderer/vulkan/UtilsVk.cpp @@ -1162,7 +1162,7 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk, vk::GraphicsPipelineDesc pipelineDesc; pipelineDesc.initDefaults(); pipelineDesc.setCullMode(VK_CULL_MODE_NONE); - pipelineDesc.setColorWriteMask(0, gl::DrawBufferMask()); + pipelineDesc.setColorWriteMask(0, gl::DrawBufferMask(), gl::DrawBufferMask()); pipelineDesc.setSingleColorWriteMask(params.colorAttachmentIndexGL, params.colorMaskFlags); pipelineDesc.setRasterizationSamples(framebuffer->getSamples()); pipelineDesc.setRenderPassDesc(framebuffer->getRenderPassDesc()); @@ -1374,11 +1374,12 @@ angle::Result UtilsVk::blitResolveImpl(ContextVk *contextVk, if (blitColor) { pipelineDesc.setColorWriteMask(kAllColorComponents, - framebuffer->getEmulatedAlphaAttachmentMask()); + framebuffer->getEmulatedAlphaAttachmentMask(), + ~gl::DrawBufferMask()); } else { - pipelineDesc.setColorWriteMask(0, gl::DrawBufferMask()); + pipelineDesc.setColorWriteMask(0, gl::DrawBufferMask(), gl::DrawBufferMask()); } pipelineDesc.setCullMode(VK_CULL_MODE_NONE); pipelineDesc.setRenderPassDesc(framebuffer->getRenderPassDesc()); diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp index 868584137..21ac5d7cc 100644 --- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp +++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp @@ -1246,7 +1246,8 @@ void GraphicsPipelineDesc::updateBlendFuncs(GraphicsPipelineTransitionBits *tran } void GraphicsPipelineDesc::setColorWriteMask(VkColorComponentFlags colorComponentFlags, - const gl::DrawBufferMask &alphaMask) + const gl::DrawBufferMask &alphaMask, + const gl::DrawBufferMask &enabledDrawBuffers) { PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend = mInputAssemblyAndColorBlendStateInfo; uint8_t colorMask = static_cast(colorComponentFlags); @@ -1254,8 +1255,11 @@ void GraphicsPipelineDesc::setColorWriteMask(VkColorComponentFlags colorComponen for (uint32_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorIndexGL++) { - uint8_t mask = - alphaMask[colorIndexGL] ? (colorMask & ~VK_COLOR_COMPONENT_A_BIT) : colorMask; + uint8_t mask = 0; + if (enabledDrawBuffers.test(colorIndexGL)) + { + mask = alphaMask[colorIndexGL] ? (colorMask & ~VK_COLOR_COMPONENT_A_BIT) : colorMask; + } Int4Array_Set(inputAndBlend.colorWriteMaskBits, colorIndexGL, mask); } } @@ -1270,9 +1274,10 @@ void GraphicsPipelineDesc::setSingleColorWriteMask(uint32_t colorIndexGL, void GraphicsPipelineDesc::updateColorWriteMask(GraphicsPipelineTransitionBits *transition, VkColorComponentFlags colorComponentFlags, - const gl::DrawBufferMask &alphaMask) + const gl::DrawBufferMask &alphaMask, + const gl::DrawBufferMask &enabledDrawBuffers) { - setColorWriteMask(colorComponentFlags, alphaMask); + setColorWriteMask(colorComponentFlags, alphaMask, enabledDrawBuffers); for (size_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorIndexGL++) diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.h b/src/libANGLE/renderer/vulkan/vk_cache_utils.h index ea237568c..2c469697f 100644 --- a/src/libANGLE/renderer/vulkan/vk_cache_utils.h +++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.h @@ -523,11 +523,13 @@ class GraphicsPipelineDesc final void updateBlendEquations(GraphicsPipelineTransitionBits *transition, const gl::BlendState &blendState); void setColorWriteMask(VkColorComponentFlags colorComponentFlags, - const gl::DrawBufferMask &alphaMask); + const gl::DrawBufferMask &alphaMask, + const gl::DrawBufferMask &enabledDrawBuffers); void setSingleColorWriteMask(uint32_t colorIndexGL, VkColorComponentFlags colorComponentFlags); void updateColorWriteMask(GraphicsPipelineTransitionBits *transition, VkColorComponentFlags colorComponentFlags, - const gl::DrawBufferMask &alphaMask); + const gl::DrawBufferMask &alphaMask, + const gl::DrawBufferMask &enabledDrawBuffers); // Depth/stencil states. void setDepthTestEnabled(bool enabled); diff --git a/src/tests/gl_tests/DrawBuffersTest.cpp b/src/tests/gl_tests/DrawBuffersTest.cpp index ce6bce9fb..1c5cb7f8a 100644 --- a/src/tests/gl_tests/DrawBuffersTest.cpp +++ b/src/tests/gl_tests/DrawBuffersTest.cpp @@ -526,6 +526,50 @@ TEST_P(DrawBuffersWebGL2Test, TwoProgramsWithDifferentOutputsAndClear) glDeleteProgram(program); } +// Test clear with gaps in draw buffers, originally show up as +// webgl_conformance_vulkan_passthrough_tests conformance/extensions/webgl-draw-buffers.html +// failure. This is added for ease of debugging. +TEST_P(DrawBuffersWebGL2Test, Clear) +{ + ANGLE_SKIP_TEST_IF(!setupTest()); + + constexpr GLint kMaxBuffers = 4; + + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &mMaxDrawBuffers); + ASSERT_GE(mMaxDrawBuffers, kMaxBuffers); + + GLenum drawBufs[kMaxBuffers] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}; + + // Enable all draw buffers. + for (GLuint texIndex = 0; texIndex < kMaxBuffers; texIndex++) + { + glBindTexture(GL_TEXTURE_2D, mTextures[texIndex]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + texIndex, GL_TEXTURE_2D, + mTextures[texIndex], 0); + } + + // Clear with all draw buffers. + setDrawBuffers(kMaxBuffers, drawBufs); + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Clear with first half none draw buffers. + drawBufs[0] = GL_NONE; + drawBufs[1] = GL_NONE; + setDrawBuffers(kMaxBuffers, drawBufs); + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + ASSERT_GL_NO_ERROR(); + + // Verify first is drawn red, second is untouched, and last two are cleared green. + verifyAttachment2DColor(0, mTextures[0], GL_TEXTURE_2D, 0, GLColor::red); + verifyAttachment2DColor(1, mTextures[1], GL_TEXTURE_2D, 0, GLColor::red); + verifyAttachment2DColor(2, mTextures[2], GL_TEXTURE_2D, 0, GLColor::green); + verifyAttachment2DColor(3, mTextures[3], GL_TEXTURE_2D, 0, GLColor::green); +} + TEST_P(DrawBuffersTest, UnwrittenOutputVariablesShouldNotCrash) { ANGLE_SKIP_TEST_IF(!setupTest()); @@ -736,6 +780,188 @@ TEST_P(DrawBuffersTestES3, 2DArrayTextures) glDeleteProgram(program); } +// Vulkan backend is setting per buffer color mask to false for draw buffers that set to GL_NONE. +// These set of tests are to test draw buffer change followed by draw/clear/blit and followed by +// draw buffer change are behaving correctly. +class ColorMaskForDrawBuffersTest : public DrawBuffersTest +{ + protected: + void setupColorMaskForDrawBuffersTest() + { + glBindTexture(GL_TEXTURE_2D, mTextures[0]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], + 0); + glBindTexture(GL_TEXTURE_2D, mTextures[1]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[1], + 0); + glBindTexture(GL_TEXTURE_2D, mTextures[2]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, mTextures[2], + 0); + + constexpr char kFS_ESSL3[] = + "#version 300 es\n" + "precision highp float;\n" + "uniform mediump vec4 u_color0;\n" + "uniform mediump vec4 u_color1;\n" + "uniform mediump vec4 u_color2;\n" + "layout(location = 0) out vec4 out_color0;\n" + "layout(location = 1) out vec4 out_color1;\n" + "layout(location = 2) out vec4 out_color2;\n" + "void main()\n" + "{\n" + " out_color0 = u_color0;\n" + " out_color1 = u_color1;\n" + " out_color2 = u_color2;\n" + "}\n"; + program = CompileProgram(essl3_shaders::vs::Simple(), kFS_ESSL3); + glUseProgram(program); + + positionLocation = glGetAttribLocation(program, positionAttrib()); + ASSERT_NE(-1, positionLocation); + color0UniformLocation = glGetUniformLocation(program, "u_color0"); + ASSERT_NE(color0UniformLocation, -1); + color1UniformLocation = glGetUniformLocation(program, "u_color1"); + ASSERT_NE(color1UniformLocation, -1); + color2UniformLocation = glGetUniformLocation(program, "u_color2"); + ASSERT_NE(color2UniformLocation, -1); + + glUniform4fv(color0UniformLocation, 1, GLColor::red.toNormalizedVector().data()); + glUniform4fv(color1UniformLocation, 1, GLColor::green.toNormalizedVector().data()); + glUniform4fv(color2UniformLocation, 1, GLColor::yellow.toNormalizedVector().data()); + + // First draw into both buffers so that buffer0 is red and buffer1 is green + resetDrawBuffers(); + drawQuad(program, positionAttrib(), 0.5); + EXPECT_GL_NO_ERROR(); + + for (int i = 0; i < 4; i++) + { + drawBuffers[i] = GL_NONE; + } + } + + void resetDrawBuffers() + { + drawBuffers[0] = GL_COLOR_ATTACHMENT0; + drawBuffers[1] = GL_COLOR_ATTACHMENT1; + drawBuffers[2] = GL_COLOR_ATTACHMENT2; + drawBuffers[3] = GL_NONE; + setDrawBuffers(4, drawBuffers); + } + + GLenum drawBuffers[4]; + GLuint program; + GLint positionLocation; + GLint color0UniformLocation; + GLint color1UniformLocation; + GLint color2UniformLocation; +}; + +// Test draw buffer state change followed draw call +TEST_P(ColorMaskForDrawBuffersTest, DrawQuad) +{ + ANGLE_SKIP_TEST_IF(!setupTest()); + setupColorMaskForDrawBuffersTest(); + + // Draw blue into attachment0. Buffer0 should be blue and buffer1 should remain green + drawBuffers[0] = GL_COLOR_ATTACHMENT0; + setDrawBuffers(4, drawBuffers); + glUniform4fv(color0UniformLocation, 1, GLColor::blue.toNormalizedVector().data()); + glUniform4fv(color1UniformLocation, 1, GLColor::cyan.toNormalizedVector().data()); + glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2); + drawQuad(program, positionAttrib(), 0.5); + + resetDrawBuffers(); + glUniform4fv(color0UniformLocation, 1, GLColor::magenta.toNormalizedVector().data()); + glUniform4fv(color1UniformLocation, 1, GLColor::white.toNormalizedVector().data()); + glViewport(getWindowWidth() / 2, 0, getWindowWidth() / 2, getWindowHeight() / 2); + drawQuad(program, positionAttrib(), 0.5); + EXPECT_GL_NO_ERROR(); + + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], + 0); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 3 / 4, getWindowHeight() / 4, GLColor::magenta); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() * 3 / 4, GLColor::red); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], + 0); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 3 / 4, getWindowHeight() / 4, GLColor::white); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() * 3 / 4, GLColor::green); + EXPECT_GL_NO_ERROR(); +} + +// Test draw buffer state change followed clear +TEST_P(ColorMaskForDrawBuffersTest, Clear) +{ + ANGLE_SKIP_TEST_IF(!setupTest()); + setupColorMaskForDrawBuffersTest(); + + // Clear attachment1. Buffer0 should retain red and buffer1 should be blue + drawBuffers[1] = GL_COLOR_ATTACHMENT1; + setDrawBuffers(4, drawBuffers); + GLfloat *clearColor = GLColor::blue.toNormalizedVector().data(); + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + glClear(GL_COLOR_BUFFER_BIT); + verifyAttachment2DColor(0, mTextures[0], GL_TEXTURE_2D, 0, GLColor::red); + verifyAttachment2DColor(1, mTextures[1], GL_TEXTURE_2D, 0, GLColor::blue); + EXPECT_GL_NO_ERROR(); +} + +// Test draw buffer state change followed scissored clear +TEST_P(ColorMaskForDrawBuffersTest, ScissoredClear) +{ + ANGLE_SKIP_TEST_IF(!setupTest()); + setupColorMaskForDrawBuffersTest(); + + // Clear attachment1. Buffer0 should retain red and buffer1 should be blue + drawBuffers[1] = GL_COLOR_ATTACHMENT1; + setDrawBuffers(4, drawBuffers); + GLfloat *clearColor = GLColor::blue.toNormalizedVector().data(); + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + glScissor(0, 0, getWindowWidth() / 2, getWindowHeight() / 2); + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT); + + resetDrawBuffers(); + clearColor = GLColor::magenta.toNormalizedVector().data(); + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + glScissor(getWindowWidth() / 2, 0, getWindowWidth() / 2, getWindowHeight() / 2); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_GL_NO_ERROR(); + + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], + 0); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 3 / 4, getWindowHeight() / 4, GLColor::magenta); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() * 3 / 4, GLColor::red); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], + 0); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 3 / 4, getWindowHeight() / 4, GLColor::magenta); + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() * 3 / 4, GLColor::green); + EXPECT_GL_NO_ERROR(); +} + +// Test draw buffer state change followed FBO blit +TEST_P(ColorMaskForDrawBuffersTest, Blit) +{ + ANGLE_SKIP_TEST_IF(!setupTest()); + setupColorMaskForDrawBuffersTest(); + + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[2], + 0); + + // BLIT mTexture[2] to attachment0. Buffer0 should remain red and buffer1 should be yellow + drawBuffers[0] = GL_COLOR_ATTACHMENT0; + setDrawBuffers(4, drawBuffers); + glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(), + getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + verifyAttachment2DColor(0, mTextures[0], GL_TEXTURE_2D, 0, GLColor::yellow); + verifyAttachment2DColor(1, mTextures[1], GL_TEXTURE_2D, 0, GLColor::green); + EXPECT_GL_NO_ERROR(); +} + // Use this to select which configurations (e.g. which renderer, which GLES major version) these // tests should be run against. ANGLE_INSTANTIATE_TEST(DrawBuffersTest, @@ -745,3 +971,5 @@ ANGLE_INSTANTIATE_TEST(DrawBuffersTest, ANGLE_INSTANTIATE_TEST_ES3(DrawBuffersWebGL2Test); ANGLE_INSTANTIATE_TEST_ES3(DrawBuffersTestES3); + +ANGLE_INSTANTIATE_TEST_ES3(ColorMaskForDrawBuffersTest); diff --git a/src/tests/gl_tests/VulkanPerformanceCounterTest.cpp b/src/tests/gl_tests/VulkanPerformanceCounterTest.cpp index b58ad347e..6056d3964 100644 --- a/src/tests/gl_tests/VulkanPerformanceCounterTest.cpp +++ b/src/tests/gl_tests/VulkanPerformanceCounterTest.cpp @@ -1183,6 +1183,65 @@ TEST_P(VulkanPerformanceCounterTest, MaskedClearDoesNotBreakRenderPass) EXPECT_PIXEL_NEAR(0, 0, 63, 127, 255, 191, 1); } +// Tests that draw buffer change with all color channel mask off should not break renderpass +TEST_P(VulkanPerformanceCounterTest, DrawbufferChangeWithAllColorMaskDisabled) +{ + const rx::vk::PerfCounters &counters = hackANGLE(); + + ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor()); + glUseProgram(program); + GLint colorUniformLocation = + glGetUniformLocation(program, angle::essl1_shaders::ColorUniform()); + ASSERT_NE(-1, colorUniformLocation); + ASSERT_GL_NO_ERROR(); + + GLTexture textureRGBA; + glBindTexture(GL_TEXTURE_2D, textureRGBA); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GLTexture textureDepth; + glBindTexture(GL_TEXTURE_2D, textureDepth); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 64, 64, 0, GL_DEPTH_COMPONENT, + GL_UNSIGNED_INT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + GLFramebuffer framebuffer; + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureRGBA, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureDepth, 0); + ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); + ASSERT_GL_NO_ERROR(); + + uint32_t expectedRenderPassCount = counters.renderPasses + 1; + + // Draw into FBO + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // clear to green + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, 256, 256); + glUniform4fv(colorUniformLocation, 1, GLColor::blue.toNormalizedVector().data()); + GLenum glDrawBuffers_bufs_1[] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, glDrawBuffers_bufs_1); + glEnable(GL_DEPTH_TEST); + drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); + // Change draw buffer state and color mask + GLenum glDrawBuffers_bufs_0[] = {GL_NONE}; + glDrawBuffers(1, glDrawBuffers_bufs_0); + glColorMask(false, false, false, false); + drawQuad(program, essl1_shaders::PositionAttrib(), 0.6f); + // Change back draw buffer state and color mask + glDrawBuffers(1, glDrawBuffers_bufs_1); + glColorMask(true, true, true, true); + glUniform4fv(colorUniformLocation, 1, GLColor::red.toNormalizedVector().data()); + drawQuad(program, essl1_shaders::PositionAttrib(), 0.7f); + + uint32_t actualRenderPassCount = counters.renderPasses; + EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); +} + ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest, ES3_VULKAN()); ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_ES31, ES31_VULKAN());