Vulkan: Implement masked color clears.
This implements masked color clear using clear shaders. The shaders
themselves were introduced in a prior patch. In order to get the
right setup for the draw call to trigger the shaders, we create
an internal pipeline from the pipeline cache. We also use a special
pipeline layout with only uniform buffers. The masked out color
channels are disabled via settings on the pipeline.
This fixes the dEQP masked color clear tests. It doesn't handle
masked color clears combined with the depth clear bit. It's likely
we don't have test coverage for this case.
Bug: angleproject:2455
Change-Id: I513248cc0f7e58f490fc16ac9afb40119d730ccc
Reviewed-on: https://chromium-review.googlesource.com/1031373
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index e999850..f5557e6 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -54,12 +54,22 @@
}
FramebufferVk::FramebufferVk(const gl::FramebufferState &state)
- : FramebufferImpl(state), mBackbuffer(nullptr), mRenderPassDesc(), mFramebuffer()
+ : FramebufferImpl(state),
+ mBackbuffer(nullptr),
+ mRenderPassDesc(),
+ mFramebuffer(),
+ mActiveColorComponents(0),
+ mMaskedClearDescriptorSet(VK_NULL_HANDLE)
{
}
FramebufferVk::FramebufferVk(const gl::FramebufferState &state, WindowSurfaceVk *backbuffer)
- : FramebufferImpl(state), mBackbuffer(backbuffer), mRenderPassDesc(), mFramebuffer()
+ : FramebufferImpl(state),
+ mBackbuffer(backbuffer),
+ mRenderPassDesc(),
+ mFramebuffer(),
+ mActiveColorComponents(0),
+ mMaskedClearDescriptorSet(VK_NULL_HANDLE)
{
}
@@ -70,8 +80,9 @@
void FramebufferVk::destroy(const gl::Context *context)
{
RendererVk *renderer = vk::GetImpl(context)->getRenderer();
-
renderer->releaseResource(*this, &mFramebuffer);
+ renderer->releaseResource(*this, &mMaskedClearUniformBuffer.buffer);
+ renderer->releaseResource(*this, &mMaskedClearUniformBuffer.memory);
}
void FramebufferVk::destroyDefault(const egl::Display *display)
@@ -129,6 +140,15 @@
const gl::FramebufferAttachment *depthStencilAttachment = mState.getDepthStencilAttachment();
const gl::State &glState = context->getGLState();
+ // The most costly clear mode is when we need to mask out specific color channels. This can
+ // only be done with a draw call. The scissor region however can easily be integrated with
+ // this method. Similarly for depth/stencil clear.
+ VkColorComponentFlags colorMaskFlags = contextVk->getClearColorMask();
+ if (clearColor && (mActiveColorComponents & colorMaskFlags) != mActiveColorComponents)
+ {
+ return clearWithDraw(contextVk, colorMaskFlags);
+ }
+
// If we clear the depth OR the stencil but not both, and we have a packed depth stencil
// attachment, we need to use clearAttachment instead of clearDepthStencil since Vulkan won't
// allow us to clear one or the other separately.
@@ -179,16 +199,15 @@
writingNode = getCurrentWritingNode();
}
- // TODO(jmadill): Check for masked color clear. http://anglebug.com/2455
-
// TODO(jmadill): Support gaps in RenderTargets. http://anglebug.com/2394
const auto &colorRenderTargets = mRenderTargetCache.getColors();
+ const VkClearColorValue &clearColorValue = contextVk->getClearColorValue().color;
for (size_t colorIndex : mState.getEnabledDrawBuffers())
{
RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndex];
ASSERT(colorRenderTarget);
colorRenderTarget->resource->onWriteResource(writingNode, currentSerial);
- colorRenderTarget->image->clearColor(contextVk->getClearColorValue().color, commandBuffer);
+ colorRenderTarget->image->clearColor(clearColorValue, commandBuffer);
}
return gl::NoError();
@@ -349,11 +368,28 @@
size_t colorIndex =
static_cast<size_t>(dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
ANGLE_TRY(mRenderTargetCache.updateColorRenderTarget(context, mState, colorIndex));
+
+ // Update cached masks for masked clears.
+ RenderTargetVk *renderTarget = mRenderTargetCache.getColors()[colorIndex];
+ if (renderTarget)
+ {
+ const angle::Format &format = renderTarget->image->getFormat().textureFormat();
+ updateActiveColorMasks(colorIndex, format.redBits > 0, format.greenBits > 0,
+ format.blueBits > 0, format.alphaBits > 0);
+ }
+ else
+ {
+ updateActiveColorMasks(colorIndex, 0, 0, 0, 0);
+ }
break;
}
}
}
+ mActiveColorComponents = gl_vk::GetColorComponentFlags(
+ mActiveColorComponentMasks[0].any(), mActiveColorComponentMasks[1].any(),
+ mActiveColorComponentMasks[2].any(), mActiveColorComponentMasks[3].any());
+
mRenderPassDesc.reset();
renderer->releaseResource(*this, &mFramebuffer);
@@ -466,6 +502,8 @@
{
RendererVk *renderer = contextVk->getRenderer();
+ getNewWritingNode(renderer);
+
// This command can only happen inside a render pass, so obtain one if its already happening
// or create a new one if not.
vk::CommandGraphNode *node = nullptr;
@@ -491,13 +529,12 @@
// When clearing, the scissor region must be clipped to the renderArea per the validation rules
// in Vulkan.
gl::Rectangle intersection;
- if (!ClipRectangle(contextVk->getGLState().getScissor(), node->getRenderPassRenderArea(),
- &intersection))
+ if (!gl::ClipRectangle(contextVk->getGLState().getScissor(), node->getRenderPassRenderArea(),
+ &intersection))
{
// There is nothing to clear since the scissor is outside of the render area.
return gl::NoError();
}
-
clearRect.rect = gl_vk::GetRect(intersection);
gl::AttachmentArray<VkClearAttachment> clearAttachments;
@@ -552,6 +589,132 @@
return gl::NoError();
}
+gl::Error FramebufferVk::clearWithDraw(ContextVk *contextVk, VkColorComponentFlags colorMaskFlags)
+{
+ RendererVk *renderer = contextVk->getRenderer();
+ vk::ShaderLibrary *shaderLibrary = renderer->getShaderLibrary();
+
+ const vk::ShaderAndSerial *fullScreenQuad = nullptr;
+ ANGLE_TRY(shaderLibrary->getShader(renderer, vk::InternalShaderID::FullScreenQuad_vert,
+ &fullScreenQuad));
+
+ const vk::ShaderAndSerial *uniformColor = nullptr;
+ ANGLE_TRY(
+ shaderLibrary->getShader(renderer, vk::InternalShaderID::UniformColor_frag, &uniformColor));
+
+ const vk::PipelineLayout *pipelineLayout = nullptr;
+ ANGLE_TRY(renderer->getInternalUniformPipelineLayout(&pipelineLayout));
+
+ vk::CommandBuffer *commandBuffer = nullptr;
+ ANGLE_TRY(beginWriteResource(renderer, &commandBuffer));
+
+ vk::CommandGraphNode *node = nullptr;
+ ANGLE_TRY(getCommandGraphNodeForDraw(contextVk, &node));
+
+ // This pipeline desc could be cached.
+ vk::PipelineDesc pipelineDesc;
+ pipelineDesc.initDefaults();
+ pipelineDesc.updateColorWriteMask(colorMaskFlags);
+ pipelineDesc.updateRenderPassDesc(getRenderPassDesc());
+ pipelineDesc.updateShaders(fullScreenQuad->queueSerial(), uniformColor->queueSerial());
+ pipelineDesc.updateViewport(node->getRenderPassRenderArea(), 0.0f, 1.0f);
+
+ const gl::State &glState = contextVk->getGLState();
+ if (glState.isScissorTestEnabled())
+ {
+ gl::Rectangle intersection;
+ if (!gl::ClipRectangle(glState.getScissor(), node->getRenderPassRenderArea(),
+ &intersection))
+ {
+ return gl::NoError();
+ }
+
+ pipelineDesc.updateScissor(intersection);
+ }
+ else
+ {
+ pipelineDesc.updateScissor(node->getRenderPassRenderArea());
+ }
+
+ vk::PipelineAndSerial *pipeline = nullptr;
+ ANGLE_TRY(renderer->getInternalPipeline(*fullScreenQuad, *uniformColor, *pipelineLayout,
+ pipelineDesc, gl::AttributesMask(), &pipeline));
+ pipeline->updateSerial(renderer->getCurrentQueueSerial());
+
+ VkDevice device = renderer->getDevice();
+
+ if (!mMaskedClearUniformBuffer.buffer.valid())
+ {
+ ASSERT(mMaskedClearDescriptorSet == VK_NULL_HANDLE);
+
+ VkBufferUsageFlags bufferUsage =
+ (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+
+ VkBufferCreateInfo uniformBufferInfo;
+ uniformBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ uniformBufferInfo.pNext = nullptr;
+ uniformBufferInfo.flags = 0;
+ uniformBufferInfo.size = sizeof(VkClearColorValue);
+ uniformBufferInfo.usage = bufferUsage;
+ uniformBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ uniformBufferInfo.queueFamilyIndexCount = 0;
+ uniformBufferInfo.pQueueFamilyIndices = nullptr;
+
+ ANGLE_TRY(mMaskedClearUniformBuffer.buffer.init(device, uniformBufferInfo));
+
+ VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ size_t requiredSize = 0;
+ ANGLE_TRY(vk::AllocateBufferMemory(renderer, memoryFlags, &mMaskedClearUniformBuffer.buffer,
+ &mMaskedClearUniformBuffer.memory, &requiredSize));
+
+ const vk::DescriptorSetLayout &descriptorSetLayout =
+ renderer->getInternalUniformDescriptorSetLayout();
+
+ // This might confuse the dynamic descriptor pool's counting, but it shouldn't cause
+ // overflow.
+ vk::DynamicDescriptorPool *descriptorPool = contextVk->getDynamicDescriptorPool();
+ descriptorPool->allocateDescriptorSets(contextVk, descriptorSetLayout.ptr(), 1,
+ &mMaskedClearDescriptorSet);
+
+ VkDescriptorBufferInfo bufferInfo;
+ bufferInfo.buffer = mMaskedClearUniformBuffer.buffer.getHandle();
+ bufferInfo.offset = 0;
+ bufferInfo.range = VK_WHOLE_SIZE;
+
+ VkWriteDescriptorSet writeSet;
+ writeSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeSet.pNext = nullptr;
+ writeSet.dstSet = mMaskedClearDescriptorSet;
+ writeSet.dstBinding = 1;
+ writeSet.dstArrayElement = 0;
+ writeSet.descriptorCount = 1;
+ writeSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ writeSet.pImageInfo = nullptr;
+ writeSet.pBufferInfo = &bufferInfo;
+ writeSet.pTexelBufferView = nullptr;
+
+ vkUpdateDescriptorSets(device, 1, &writeSet, 0, nullptr);
+ }
+
+ VkClearColorValue clearColorValue = contextVk->getClearColorValue().color;
+ commandBuffer->updateBuffer(mMaskedClearUniformBuffer.buffer, 0, sizeof(VkClearColorValue),
+ clearColorValue.float32);
+
+ vk::CommandBuffer *drawCommandBuffer = nullptr;
+ ANGLE_TRY(node->beginInsideRenderPassRecording(renderer, &drawCommandBuffer));
+
+ std::array<uint32_t, 2> dynamicOffsets = {{0, 0}};
+ drawCommandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0, 1,
+ &mMaskedClearDescriptorSet, 2, dynamicOffsets.data());
+
+ // TODO(jmadill): Masked combined color and depth/stencil clear. http://anglebug.com/2455
+ // Any active queries submitted by the user should also be paused here.
+ drawCommandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->get());
+ drawCommandBuffer->draw(6, 1, 0, 0);
+
+ return gl::NoError();
+}
+
gl::Error FramebufferVk::getSamplePosition(size_t index, GLfloat *xy) const
{
UNIMPLEMENTED();
@@ -625,4 +788,11 @@
return gl::NoError();
}
+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);
+}
} // namespace rx