Vulkan: Mask the alpha channel for draw when needed

When the angleFormat we have has no alpha channel, but the actual
texture underneath has one, we shouldn't be drawing over the alpha
channel, so we apply a mask on it when we're in this situation.

Bug: angleproject:2597
Change-Id: Ia7110709e6ee32bb61988d08f5049e4e80e7e24e
Reviewed-on: https://chromium-review.googlesource.com/1106759
Commit-Queue: Luc Ferron <lucferron@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index 2d79599..0b828f7 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -362,6 +362,17 @@
     UNIMPLEMENTED();
 }
 
+void ContextVk::updateClearColorMask(const gl::BlendState &blendState)
+{
+    mClearColorMask =
+        gl_vk::GetColorComponentFlags(blendState.colorMaskRed, blendState.colorMaskGreen,
+                                      blendState.colorMaskBlue, blendState.colorMaskAlpha);
+
+    FramebufferVk *framebufferVk = vk::GetImpl(mState.getState().getDrawFramebuffer());
+    mPipelineDesc->updateColorWriteMask(mClearColorMask,
+                                        framebufferVk->getEmulatedAlphaAttachmentMask());
+}
+
 void ContextVk::updateScissor(const gl::State &glState)
 {
     if (glState.isScissorTestEnabled())
@@ -417,14 +428,8 @@
                 mPipelineDesc->updateBlendEquations(glState.getBlendState());
                 break;
             case gl::State::DIRTY_BIT_COLOR_MASK:
-            {
-                const gl::BlendState &blendState = glState.getBlendState();
-                mClearColorMask                  = gl_vk::GetColorComponentFlags(
-                    blendState.colorMaskRed, blendState.colorMaskGreen, blendState.colorMaskBlue,
-                    blendState.colorMaskAlpha);
-                mPipelineDesc->updateColorWriteMask(mClearColorMask);
+                updateClearColorMask(glState.getBlendState());
                 break;
-            }
             case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
                 WARN() << "DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED unimplemented";
                 break;
@@ -534,7 +539,7 @@
                 WARN() << "DIRTY_BIT_READ_FRAMEBUFFER_BINDING unimplemented";
                 break;
             case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
-                WARN() << "DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING unimplemented";
+                updateClearColorMask(glState.getBlendState());
                 break;
             case gl::State::DIRTY_BIT_RENDERBUFFER_BINDING:
                 WARN() << "DIRTY_BIT_RENDERBUFFER_BINDING unimplemented";
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.h b/src/libANGLE/renderer/vulkan/ContextVk.h
index 854e32b..a73f4c6 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.h
+++ b/src/libANGLE/renderer/vulkan/ContextVk.h
@@ -171,6 +171,7 @@
                         vk::CommandBuffer **commandBufferOut,
                         bool *shouldApplyVertexArrayOut);
 
+    void updateClearColorMask(const gl::BlendState &blendState);
     void updateScissor(const gl::State &glState);
 
     RendererVk *mRenderer;
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index 59e6662..c22b2f9 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -203,11 +203,21 @@
     const VkClearColorValue &clearColorValue = contextVk->getClearColorValue().color;
     for (size_t colorIndex : mState.getEnabledDrawBuffers())
     {
+        VkClearColorValue modifiedClearColorValue = clearColorValue;
         RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndex];
+
+        // Its possible we're clearing a render target that has no alpha channel but we represent it
+        // with a texture that has one. We must not affect its alpha channel no matter what the
+        // clear value is in that case.
+        if (mEmulatedAlphaAttachmentMask[colorIndex])
+        {
+            modifiedClearColorValue.float32[3] = 1.0;
+        }
+
         ASSERT(colorRenderTarget);
         vk::ImageHelper *image = colorRenderTarget->getImageForWrite(currentSerial, this);
         GLint mipLevelToClear  = (attachment->type() == GL_TEXTURE) ? attachment->mipLevel() : 0;
-        image->clearColor(clearColorValue, mipLevelToClear, 1, commandBuffer);
+        image->clearColor(modifiedClearColorValue, mipLevelToClear, 1, commandBuffer);
     }
 
     return gl::NoError();
@@ -369,9 +379,19 @@
                 RenderTargetVk *renderTarget = mRenderTargetCache.getColors()[colorIndex];
                 if (renderTarget)
                 {
-                    const angle::Format &format = renderTarget->getImageFormat().textureFormat();
-                    updateActiveColorMasks(colorIndex, format.redBits > 0, format.greenBits > 0,
-                                           format.blueBits > 0, format.alphaBits > 0);
+                    const angle::Format &emulatedFormat =
+                        renderTarget->getImageFormat().textureFormat();
+                    updateActiveColorMasks(
+                        colorIndex, emulatedFormat.redBits > 0, emulatedFormat.greenBits > 0,
+                        emulatedFormat.blueBits > 0, emulatedFormat.alphaBits > 0);
+
+                    // TODO(lucferron): Add a test to trigger edge case where the framebuffer
+                    // attachment would change but not the binding.
+                    // http://anglebug.com/2597
+                    const angle::Format &sourceFormat =
+                        renderTarget->getImageFormat().angleFormat();
+                    mEmulatedAlphaAttachmentMask.set(
+                        colorIndex, sourceFormat.alphaBits == 0 && emulatedFormat.alphaBits > 0);
                 }
                 else
                 {
@@ -383,8 +403,8 @@
     }
 
     mActiveColorComponents = gl_vk::GetColorComponentFlags(
-        mActiveColorComponentMasks[0].any(), mActiveColorComponentMasks[1].any(),
-        mActiveColorComponentMasks[2].any(), mActiveColorComponentMasks[3].any());
+        mActiveColorComponentMasksForClear[0].any(), mActiveColorComponentMasksForClear[1].any(),
+        mActiveColorComponentMasksForClear[2].any(), mActiveColorComponentMasksForClear[3].any());
 
     mRenderPassDesc.reset();
     renderer->releaseObject(getStoredQueueSerial(), &mFramebuffer);
@@ -626,7 +646,7 @@
     // This pipeline desc could be cached.
     vk::PipelineDesc pipelineDesc;
     pipelineDesc.initDefaults();
-    pipelineDesc.updateColorWriteMask(colorMaskFlags);
+    pipelineDesc.updateColorWriteMask(colorMaskFlags, getEmulatedAlphaAttachmentMask());
     pipelineDesc.updateRenderPassDesc(getRenderPassDesc());
     pipelineDesc.updateShaders(fullScreenQuad->queueSerial(), pushConstantColor->queueSerial());
     pipelineDesc.updateViewport(renderArea, 0.0f, 1.0f);
@@ -740,10 +760,15 @@
 
 void FramebufferVk::updateActiveColorMasks(size_t colorIndex, bool r, bool g, bool b, bool a)
 {
-    mActiveColorComponentMasks[0].set(colorIndex, r);
-    mActiveColorComponentMasks[1].set(colorIndex, g);
-    mActiveColorComponentMasks[2].set(colorIndex, b);
-    mActiveColorComponentMasks[3].set(colorIndex, a);
+    mActiveColorComponentMasksForClear[0].set(colorIndex, r);
+    mActiveColorComponentMasksForClear[1].set(colorIndex, g);
+    mActiveColorComponentMasksForClear[2].set(colorIndex, b);
+    mActiveColorComponentMasksForClear[3].set(colorIndex, a);
+}
+
+gl::DrawBufferMask FramebufferVk::getEmulatedAlphaAttachmentMask()
+{
+    return mEmulatedAlphaAttachmentMask;
 }
 
 gl::Error FramebufferVk::readPixelsImpl(const gl::Context *context,
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.h b/src/libANGLE/renderer/vulkan/FramebufferVk.h
index 2a312d9..52c0e06 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.h
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.h
@@ -100,6 +100,8 @@
 
     const gl::Extents &getReadImageExtents() const;
 
+    gl::DrawBufferMask getEmulatedAlphaAttachmentMask();
+
   private:
     FramebufferVk(const gl::FramebufferState &state);
     FramebufferVk(const gl::FramebufferState &state, WindowSurfaceVk *backbuffer);
@@ -124,9 +126,13 @@
     // channel is masked out, we check against the Framebuffer Attachments (RenderTargets) to see
     // if the masked out channel is present in any of the attachments.
     VkColorComponentFlags mActiveColorComponents;
-    gl::DrawBufferMask mActiveColorComponentMasks[4];
-
+    gl::DrawBufferMask mActiveColorComponentMasksForClear[4];
     vk::DynamicBuffer mReadPixelsBuffer;
+
+    // When we draw to the framebuffer, and the real format has an alpha channel but the format of
+    // the framebuffer does not, we need to mask out the alpha channel. This DrawBufferMask will
+    // contain the mask to apply to the alpha channel when drawing.
+    gl::DrawBufferMask mEmulatedAlphaAttachmentMask;
 };
 }  // namespace rx
 
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
index 1a49f00..b202c37 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
@@ -723,13 +723,15 @@
     }
 }
 
-void PipelineDesc::updateColorWriteMask(VkColorComponentFlags colorComponentFlags)
+void PipelineDesc::updateColorWriteMask(VkColorComponentFlags colorComponentFlags,
+                                        const gl::DrawBufferMask &alphaMask)
 {
     uint8_t colorMask = static_cast<uint8_t>(colorComponentFlags);
 
-    for (PackedColorBlendAttachmentState &blendAttachmentState : mColorBlendStateInfo.attachments)
+    for (size_t colorIndex = 0; colorIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorIndex++)
     {
-        blendAttachmentState.colorWriteMask = colorMask;
+        mColorBlendStateInfo.attachments[colorIndex].colorWriteMask =
+            alphaMask[colorIndex] ? (colorMask & ~VK_COLOR_COMPONENT_A_BIT) : colorMask;
     }
 }
 
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.h b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
index a5c2aac..6769e88 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
@@ -382,7 +382,8 @@
     void updateBlendColor(const gl::ColorF &color);
     void updateBlendFuncs(const gl::BlendState &blendState);
     void updateBlendEquations(const gl::BlendState &blendState);
-    void updateColorWriteMask(VkColorComponentFlags colorComponentFlags);
+    void updateColorWriteMask(VkColorComponentFlags colorComponentFlags,
+                              const gl::DrawBufferMask &alphaMask);
 
     // Depth/stencil states.
     void updateDepthTestEnabled(const gl::DepthStencilState &depthStencilState);
diff --git a/src/tests/deqp_support/deqp_gles2_test_expectations.txt b/src/tests/deqp_support/deqp_gles2_test_expectations.txt
index 935d7eb..9a36c46 100644
--- a/src/tests/deqp_support/deqp_gles2_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles2_test_expectations.txt
@@ -231,7 +231,6 @@
 2592 VULKAN : dEQP-GLES2.functional.shaders.builtin_variable.depth_range* = SKIP
 2595 VULKAN : dEQP-GLES2.functional.shaders.random.all_features.fragment* = SKIP
 2597 VULKAN : dEQP-GLES2.functional.fbo.render.recreate_stencilbuffer.no_rebind_rbo_rgb* = SKIP
-2597 VULKAN : dEQP-GLES2.functional.fbo.render.stencil_clear.tex2d_rgb_stencil_index8 = SKIP
 2161 VULKAN : dEQP-GLES2.functional.vertex_arrays.* = SKIP
 2598 VULKAN : dEQP-GLES2.functional.rasterization.primitives.line* = SKIP
 2599 VULKAN : dEQP-GLES2.functional.rasterization.limits.points = SKIP