Vulkan: Shader path for framebuffer-to-texture copy
Part 1 in a series of changes to perform image copies on the GPU.
Bug: angleproject:2958
Change-Id: I6264a880865c4738c0866f2dc71af63425fc4118
Reviewed-on: https://chromium-review.googlesource.com/c/1370724
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/UtilsVk.cpp b/src/libANGLE/renderer/vulkan/UtilsVk.cpp
index 3c616de..fbfa6bd 100644
--- a/src/libANGLE/renderer/vulkan/UtilsVk.cpp
+++ b/src/libANGLE/renderer/vulkan/UtilsVk.cpp
@@ -18,6 +18,7 @@
namespace BufferUtils_comp = vk::InternalShader::BufferUtils_comp;
namespace ConvertVertex_comp = vk::InternalShader::ConvertVertex_comp;
+namespace ImageCopy_frag = vk::InternalShader::ImageCopy_frag;
namespace
{
@@ -29,32 +30,32 @@
constexpr uint32_t kBufferCopySourceBinding = 1;
constexpr uint32_t kConvertVertexDestinationBinding = 0;
constexpr uint32_t kConvertVertexSourceBinding = 1;
+constexpr uint32_t kImageCopySourceBinding = 0;
uint32_t GetBufferUtilsFlags(size_t dispatchSize, const vk::Format &format)
{
uint32_t flags = dispatchSize % 64 == 0 ? BufferUtils_comp::kIsAligned : 0;
const angle::Format &bufferFormat = format.bufferFormat();
- flags |= bufferFormat.componentType == GL_INT
+ flags |= bufferFormat.isInt()
? BufferUtils_comp::kIsInt
- : bufferFormat.componentType == GL_UNSIGNED_INT ? BufferUtils_comp::kIsUint
- : BufferUtils_comp::kIsFloat;
+ : bufferFormat.isUint() ? BufferUtils_comp::kIsUint : BufferUtils_comp::kIsFloat;
return flags;
}
uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters ¶ms)
{
- bool srcIsInt = params.srcFormat->componentType == GL_INT;
- bool srcIsUint = params.srcFormat->componentType == GL_UNSIGNED_INT;
- bool srcIsSnorm = params.srcFormat->componentType == GL_SIGNED_NORMALIZED;
- bool srcIsUnorm = params.srcFormat->componentType == GL_UNSIGNED_NORMALIZED;
+ bool srcIsInt = params.srcFormat->isInt();
+ bool srcIsUint = params.srcFormat->isUint();
+ bool srcIsSnorm = params.srcFormat->isSnorm();
+ bool srcIsUnorm = params.srcFormat->isUnorm();
bool srcIsFixed = params.srcFormat->isFixed;
- bool srcIsFloat = params.srcFormat->componentType == GL_FLOAT;
+ bool srcIsFloat = params.srcFormat->isFloat();
- bool destIsInt = params.destFormat->componentType == GL_INT;
- bool destIsUint = params.destFormat->componentType == GL_UNSIGNED_INT;
- bool destIsFloat = params.destFormat->componentType == GL_FLOAT;
+ bool destIsInt = params.destFormat->isInt();
+ bool destIsUint = params.destFormat->isUint();
+ bool destIsFloat = params.destFormat->isFloat();
// Assert on the types to make sure the shader supports its. These are based on
// ConvertVertex_comp::Conversion values.
@@ -115,6 +116,23 @@
return flags;
}
+
+uint32_t GetImageCopyFlags(const vk::Format &srcFormat, const vk::Format &destFormat)
+{
+ const angle::Format &srcAngleFormat = srcFormat.angleFormat();
+ const angle::Format &destAngleFormat = destFormat.angleFormat();
+
+ uint32_t flags = 0;
+
+ flags |= srcAngleFormat.isInt() ? ImageCopy_frag::kSrcIsInt
+ : srcAngleFormat.isUint() ? ImageCopy_frag::kSrcIsUint
+ : ImageCopy_frag::kSrcIsFloat;
+ flags |= destAngleFormat.isInt() ? ImageCopy_frag::kDestIsInt
+ : destAngleFormat.isUint() ? ImageCopy_frag::kDestIsUint
+ : ImageCopy_frag::kDestIsFloat;
+
+ return flags;
+}
} // namespace
UtilsVk::UtilsVk() = default;
@@ -142,6 +160,10 @@
program.destroy(device);
}
mImageClearProgram.destroy(device);
+ for (vk::ShaderProgramHelper &program : mImageCopyPrograms)
+ {
+ program.destroy(device);
+ }
}
angle::Result UtilsVk::ensureResourcesInitialized(vk::Context *context,
@@ -244,6 +266,21 @@
sizeof(ImageClearShaderParams));
}
+angle::Result UtilsVk::ensureImageCopyResourcesInitialized(vk::Context *context)
+{
+ if (mPipelineLayouts[Function::ImageCopy].valid())
+ {
+ return angle::Result::Continue;
+ }
+
+ VkDescriptorPoolSize setSizes[1] = {
+ {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1},
+ };
+
+ return ensureResourcesInitialized(context, Function::ImageCopy, setSizes, ArraySize(setSizes),
+ sizeof(ImageCopyShaderParams));
+}
+
angle::Result UtilsVk::setupProgram(vk::Context *context,
Function function,
vk::RefCounted<vk::ShaderAndSerial> *fsCsShader,
@@ -504,6 +541,44 @@
return angle::Result::Continue;
}
+angle::Result UtilsVk::startRenderPass(vk::Context *context,
+ vk::ImageHelper *image,
+ vk::ImageView *imageView,
+ const vk::RenderPassDesc &renderPassDesc,
+ const gl::Rectangle &renderArea,
+ vk::CommandBuffer **commandBufferOut)
+{
+ RendererVk *renderer = context->getRenderer();
+
+ vk::RenderPass *renderPass = nullptr;
+ ANGLE_TRY(renderer->getCompatibleRenderPass(context, renderPassDesc, &renderPass));
+
+ VkFramebufferCreateInfo framebufferInfo = {};
+
+ framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ framebufferInfo.flags = 0;
+ framebufferInfo.renderPass = renderPass->getHandle();
+ framebufferInfo.attachmentCount = 1;
+ framebufferInfo.pAttachments = imageView->ptr();
+ framebufferInfo.width = renderArea.x + renderArea.width;
+ framebufferInfo.height = renderArea.y + renderArea.height;
+ framebufferInfo.layers = 1;
+
+ vk::Framebuffer framebuffer;
+ ANGLE_VK_TRY(context, framebuffer.init(context->getDevice(), framebufferInfo));
+
+ // TODO(jmadill): Proper clear value implementation. http://anglebug.com/2361
+ std::vector<VkClearValue> clearValues = {{}};
+ ASSERT(clearValues.size() == 1);
+
+ ANGLE_TRY(image->beginRenderPass(context, framebuffer, renderArea, renderPassDesc, clearValues,
+ commandBufferOut));
+
+ renderer->releaseObject(renderer->getCurrentQueueSerial(), &framebuffer);
+
+ return angle::Result::Continue;
+}
+
angle::Result UtilsVk::clearImage(ContextVk *contextVk,
FramebufferVk *framebuffer,
const ClearImageParameters ¶ms)
@@ -552,4 +627,120 @@
return angle::Result::Continue;
}
+angle::Result UtilsVk::copyImage(vk::Context *context,
+ vk::ImageHelper *dest,
+ vk::ImageView *destView,
+ vk::ImageHelper *src,
+ vk::ImageView *srcView,
+ const CopyImageParameters ¶ms)
+{
+ RendererVk *renderer = context->getRenderer();
+
+ ANGLE_TRY(ensureImageCopyResourcesInitialized(context));
+
+ const vk::Format &srcFormat = src->getFormat();
+ const vk::Format &destFormat = dest->getFormat();
+
+ ImageCopyShaderParams shaderParams;
+ shaderParams.flipY = params.flipY;
+ shaderParams.destHasLuminance = destFormat.angleFormat().luminanceBits > 0;
+ shaderParams.destIsAlpha =
+ destFormat.angleFormat().isLUMA() && destFormat.angleFormat().alphaBits > 0;
+ shaderParams.srcMip = params.srcMip;
+ shaderParams.srcOffset[0] = params.srcOffset[0];
+ shaderParams.srcOffset[1] = params.srcOffset[1];
+ shaderParams.destOffset[0] = params.destOffset[0];
+ shaderParams.destOffset[1] = params.destOffset[1];
+
+ if (params.flipY)
+ {
+ // If viewport is flipped, the shader expects srcOffset[1] to have the
+ // last row's index instead of the first's.
+ shaderParams.srcOffset[1] = params.srcHeight - shaderParams.srcOffset[1] - 1;
+ }
+
+ uint32_t flags = GetImageCopyFlags(srcFormat, destFormat);
+
+ VkDescriptorSet descriptorSet;
+ vk::SharedDescriptorPoolBinding descriptorPoolBinding;
+ ANGLE_TRY(mDescriptorPools[Function::ImageCopy].allocateSets(
+ context, mDescriptorSetLayouts[Function::ImageCopy][kSetIndex].get().ptr(), 1,
+ &descriptorPoolBinding, &descriptorSet));
+ descriptorPoolBinding.get().updateSerial(context->getRenderer()->getCurrentQueueSerial());
+
+ vk::RenderPassDesc renderPassDesc;
+ renderPassDesc.setSamples(dest->getSamples());
+ renderPassDesc.packAttachment(destFormat);
+
+ vk::GraphicsPipelineDesc pipelineDesc;
+ pipelineDesc.initDefaults();
+ pipelineDesc.updateRenderPassDesc(renderPassDesc);
+
+ gl::Rectangle renderArea;
+ renderArea.x = params.destOffset[0];
+ renderArea.y = params.destOffset[1];
+ renderArea.width = params.srcExtents[0];
+ renderArea.height = params.srcExtents[1];
+
+ // Change source layout outside render pass
+ vk::CommandBuffer *srcLayoutChange;
+ ANGLE_TRY(src->recordCommands(context, &srcLayoutChange));
+
+ src->changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, srcLayoutChange);
+
+ // Change destination layout outside render pass as well
+ vk::CommandBuffer *destLayoutChange;
+ ANGLE_TRY(dest->recordCommands(context, &destLayoutChange));
+
+ dest->changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, destLayoutChange);
+
+ vk::CommandBuffer *commandBuffer;
+ ANGLE_TRY(startRenderPass(context, dest, destView, renderPassDesc, renderArea, &commandBuffer));
+
+ // Source's layout change should happen before rendering
+ src->addReadDependency(dest);
+
+ VkDescriptorImageInfo imageInfo = {};
+ imageInfo.imageView = srcView->getHandle();
+ imageInfo.imageLayout = src->getCurrentLayout();
+
+ VkWriteDescriptorSet writeInfo = {};
+ writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeInfo.dstSet = descriptorSet;
+ writeInfo.dstBinding = kImageCopySourceBinding;
+ writeInfo.descriptorCount = 1;
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+ writeInfo.pImageInfo = &imageInfo;
+
+ vkUpdateDescriptorSets(context->getDevice(), 1, &writeInfo, 0, nullptr);
+
+ vk::ShaderLibrary &shaderLibrary = renderer->getShaderLibrary();
+ vk::RefCounted<vk::ShaderAndSerial> *vertexShader = nullptr;
+ vk::RefCounted<vk::ShaderAndSerial> *fragmentShader = nullptr;
+ ANGLE_TRY(shaderLibrary.getFullScreenQuad_vert(context, 0, &vertexShader));
+ ANGLE_TRY(shaderLibrary.getImageCopy_frag(context, flags, &fragmentShader));
+
+ ANGLE_TRY(setupProgram(context, Function::ImageCopy, fragmentShader, vertexShader,
+ &mImageCopyPrograms[flags], &pipelineDesc, descriptorSet, &shaderParams,
+ sizeof(shaderParams), commandBuffer));
+
+ VkViewport viewport;
+ gl_vk::GetViewport(renderArea, 0.0f, 1.0f, false, dest->getExtents().height, &viewport);
+
+ VkRect2D scissor = gl_vk::GetRect(renderArea);
+
+ commandBuffer->setViewport(0, 1, &viewport);
+ commandBuffer->setScissor(0, 1, &scissor);
+ commandBuffer->draw(6, 1, 0, 0);
+
+ descriptorPoolBinding.reset();
+
+ return angle::Result::Continue;
+}
+
} // namespace rx