Vulkan: Implement command re-ordering.
This introduces a new CommandBufferNode class. Nodes are linked
together to form a graph based on their dependencies. When the app
triggers a readback or swap, the graph is flushed entirely. This
sends the queued ANGLE Vulkan work to the Vulkan queue which is
then processed on the GPU with the right dependencies.
This design allows us to save on some unnecessary RenderPass creation
and also allows us to know what load/store ops to use. It also allows
us to take advantage of the Vulkan automatic RenderPass transitions
for performance. Load/Store ops and automatic transitions will be
implemented in later patches.
Bug: angleproject:2264
Change-Id: I0e729c719e38254202c6fedcede4e63125eb4810
Reviewed-on: https://chromium-review.googlesource.com/780849
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index 64ca87f..636825b 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -18,6 +18,7 @@
#include "libANGLE/Display.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/renderer_utils.h"
+#include "libANGLE/renderer/vulkan/CommandBufferNode.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/RenderTargetVk.h"
@@ -56,12 +57,20 @@
}
FramebufferVk::FramebufferVk(const gl::FramebufferState &state)
- : FramebufferImpl(state), mBackbuffer(nullptr), mRenderPassDesc(), mFramebuffer()
+ : FramebufferImpl(state),
+ mBackbuffer(nullptr),
+ mRenderPassDesc(),
+ mFramebuffer(),
+ mRenderNodeDirty(false)
{
}
FramebufferVk::FramebufferVk(const gl::FramebufferState &state, WindowSurfaceVk *backbuffer)
- : FramebufferImpl(state), mBackbuffer(backbuffer), mRenderPassDesc(), mFramebuffer()
+ : FramebufferImpl(state),
+ mBackbuffer(backbuffer),
+ mRenderPassDesc(),
+ mFramebuffer(),
+ mRenderNodeDirty(false)
{
}
@@ -110,8 +119,6 @@
gl::Error FramebufferVk::clear(const gl::Context *context, GLbitfield mask)
{
- ContextVk *contextVk = vk::GetImpl(context);
-
if (mState.getDepthAttachment() && (mask & GL_DEPTH_BUFFER_BIT) != 0)
{
// TODO(jmadill): Depth clear
@@ -143,8 +150,10 @@
const auto &size = attachment->getSize();
const gl::Rectangle renderArea(0, 0, size.width, size.height);
- vk::CommandBufferAndState *commandBuffer = nullptr;
- ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
+ RendererVk *renderer = vk::GetImpl(context)->getRenderer();
+
+ vk::CommandBuffer *commandBuffer = nullptr;
+ ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer));
for (const auto &colorAttachment : mState.getColorAttachments())
{
@@ -249,11 +258,8 @@
renderTarget->extents, vk::StagingUsage::Read,
&stagingImage));
- vk::CommandBufferAndState *commandBuffer = nullptr;
- ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
-
- // End render pass if we're in one.
- renderer->endRenderPass();
+ vk::CommandBuffer *commandBuffer = nullptr;
+ ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer));
stagingImage.getImage().changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL,
commandBuffer);
@@ -283,7 +289,9 @@
commandBuffer->copyImage(*readImage, stagingImage.getImage(), 1, ®ion);
- ANGLE_TRY(renderer->submitAndFinishCommandBuffer(commandBuffer));
+ // Triggers a full finish.
+ // TODO(jmadill): Don't block on asynchronous readback.
+ ANGLE_TRY(renderer->finish(context));
// TODO(jmadill): parameters
uint8_t *mapPointer = nullptr;
@@ -338,7 +346,9 @@
// TODO(jmadill): Smarter update.
mRenderPassDesc.reset();
renderer->releaseResource(*this, &mFramebuffer);
- renderer->onReleaseRenderPass(this);
+
+ // Trigger a new set of secondary commands next time we render to this FBO,.
+ mRenderNodeDirty = true;
// TODO(jmadill): Use pipeline cache.
contextVk->invalidateCurrentPipeline();
@@ -411,7 +421,7 @@
if (colorAttachment.isAttached())
{
RenderTargetVk *renderTarget = nullptr;
- ANGLE_TRY(colorAttachment.getRenderTarget<RenderTargetVk>(context, &renderTarget));
+ ANGLE_TRY(colorAttachment.getRenderTarget(context, &renderTarget));
attachments.push_back(renderTarget->imageView->getHandle());
ASSERT(attachmentsSize.empty() || attachmentsSize == colorAttachment.getSize());
@@ -423,7 +433,7 @@
if (depthStencilAttachment && depthStencilAttachment->isAttached())
{
RenderTargetVk *renderTarget = nullptr;
- ANGLE_TRY(depthStencilAttachment->getRenderTarget<RenderTargetVk>(context, &renderTarget));
+ ANGLE_TRY(depthStencilAttachment->getRenderTarget(context, &renderTarget));
attachments.push_back(renderTarget->imageView->getHandle());
ASSERT(attachmentsSize.empty() || attachmentsSize == depthStencilAttachment->getSize());
@@ -455,52 +465,28 @@
return gl::InternalError() << "getSamplePosition is unimplemented.";
}
-gl::Error FramebufferVk::beginRenderPass(const gl::Context *context,
- RendererVk *rendererVk,
- vk::CommandBuffer *commandBuffer,
- Serial queueSerial)
+gl::Error FramebufferVk::getRenderNode(const gl::Context *context, vk::CommandBufferNode **nodeOut)
{
- uint32_t attachmentIndex = 0;
- vk::AttachmentOpsArray attachmentOps;
+ ContextVk *contextVk = vk::GetImpl(context);
+ RendererVk *renderer = contextVk->getRenderer();
+ Serial currentSerial = renderer->getCurrentQueueSerial();
- // TODO(jmadill): Cache render targets.
- for (const auto &colorAttachment : mState.getColorAttachments())
+ if (isCurrentlyRecording(currentSerial) && !mRenderNodeDirty)
{
- if (colorAttachment.isAttached())
- {
- RenderTargetVk *renderTarget = nullptr;
- ANGLE_TRY(colorAttachment.getRenderTarget<RenderTargetVk>(context, &renderTarget));
- renderTarget->resource->setQueueSerial(queueSerial);
-
- // Fill in some default load and store ops.
- attachmentOps.initDummyOp(attachmentIndex++, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
- }
+ *nodeOut = getCurrentWriteNode(currentSerial);
+ ASSERT((*nodeOut)->getInsideRenderPassCommands()->valid());
+ return gl::NoError();
}
- const auto *depthStencilAttachment = mState.getDepthStencilAttachment();
- if (depthStencilAttachment && depthStencilAttachment->isAttached())
- {
- RenderTargetVk *renderTarget = nullptr;
- ANGLE_TRY(depthStencilAttachment->getRenderTarget<RenderTargetVk>(context, &renderTarget));
- renderTarget->resource->setQueueSerial(queueSerial);
-
- // Fill in some default load and store ops.
- attachmentOps.initDummyOp(attachmentIndex++,
- VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
- }
+ vk::CommandBufferNode *node = getNewWriteNode(renderer);
vk::Framebuffer *framebuffer = nullptr;
- ANGLE_TRY_RESULT(getFramebuffer(context, rendererVk), framebuffer);
- ASSERT(framebuffer && framebuffer->valid());
+ ANGLE_TRY_RESULT(getFramebuffer(context, renderer), framebuffer);
- const vk::RenderPassDesc &desc = getRenderPassDesc(context);
-
- vk::RenderPass *renderPass = nullptr;
- ANGLE_TRY(rendererVk->getRenderPassWithOps(desc, attachmentOps, &renderPass));
- ASSERT(renderPass && renderPass->valid());
-
- // TODO(jmadill): Proper clear value implementation.
const gl::State &glState = context->getGLState();
+
+ // Hard-code RenderPass to clear the first render target to the current clear value.
+ // TODO(jmadill): Proper clear value implementation.
VkClearColorValue colorClear;
memset(&colorClear, 0, sizeof(VkClearColorValue));
colorClear.float32[0] = glState.getColorClearValue().red;
@@ -511,22 +497,39 @@
std::vector<VkClearValue> attachmentClearValues;
attachmentClearValues.push_back({colorClear});
- // Updated the cached image layout of the attachments in this FBO.
- // For a default FBO, we need to call through to the WindowSurfaceVk
- // TODO(jmadill): Iterate over all attachments.
- RenderTargetVk *renderTarget = nullptr;
- ANGLE_TRY(mState.getFirstColorAttachment()->getRenderTarget(context, &renderTarget));
- renderTarget->image->updateLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ node->storeRenderPassInfo(*framebuffer, glState.getViewport(), attachmentClearValues);
- commandBuffer->beginRenderPass(*renderPass, *framebuffer, glState.getViewport(), 1,
- attachmentClearValues.data());
-
- setQueueSerial(queueSerial);
- if (mBackbuffer)
+ // Initialize RenderPass info.
+ // TODO(jmadill): Could cache this info, would require dependent state change messaging.
+ const auto &colorAttachments = mState.getColorAttachments();
+ for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex)
{
- mBackbuffer->setQueueSerial(queueSerial);
+ const auto &colorAttachment = colorAttachments[attachmentIndex];
+ if (colorAttachment.isAttached())
+ {
+ RenderTargetVk *renderTarget = nullptr;
+ ANGLE_SWALLOW_ERR(colorAttachment.getRenderTarget(context, &renderTarget));
+
+ // TODO(jmadill): May need layout transition.
+ renderTarget->image->updateLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ node->appendColorRenderTarget(currentSerial, renderTarget);
+ }
}
+ const gl::FramebufferAttachment *depthStencilAttachment = mState.getDepthOrStencilAttachment();
+ if (depthStencilAttachment && depthStencilAttachment->isAttached())
+ {
+ RenderTargetVk *renderTarget = nullptr;
+ ANGLE_SWALLOW_ERR(depthStencilAttachment->getRenderTarget(context, &renderTarget));
+
+ // TODO(jmadill): May need layout transition.
+ renderTarget->image->updateLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+ node->appendDepthStencilRenderTarget(currentSerial, renderTarget);
+ }
+
+ mRenderNodeDirty = false;
+
+ *nodeOut = node;
return gl::NoError();
}