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/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index cae6e09..1b20c0d 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -14,6 +14,7 @@
#include "libANGLE/Context.h"
#include "libANGLE/Program.h"
#include "libANGLE/renderer/vulkan/BufferVk.h"
+#include "libANGLE/renderer/vulkan/CommandBufferNode.h"
#include "libANGLE/renderer/vulkan/CompilerVk.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/DeviceVk.h"
@@ -62,7 +63,11 @@
} // anonymous namespace
ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer)
- : ContextImpl(state), mRenderer(renderer), mCurrentDrawMode(GL_NONE)
+ : ContextImpl(state),
+ mRenderer(renderer),
+ mCurrentDrawMode(GL_NONE),
+ mVertexArrayDirty(false),
+ mTexturesDirty(false)
{
// The module handle is filled out at draw time.
mCurrentShaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@@ -238,15 +243,14 @@
gl::Error ContextVk::flush(const gl::Context *context)
{
+ // TODO(jmadill): Flush will need to insert a semaphore for the next flush to wait on.
UNIMPLEMENTED();
return gl::InternalError();
}
gl::Error ContextVk::finish(const gl::Context *context)
{
- // TODO(jmadill): Implement finish.
- // UNIMPLEMENTED();
- return gl::NoError();
+ return mRenderer->finish(context);
}
gl::Error ContextVk::initPipeline(const gl::Context *context)
@@ -295,7 +299,10 @@
return gl::NoError();
}
-gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode, DrawType drawType)
+gl::Error ContextVk::setupDraw(const gl::Context *context,
+ GLenum mode,
+ DrawType drawType,
+ vk::CommandBuffer **commandBuffer)
{
if (mode != mCurrentDrawMode)
{
@@ -325,23 +332,65 @@
angle::MemoryBuffer *zeroBuf = nullptr;
ANGLE_TRY(context->getZeroFilledBuffer(maxAttrib * sizeof(VkDeviceSize), &zeroBuf));
- vk::CommandBufferAndState *commandBuffer = nullptr;
- ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer));
- ANGLE_TRY(mRenderer->ensureInRenderPass(context, vkFBO));
+ // TODO(jmadill): Need to link up the TextureVk to the Secondary CB.
+ vk::CommandBufferNode *renderNode = nullptr;
+ ANGLE_TRY(vkFBO->getRenderNode(context, &renderNode));
- commandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline);
- commandBuffer->bindVertexBuffers(0, maxAttrib, vertexHandles.data(),
- reinterpret_cast<const VkDeviceSize *>(zeroBuf->data()));
+ if (!renderNode->getInsideRenderPassCommands()->valid())
+ {
+ mVertexArrayDirty = true;
+ mTexturesDirty = true;
+ ANGLE_TRY(renderNode->startRenderPassRecording(mRenderer, commandBuffer));
+ }
+ else
+ {
+ *commandBuffer = renderNode->getInsideRenderPassCommands();
+ }
+ // Ensure any writes to the VAO buffers are flushed before we read from them.
+ if (mVertexArrayDirty)
+ {
+ mVertexArrayDirty = false;
+ vkVAO->updateDrawDependencies(renderNode, programGL->getActiveAttribLocationsMask(),
+ queueSerial, drawType);
+ }
+
+ // Ensure any writes to the textures are flushed before we read from them.
+ if (mTexturesDirty)
+ {
+ mTexturesDirty = false;
+ // TODO(jmadill): Should probably merge this for loop with programVk's descriptor update.
+ const auto &completeTextures = state.getCompleteTextureCache();
+ for (const gl::SamplerBinding &samplerBinding : programGL->getSamplerBindings())
+ {
+ ASSERT(!samplerBinding.unreferenced);
+
+ // TODO(jmadill): Sampler arrays
+ ASSERT(samplerBinding.boundTextureUnits.size() == 1);
+
+ GLuint textureUnit = samplerBinding.boundTextureUnits[0];
+ const gl::Texture *texture = completeTextures[textureUnit];
+
+ // TODO(jmadill): Incomplete textures handling.
+ ASSERT(texture);
+
+ TextureVk *textureVk = vk::GetImpl(texture);
+ textureVk->updateDependencies(renderNode, mRenderer->getCurrentQueueSerial());
+ }
+ }
+
+ (*commandBuffer)->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline);
+ (*commandBuffer)
+ ->bindVertexBuffers(0, maxAttrib, vertexHandles.data(),
+ reinterpret_cast<const VkDeviceSize *>(zeroBuf->data()));
+
+ // Update the queue serial for the pipeline object.
// TODO(jmadill): the queue serial should be bound to the pipeline.
- setQueueSerial(queueSerial);
- vkVAO->updateCurrentBufferSerials(programGL->getActiveAttribLocationsMask(), queueSerial,
- drawType);
+ updateQueueSerial(queueSerial);
// TODO(jmadill): Can probably use more dirty bits here.
- ContextVk *contextVk = vk::GetImpl(context);
- ANGLE_TRY(programVk->updateUniforms(contextVk));
- programVk->updateTexturesDescriptorSet(contextVk);
+ ANGLE_TRY(programVk->updateUniforms(this));
+ programVk->updateTexturesDescriptorSet(this);
// Bind the graphics descriptor sets.
// TODO(jmadill): Handle multiple command buffers.
@@ -351,9 +400,9 @@
if (!descriptorSets.empty() && ((setCount - firstSet) > 0))
{
const vk::PipelineLayout &pipelineLayout = programVk->getPipelineLayout();
- commandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, firstSet,
- setCount - firstSet, &descriptorSets[firstSet], 0,
- nullptr);
+ (*commandBuffer)
+ ->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, firstSet,
+ setCount - firstSet, &descriptorSets[firstSet], 0, nullptr);
}
return gl::NoError();
@@ -361,11 +410,8 @@
gl::Error ContextVk::drawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count)
{
- ANGLE_TRY(setupDraw(context, mode, DrawType::Arrays));
-
- vk::CommandBufferAndState *commandBuffer = nullptr;
- ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer));
-
+ vk::CommandBuffer *commandBuffer = nullptr;
+ ANGLE_TRY(setupDraw(context, mode, DrawType::Arrays, &commandBuffer));
commandBuffer->draw(count, 1, first, 0);
return gl::NoError();
}
@@ -386,7 +432,8 @@
GLenum type,
const void *indices)
{
- ANGLE_TRY(setupDraw(context, mode, DrawType::Elements));
+ vk::CommandBuffer *commandBuffer;
+ ANGLE_TRY(setupDraw(context, mode, DrawType::Elements, &commandBuffer));
if (indices)
{
@@ -402,9 +449,6 @@
return gl::InternalError() << "Unsigned byte translation is not yet implemented.";
}
- vk::CommandBufferAndState *commandBuffer = nullptr;
- ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer));
-
const gl::Buffer *elementArrayBuffer =
mState.getState().getVertexArray()->getElementArrayBuffer().get();
ASSERT(elementArrayBuffer);
@@ -444,18 +488,6 @@
return mRenderer->getDevice();
}
-vk::Error ContextVk::getStartedCommandBuffer(vk::CommandBufferAndState **commandBufferOut)
-{
- return mRenderer->getStartedCommandBuffer(commandBufferOut);
-}
-
-vk::Error ContextVk::submitCommands(vk::CommandBufferAndState *commandBuffer)
-{
- setQueueSerial(mRenderer->getCurrentQueueSerial());
- ANGLE_TRY(mRenderer->submitCommandBuffer(commandBuffer));
- return vk::NoError();
-}
-
gl::Error ContextVk::drawArraysIndirect(const gl::Context *context,
GLenum mode,
const void *indirect)
@@ -681,7 +713,7 @@
WARN() << "DIRTY_BIT_RENDERBUFFER_BINDING unimplemented";
break;
case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING:
- WARN() << "DIRTY_BIT_VERTEX_ARRAY_BINDING unimplemented";
+ mVertexArrayDirty = true;
break;
case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING:
WARN() << "DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING unimplemented";
@@ -755,6 +787,7 @@
{
ProgramVk *programVk = vk::GetImpl(glState.getProgram());
programVk->invalidateTextures();
+ mTexturesDirty = true;
}
}
@@ -875,6 +908,13 @@
mRenderer->releaseResource(*this, &mCurrentPipeline);
}
+void ContextVk::onVertexArrayChange()
+{
+ // TODO(jmadill): Does not handle dependent state changes.
+ mVertexArrayDirty = true;
+ invalidateCurrentPipeline();
+}
+
gl::Error ContextVk::dispatchCompute(const gl::Context *context,
GLuint numGroupsX,
GLuint numGroupsY,