Commit 49ac74bd by Jamie Madill Committed by Commit Bot

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/780849Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent b8cb939f
...@@ -191,11 +191,8 @@ vk::Error BufferVk::setDataImpl(ContextVk *contextVk, ...@@ -191,11 +191,8 @@ vk::Error BufferVk::setDataImpl(ContextVk *contextVk,
stagingBuffer.getDeviceMemory().unmap(device); stagingBuffer.getDeviceMemory().unmap(device);
// Enqueue a copy command on the GPU. // Enqueue a copy command on the GPU.
// TODO(jmadill): Command re-ordering for render passes. vk::CommandBuffer *commandBuffer = nullptr;
renderer->endRenderPass(); ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer));
vk::CommandBufferAndState *commandBuffer = nullptr;
ANGLE_TRY(renderer->getStartedCommandBuffer(&commandBuffer));
// Insert a barrier to ensure reads from the buffer are complete. // Insert a barrier to ensure reads from the buffer are complete.
// TODO(jmadill): Insert minimal barriers. // TODO(jmadill): Insert minimal barriers.
...@@ -216,7 +213,8 @@ vk::Error BufferVk::setDataImpl(ContextVk *contextVk, ...@@ -216,7 +213,8 @@ vk::Error BufferVk::setDataImpl(ContextVk *contextVk,
VkBufferCopy copyRegion = {offset, 0, size}; VkBufferCopy copyRegion = {offset, 0, size};
commandBuffer->copyBuffer(stagingBuffer.getBuffer(), mBuffer, 1, &copyRegion); commandBuffer->copyBuffer(stagingBuffer.getBuffer(), mBuffer, 1, &copyRegion);
setQueueSerial(renderer->getCurrentQueueSerial()); // Immediately release staging buffer.
// TODO(jmadill): Staging buffer re-use.
renderer->releaseObject(getQueueSerial(), &stagingBuffer); renderer->releaseObject(getQueueSerial(), &stagingBuffer);
} }
else else
......
//
// Copyright 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// CommandBufferNode:
// Deferred work constructed by GL calls, that will later be flushed to Vulkan.
//
#ifndef LIBANGLE_RENDERER_VULKAN_COMMAND_BUFFER_NODE_H_
#define LIBANGLE_RENDERER_VULKAN_COMMAND_BUFFER_NODE_H_
#include "libANGLE/renderer/vulkan/renderervk_utils.h"
namespace rx
{
namespace vk
{
enum class VisitedState
{
Unvisited,
Ready,
Visited,
};
class CommandBufferNode final : angle::NonCopyable
{
public:
CommandBufferNode();
~CommandBufferNode();
// Immutable queries for when we're walking the commands tree.
CommandBuffer *getOutsideRenderPassCommands();
CommandBuffer *getInsideRenderPassCommands();
// For outside the render pass (copies, transitions, etc).
Error startRecording(VkDevice device,
const CommandPool &commandPool,
CommandBuffer **commandsOut);
// For rendering commands (draws).
Error startRenderPassRecording(RendererVk *renderer, CommandBuffer **commandsOut);
// Commands for storing info relevant to the RenderPass.
// RenderTargets must be added in order, with the depth/stencil being added last.
void storeRenderPassInfo(const Framebuffer &framebuffer,
const gl::Rectangle renderArea,
const std::vector<VkClearValue> &clearValues);
void appendColorRenderTarget(Serial serial, RenderTargetVk *colorRenderTarget);
void appendDepthStencilRenderTarget(Serial serial, RenderTargetVk *depthStencilRenderTarget);
// Commands for linking nodes in the dependency graph.
void addDependency(CommandBufferNode *node);
void addDependencies(const std::vector<CommandBufferNode *> &nodes);
bool hasDependencies() const;
bool isDependency() const;
// Used for testing only.
bool hasDependency(CommandBufferNode *searchNode);
// Commands for traversing the node on a flush operation.
VisitedState visitedState() const;
void visitDependencies(std::vector<CommandBufferNode *> *stack);
Error visitAndExecute(RendererVk *renderer, CommandBuffer *primaryCommandBuffer);
private:
void initAttachmentDesc(VkAttachmentDescription *desc);
void markAsDependency();
// Only used if we need a RenderPass for these commands.
RenderPassDesc mRenderPassDesc;
Framebuffer mRenderPassFramebuffer;
gl::Rectangle mRenderPassRenderArea;
gl::AttachmentArray<VkClearValue> mRenderPassClearValues;
// Keep a separate buffers for commands inside and outside a RenderPass.
// TODO(jmadill): We might not need inside and outside RenderPass commands separate.
CommandBuffer mOutsideRenderPassCommands;
CommandBuffer mInsideRenderPassCommands;
// Dependency commands must finish before these command can execute.
std::vector<CommandBufferNode *> mDependencies;
bool mIsDependency;
// Used when traversing the dependency graph.
VisitedState mVisitedState;
};
} // namespace vk
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_COMMAND_BUFFER_NODE_H_
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Program.h" #include "libANGLE/Program.h"
#include "libANGLE/renderer/vulkan/BufferVk.h" #include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/CommandBufferNode.h"
#include "libANGLE/renderer/vulkan/CompilerVk.h" #include "libANGLE/renderer/vulkan/CompilerVk.h"
#include "libANGLE/renderer/vulkan/ContextVk.h" #include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/DeviceVk.h" #include "libANGLE/renderer/vulkan/DeviceVk.h"
...@@ -62,7 +63,11 @@ enum DescriptorPoolIndex : uint8_t ...@@ -62,7 +63,11 @@ enum DescriptorPoolIndex : uint8_t
} // anonymous namespace } // anonymous namespace
ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer) 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. // The module handle is filled out at draw time.
mCurrentShaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; mCurrentShaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
...@@ -238,15 +243,14 @@ gl::Error ContextVk::initialize() ...@@ -238,15 +243,14 @@ gl::Error ContextVk::initialize()
gl::Error ContextVk::flush(const gl::Context *context) 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(); UNIMPLEMENTED();
return gl::InternalError(); return gl::InternalError();
} }
gl::Error ContextVk::finish(const gl::Context *context) gl::Error ContextVk::finish(const gl::Context *context)
{ {
// TODO(jmadill): Implement finish. return mRenderer->finish(context);
// UNIMPLEMENTED();
return gl::NoError();
} }
gl::Error ContextVk::initPipeline(const gl::Context *context) gl::Error ContextVk::initPipeline(const gl::Context *context)
...@@ -295,7 +299,10 @@ gl::Error ContextVk::initPipeline(const gl::Context *context) ...@@ -295,7 +299,10 @@ gl::Error ContextVk::initPipeline(const gl::Context *context)
return gl::NoError(); 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) if (mode != mCurrentDrawMode)
{ {
...@@ -325,23 +332,65 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode, DrawType ...@@ -325,23 +332,65 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode, DrawType
angle::MemoryBuffer *zeroBuf = nullptr; angle::MemoryBuffer *zeroBuf = nullptr;
ANGLE_TRY(context->getZeroFilledBuffer(maxAttrib * sizeof(VkDeviceSize), &zeroBuf)); ANGLE_TRY(context->getZeroFilledBuffer(maxAttrib * sizeof(VkDeviceSize), &zeroBuf));
vk::CommandBufferAndState *commandBuffer = nullptr; // TODO(jmadill): Need to link up the TextureVk to the Secondary CB.
ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer)); vk::CommandBufferNode *renderNode = nullptr;
ANGLE_TRY(mRenderer->ensureInRenderPass(context, vkFBO)); ANGLE_TRY(vkFBO->getRenderNode(context, &renderNode));
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];
commandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline); // TODO(jmadill): Incomplete textures handling.
commandBuffer->bindVertexBuffers(0, maxAttrib, vertexHandles.data(), ASSERT(texture);
reinterpret_cast<const VkDeviceSize *>(zeroBuf->data()));
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. // TODO(jmadill): the queue serial should be bound to the pipeline.
setQueueSerial(queueSerial); updateQueueSerial(queueSerial);
vkVAO->updateCurrentBufferSerials(programGL->getActiveAttribLocationsMask(), queueSerial,
drawType);
// TODO(jmadill): Can probably use more dirty bits here. // TODO(jmadill): Can probably use more dirty bits here.
ContextVk *contextVk = vk::GetImpl(context); ANGLE_TRY(programVk->updateUniforms(this));
ANGLE_TRY(programVk->updateUniforms(contextVk)); programVk->updateTexturesDescriptorSet(this);
programVk->updateTexturesDescriptorSet(contextVk);
// Bind the graphics descriptor sets. // Bind the graphics descriptor sets.
// TODO(jmadill): Handle multiple command buffers. // TODO(jmadill): Handle multiple command buffers.
...@@ -351,9 +400,9 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode, DrawType ...@@ -351,9 +400,9 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode, DrawType
if (!descriptorSets.empty() && ((setCount - firstSet) > 0)) if (!descriptorSets.empty() && ((setCount - firstSet) > 0))
{ {
const vk::PipelineLayout &pipelineLayout = programVk->getPipelineLayout(); const vk::PipelineLayout &pipelineLayout = programVk->getPipelineLayout();
commandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, firstSet, (*commandBuffer)
setCount - firstSet, &descriptorSets[firstSet], 0, ->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, firstSet,
nullptr); setCount - firstSet, &descriptorSets[firstSet], 0, nullptr);
} }
return gl::NoError(); return gl::NoError();
...@@ -361,11 +410,8 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode, DrawType ...@@ -361,11 +410,8 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode, DrawType
gl::Error ContextVk::drawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count) gl::Error ContextVk::drawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count)
{ {
ANGLE_TRY(setupDraw(context, mode, DrawType::Arrays)); vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(setupDraw(context, mode, DrawType::Arrays, &commandBuffer));
vk::CommandBufferAndState *commandBuffer = nullptr;
ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer));
commandBuffer->draw(count, 1, first, 0); commandBuffer->draw(count, 1, first, 0);
return gl::NoError(); return gl::NoError();
} }
...@@ -386,7 +432,8 @@ gl::Error ContextVk::drawElements(const gl::Context *context, ...@@ -386,7 +432,8 @@ gl::Error ContextVk::drawElements(const gl::Context *context,
GLenum type, GLenum type,
const void *indices) const void *indices)
{ {
ANGLE_TRY(setupDraw(context, mode, DrawType::Elements)); vk::CommandBuffer *commandBuffer;
ANGLE_TRY(setupDraw(context, mode, DrawType::Elements, &commandBuffer));
if (indices) if (indices)
{ {
...@@ -402,9 +449,6 @@ gl::Error ContextVk::drawElements(const gl::Context *context, ...@@ -402,9 +449,6 @@ gl::Error ContextVk::drawElements(const gl::Context *context,
return gl::InternalError() << "Unsigned byte translation is not yet implemented."; return gl::InternalError() << "Unsigned byte translation is not yet implemented.";
} }
vk::CommandBufferAndState *commandBuffer = nullptr;
ANGLE_TRY(mRenderer->getStartedCommandBuffer(&commandBuffer));
const gl::Buffer *elementArrayBuffer = const gl::Buffer *elementArrayBuffer =
mState.getState().getVertexArray()->getElementArrayBuffer().get(); mState.getState().getVertexArray()->getElementArrayBuffer().get();
ASSERT(elementArrayBuffer); ASSERT(elementArrayBuffer);
...@@ -444,18 +488,6 @@ VkDevice ContextVk::getDevice() const ...@@ -444,18 +488,6 @@ VkDevice ContextVk::getDevice() const
return mRenderer->getDevice(); 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, gl::Error ContextVk::drawArraysIndirect(const gl::Context *context,
GLenum mode, GLenum mode,
const void *indirect) const void *indirect)
...@@ -681,7 +713,7 @@ void ContextVk::syncState(const gl::Context *context, const gl::State::DirtyBits ...@@ -681,7 +713,7 @@ void ContextVk::syncState(const gl::Context *context, const gl::State::DirtyBits
WARN() << "DIRTY_BIT_RENDERBUFFER_BINDING unimplemented"; WARN() << "DIRTY_BIT_RENDERBUFFER_BINDING unimplemented";
break; break;
case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING: case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING:
WARN() << "DIRTY_BIT_VERTEX_ARRAY_BINDING unimplemented"; mVertexArrayDirty = true;
break; break;
case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING: case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING:
WARN() << "DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING unimplemented"; WARN() << "DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING unimplemented";
...@@ -755,6 +787,7 @@ void ContextVk::syncState(const gl::Context *context, const gl::State::DirtyBits ...@@ -755,6 +787,7 @@ void ContextVk::syncState(const gl::Context *context, const gl::State::DirtyBits
{ {
ProgramVk *programVk = vk::GetImpl(glState.getProgram()); ProgramVk *programVk = vk::GetImpl(glState.getProgram());
programVk->invalidateTextures(); programVk->invalidateTextures();
mTexturesDirty = true;
} }
} }
...@@ -875,6 +908,13 @@ void ContextVk::invalidateCurrentPipeline() ...@@ -875,6 +908,13 @@ void ContextVk::invalidateCurrentPipeline()
mRenderer->releaseResource(*this, &mCurrentPipeline); 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, gl::Error ContextVk::dispatchCompute(const gl::Context *context,
GLuint numGroupsX, GLuint numGroupsX,
GLuint numGroupsY, GLuint numGroupsY,
......
...@@ -140,15 +140,6 @@ class ContextVk : public ContextImpl, public ResourceVk ...@@ -140,15 +140,6 @@ class ContextVk : public ContextImpl, public ResourceVk
// Path object creation // Path object creation
std::vector<PathImpl *> createPaths(GLsizei) override; std::vector<PathImpl *> createPaths(GLsizei) override;
VkDevice getDevice() const;
vk::Error getStartedCommandBuffer(vk::CommandBufferAndState **commandBufferOut);
vk::Error submitCommands(vk::CommandBufferAndState *commandBuffer);
RendererVk *getRenderer() { return mRenderer; }
// TODO(jmadill): Use pipeline cache.
void invalidateCurrentPipeline();
gl::Error dispatchCompute(const gl::Context *context, gl::Error dispatchCompute(const gl::Context *context,
GLuint numGroupsX, GLuint numGroupsX,
GLuint numGroupsY, GLuint numGroupsY,
...@@ -158,11 +149,22 @@ class ContextVk : public ContextImpl, public ResourceVk ...@@ -158,11 +149,22 @@ class ContextVk : public ContextImpl, public ResourceVk
gl::Error memoryBarrier(const gl::Context *context, GLbitfield barriers) override; gl::Error memoryBarrier(const gl::Context *context, GLbitfield barriers) override;
gl::Error memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers) override; gl::Error memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers) override;
VkDevice getDevice() const;
RendererVk *getRenderer() { return mRenderer; }
// TODO(jmadill): Use pipeline cache.
void invalidateCurrentPipeline();
void onVertexArrayChange();
vk::DescriptorPool *getDescriptorPool(); vk::DescriptorPool *getDescriptorPool();
private: private:
gl::Error initPipeline(const gl::Context *context); gl::Error initPipeline(const gl::Context *context);
gl::Error setupDraw(const gl::Context *context, GLenum mode, DrawType drawType); gl::Error setupDraw(const gl::Context *context,
GLenum mode,
DrawType drawType,
vk::CommandBuffer **commandBuffer);
RendererVk *mRenderer; RendererVk *mRenderer;
vk::Pipeline mCurrentPipeline; vk::Pipeline mCurrentPipeline;
...@@ -187,6 +189,10 @@ class ContextVk : public ContextImpl, public ResourceVk ...@@ -187,6 +189,10 @@ class ContextVk : public ContextImpl, public ResourceVk
// The descriptor pool is externally sychronized, so cannot be accessed from different threads // The descriptor pool is externally sychronized, so cannot be accessed from different threads
// simulataneously. Hence, we keep it in the ContextVk instead of the RendererVk. // simulataneously. Hence, we keep it in the ContextVk instead of the RendererVk.
vk::DescriptorPool mDescriptorPool; vk::DescriptorPool mDescriptorPool;
// Triggers adding dependencies to the command graph.
bool mVertexArrayDirty;
bool mTexturesDirty;
}; };
} // namespace rx } // namespace rx
......
...@@ -84,12 +84,8 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk ...@@ -84,12 +84,8 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk
gl::Error getSamplePosition(size_t index, GLfloat *xy) const override; gl::Error getSamplePosition(size_t index, GLfloat *xy) const override;
gl::Error beginRenderPass(const gl::Context *context,
RendererVk *rendererVk,
vk::CommandBuffer *commandBuffer,
Serial queueSerial);
const vk::RenderPassDesc &getRenderPassDesc(const gl::Context *context); const vk::RenderPassDesc &getRenderPassDesc(const gl::Context *context);
gl::Error getRenderNode(const gl::Context *context, vk::CommandBufferNode **nodeOut);
private: private:
FramebufferVk(const gl::FramebufferState &state); FramebufferVk(const gl::FramebufferState &state);
...@@ -102,6 +98,7 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk ...@@ -102,6 +98,7 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk
Optional<vk::RenderPassDesc> mRenderPassDesc; Optional<vk::RenderPassDesc> mRenderPassDesc;
vk::Framebuffer mFramebuffer; vk::Framebuffer mFramebuffer;
bool mRenderNodeDirty;
}; };
} // namespace rx } // namespace rx
......
...@@ -79,14 +79,12 @@ class RendererVk : angle::NonCopyable ...@@ -79,14 +79,12 @@ class RendererVk : angle::NonCopyable
vk::ErrorOrResult<uint32_t> selectPresentQueueForSurface(VkSurfaceKHR surface); vk::ErrorOrResult<uint32_t> selectPresentQueueForSurface(VkSurfaceKHR surface);
// TODO(jmadill): Use ContextImpl for command buffers to enable threaded contexts. vk::Error finish(const gl::Context *context);
vk::Error getStartedCommandBuffer(vk::CommandBufferAndState **commandBufferOut); vk::Error flush(const gl::Context *context,
vk::Error submitCommandBuffer(vk::CommandBufferAndState *commandBuffer); const vk::Semaphore &waitSemaphore,
vk::Error submitAndFinishCommandBuffer(vk::CommandBufferAndState *commandBuffer); const vk::Semaphore &signalSemaphore);
vk::Error submitCommandsWithSync(vk::CommandBufferAndState *commandBuffer,
const vk::Semaphore &waitSemaphore, const vk::CommandPool &getCommandPool() const;
const vk::Semaphore &signalSemaphore);
vk::Error finish();
const gl::Caps &getNativeCaps() const; const gl::Caps &getNativeCaps() const;
const gl::TextureCapsMap &getNativeTextureCaps() const; const gl::TextureCapsMap &getNativeTextureCaps() const;
...@@ -130,13 +128,6 @@ class RendererVk : angle::NonCopyable ...@@ -130,13 +128,6 @@ class RendererVk : angle::NonCopyable
const vk::MemoryProperties &getMemoryProperties() const { return mMemoryProperties; } const vk::MemoryProperties &getMemoryProperties() const { return mMemoryProperties; }
// TODO(jmadill): Don't keep a single renderpass in the Renderer.
gl::Error ensureInRenderPass(const gl::Context *context, FramebufferVk *framebufferVk);
void endRenderPass();
// This is necessary to update the cached current RenderPass Framebuffer.
void onReleaseRenderPass(const FramebufferVk *framebufferVk);
// TODO(jmadill): We could pass angle::Format::ID here. // TODO(jmadill): We could pass angle::Format::ID here.
const vk::Format &getFormat(GLenum internalFormat) const const vk::Format &getFormat(GLenum internalFormat) const
{ {
...@@ -149,16 +140,22 @@ class RendererVk : angle::NonCopyable ...@@ -149,16 +140,22 @@ class RendererVk : angle::NonCopyable
const vk::AttachmentOpsArray &ops, const vk::AttachmentOpsArray &ops,
vk::RenderPass **renderPassOut); vk::RenderPass **renderPassOut);
// This should only be called from ResourceVk.
// TODO(jmadill): Keep in ContextVk to enable threaded rendering.
vk::CommandBufferNode *allocateCommandNode();
private: private:
vk::Error initializeDevice(uint32_t queueFamilyIndex);
void ensureCapsInitialized() const; void ensureCapsInitialized() const;
void generateCaps(gl::Caps *outCaps, void generateCaps(gl::Caps *outCaps,
gl::TextureCapsMap *outTextureCaps, gl::TextureCapsMap *outTextureCaps,
gl::Extensions *outExtensions, gl::Extensions *outExtensions,
gl::Limitations *outLimitations) const; gl::Limitations *outLimitations) const;
vk::Error submit(const VkSubmitInfo &submitInfo); vk::Error submitFrame(const VkSubmitInfo &submitInfo, vk::CommandBuffer &&commandBatch);
vk::Error submitFrame(const VkSubmitInfo &submitInfo);
vk::Error checkInFlightCommands(); vk::Error checkInFlightCommands();
void freeAllInFlightResources(); void freeAllInFlightResources();
vk::Error flushCommandGraph(const gl::Context *context, vk::CommandBuffer *commandBatch);
void resetCommandGraph();
mutable bool mCapsInitialized; mutable bool mCapsInitialized;
mutable gl::Caps mNativeCaps; mutable gl::Caps mNativeCaps;
...@@ -166,8 +163,6 @@ class RendererVk : angle::NonCopyable ...@@ -166,8 +163,6 @@ class RendererVk : angle::NonCopyable
mutable gl::Extensions mNativeExtensions; mutable gl::Extensions mNativeExtensions;
mutable gl::Limitations mNativeLimitations; mutable gl::Limitations mNativeLimitations;
vk::Error initializeDevice(uint32_t queueFamilyIndex);
VkInstance mInstance; VkInstance mInstance;
bool mEnableValidationLayers; bool mEnableValidationLayers;
VkDebugReportCallbackEXT mDebugReportCallback; VkDebugReportCallbackEXT mDebugReportCallback;
...@@ -178,21 +173,30 @@ class RendererVk : angle::NonCopyable ...@@ -178,21 +173,30 @@ class RendererVk : angle::NonCopyable
uint32_t mCurrentQueueFamilyIndex; uint32_t mCurrentQueueFamilyIndex;
VkDevice mDevice; VkDevice mDevice;
vk::CommandPool mCommandPool; vk::CommandPool mCommandPool;
vk::CommandBufferAndState mCommandBuffer;
GlslangWrapper *mGlslangWrapper; GlslangWrapper *mGlslangWrapper;
SerialFactory mQueueSerialFactory; SerialFactory mQueueSerialFactory;
Serial mLastCompletedQueueSerial; Serial mLastCompletedQueueSerial;
Serial mCurrentQueueSerial; Serial mCurrentQueueSerial;
std::vector<vk::CommandBufferAndSerial> mInFlightCommands;
std::vector<vk::FenceAndSerial> mInFlightFences; struct CommandBatch final : angle::NonCopyable
{
CommandBatch();
~CommandBatch();
CommandBatch(CommandBatch &&other);
CommandBatch &operator=(CommandBatch &&other);
vk::CommandPool commandPool;
vk::Fence fence;
Serial serial;
};
std::vector<CommandBatch> mInFlightCommands;
std::vector<vk::GarbageObject> mGarbage; std::vector<vk::GarbageObject> mGarbage;
vk::MemoryProperties mMemoryProperties; vk::MemoryProperties mMemoryProperties;
vk::FormatTable mFormatTable; vk::FormatTable mFormatTable;
// TODO(jmadill): Don't keep a single renderpass in the Renderer.
FramebufferVk *mCurrentRenderPassFramebuffer;
RenderPassCache mRenderPassCache; RenderPassCache mRenderPassCache;
std::vector<vk::CommandBufferNode *> mOpenCommandGraph;
}; };
} // namespace rx } // namespace rx
......
...@@ -192,7 +192,7 @@ void WindowSurfaceVk::destroy(const egl::Display *display) ...@@ -192,7 +192,7 @@ void WindowSurfaceVk::destroy(const egl::Display *display)
VkDevice device = rendererVk->getDevice(); VkDevice device = rendererVk->getDevice();
VkInstance instance = rendererVk->getInstance(); VkInstance instance = rendererVk->getInstance();
rendererVk->finish(); rendererVk->finish(display->getProxyContext());
mAcquireNextImageSemaphore.destroy(device); mAcquireNextImageSemaphore.destroy(device);
...@@ -359,9 +359,9 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer) ...@@ -359,9 +359,9 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer)
std::vector<VkImage> swapchainImages(imageCount); std::vector<VkImage> swapchainImages(imageCount);
ANGLE_VK_TRY(vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, swapchainImages.data())); ANGLE_VK_TRY(vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, swapchainImages.data()));
// CommandBuffer is a singleton in the Renderer. // Allocate a command buffer for clearing our images to black.
vk::CommandBufferAndState *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(renderer->getStartedCommandBuffer(&commandBuffer)); ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer));
VkClearColorValue transparentBlack; VkClearColorValue transparentBlack;
transparentBlack.float32[0] = 0.0f; transparentBlack.float32[0] = 0.0f;
...@@ -409,8 +409,6 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer) ...@@ -409,8 +409,6 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer)
ANGLE_TRY(member.commandsCompleteSemaphore.init(device)); ANGLE_TRY(member.commandsCompleteSemaphore.init(device));
} }
ANGLE_TRY(renderer->submitAndFinishCommandBuffer(commandBuffer));
// Get the first available swapchain iamge. // Get the first available swapchain iamge.
ANGLE_TRY(nextSwapchainImage(renderer)); ANGLE_TRY(nextSwapchainImage(renderer));
...@@ -427,20 +425,17 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context) ...@@ -427,20 +425,17 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context)
const DisplayVk *displayVk = vk::GetImpl(context->getCurrentDisplay()); const DisplayVk *displayVk = vk::GetImpl(context->getCurrentDisplay());
RendererVk *renderer = displayVk->getRenderer(); RendererVk *renderer = displayVk->getRenderer();
vk::CommandBufferAndState *currentCB = nullptr; vk::CommandBuffer *swapCommands = nullptr;
ANGLE_TRY(renderer->getStartedCommandBuffer(&currentCB)); ANGLE_TRY(recordWriteCommands(renderer, &swapCommands));
// End render pass
renderer->endRenderPass();
auto &image = mSwapchainImages[mCurrentSwapchainImageIndex]; auto &image = mSwapchainImages[mCurrentSwapchainImageIndex];
image.image.changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, image.image.changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, currentCB); VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, swapCommands);
ANGLE_TRY(renderer->submitCommandsWithSync(currentCB, image.imageAcquiredSemaphore, ANGLE_TRY(
image.commandsCompleteSemaphore)); renderer->flush(context, image.imageAcquiredSemaphore, image.commandsCompleteSemaphore));
VkPresentInfoKHR presentInfo; VkPresentInfoKHR presentInfo;
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
......
...@@ -177,7 +177,6 @@ gl::Error TextureVk::setImage(const gl::Context *context, ...@@ -177,7 +177,6 @@ gl::Error TextureVk::setImage(const gl::Context *context,
mRenderTarget.resource = this; mRenderTarget.resource = this;
// Handle initial data. // Handle initial data.
// TODO(jmadill): Consider re-using staging texture.
if (pixels) if (pixels)
{ {
ANGLE_TRY(setSubImageImpl(contextVk, formatInfo, unpack, type, pixels)); ANGLE_TRY(setSubImageImpl(contextVk, formatInfo, unpack, type, pixels));
...@@ -258,13 +257,8 @@ gl::Error TextureVk::setSubImageImpl(ContextVk *contextVk, ...@@ -258,13 +257,8 @@ gl::Error TextureVk::setSubImageImpl(ContextVk *contextVk,
stagingImage.getDeviceMemory().unmap(device); stagingImage.getDeviceMemory().unmap(device);
vk::CommandBufferAndState *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer)); ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer));
setQueueSerial(renderer->getCurrentQueueSerial());
// Ensure we aren't in a render pass.
// TODO(jmadill): Command reordering.
renderer->endRenderPass();
stagingImage.getImage().changeLayoutWithStages( stagingImage.getImage().changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
...@@ -277,7 +271,8 @@ gl::Error TextureVk::setSubImageImpl(ContextVk *contextVk, ...@@ -277,7 +271,8 @@ gl::Error TextureVk::setSubImageImpl(ContextVk *contextVk,
commandBuffer->copySingleImage(stagingImage.getImage(), mImage, wholeRegion, commandBuffer->copySingleImage(stagingImage.getImage(), mImage, wholeRegion,
VK_IMAGE_ASPECT_COLOR_BIT); VK_IMAGE_ASPECT_COLOR_BIT);
// TODO(jmadill): Re-use staging images. // Immediately release staging image.
// TODO(jmadill): Staging image re-use.
renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingImage); renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingImage);
return gl::NoError(); return gl::NoError();
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/renderer/vulkan/BufferVk.h" #include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/CommandBufferNode.h"
#include "libANGLE/renderer/vulkan/ContextVk.h" #include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/formatutilsvk.h" #include "libANGLE/renderer/vulkan/formatutilsvk.h"
...@@ -48,7 +49,7 @@ void VertexArrayVk::syncState(const gl::Context *context, ...@@ -48,7 +49,7 @@ void VertexArrayVk::syncState(const gl::Context *context,
// Invalidate current pipeline. // Invalidate current pipeline.
// TODO(jmadill): Use pipeline cache. // TODO(jmadill): Use pipeline cache.
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
contextVk->invalidateCurrentPipeline(); contextVk->onVertexArrayChange();
// Invalidate the vertex descriptions. // Invalidate the vertex descriptions.
invalidateVertexDescriptions(); invalidateVertexDescriptions();
...@@ -107,22 +108,23 @@ const gl::AttribArray<VkBuffer> &VertexArrayVk::getCurrentArrayBufferHandles() c ...@@ -107,22 +108,23 @@ const gl::AttribArray<VkBuffer> &VertexArrayVk::getCurrentArrayBufferHandles() c
return mCurrentArrayBufferHandles; return mCurrentArrayBufferHandles;
} }
void VertexArrayVk::updateCurrentBufferSerials(const gl::AttributesMask &activeAttribsMask, void VertexArrayVk::updateDrawDependencies(vk::CommandBufferNode *readNode,
Serial serial, const gl::AttributesMask &activeAttribsMask,
DrawType drawType) Serial serial,
DrawType drawType)
{ {
// Handle the bound array buffers. // Handle the bound array buffers.
for (auto attribIndex : activeAttribsMask) for (auto attribIndex : activeAttribsMask)
{ {
ASSERT(mCurrentArrayBufferResources[attribIndex]); ASSERT(mCurrentArrayBufferResources[attribIndex]);
mCurrentArrayBufferResources[attribIndex]->setQueueSerial(serial); mCurrentArrayBufferResources[attribIndex]->updateDependencies(readNode, serial);
} }
// Handle the bound element array buffer. // Handle the bound element array buffer.
if (drawType == DrawType::Elements) if (drawType == DrawType::Elements)
{ {
ASSERT(mCurrentElementArrayBufferResource); ASSERT(mCurrentElementArrayBufferResource);
mCurrentElementArrayBufferResource->setQueueSerial(serial); mCurrentElementArrayBufferResource->updateDependencies(readNode, serial);
} }
} }
......
...@@ -30,9 +30,10 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -30,9 +30,10 @@ class VertexArrayVk : public VertexArrayImpl
const gl::AttribArray<VkBuffer> &getCurrentArrayBufferHandles() const; const gl::AttribArray<VkBuffer> &getCurrentArrayBufferHandles() const;
void updateCurrentBufferSerials(const gl::AttributesMask &activeAttribsMask, void updateDrawDependencies(vk::CommandBufferNode *readNode,
Serial serial, const gl::AttributesMask &activeAttribsMask,
DrawType drawType); Serial serial,
DrawType drawType);
void invalidateVertexDescriptions(); void invalidateVertexDescriptions();
void updateVertexDescriptions(const gl::Context *context); void updateVertexDescriptions(const gl::Context *context);
......
...@@ -9,8 +9,11 @@ ...@@ -9,8 +9,11 @@
#include "renderervk_utils.h" #include "renderervk_utils.h"
#include "libANGLE/Context.h"
#include "libANGLE/SizedMRUCache.h" #include "libANGLE/SizedMRUCache.h"
#include "libANGLE/renderer/vulkan/CommandBufferNode.h"
#include "libANGLE/renderer/vulkan/ContextVk.h" #include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/RenderTargetVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h" #include "libANGLE/renderer/vulkan/RendererVk.h"
namespace rx namespace rx
...@@ -315,6 +318,20 @@ CommandBuffer::CommandBuffer() ...@@ -315,6 +318,20 @@ CommandBuffer::CommandBuffer()
{ {
} }
VkCommandBuffer CommandBuffer::releaseHandle()
{
VkCommandBuffer handle = mHandle;
mHandle = nullptr;
return handle;
}
Error CommandBuffer::init(VkDevice device, const VkCommandBufferAllocateInfo &createInfo)
{
ASSERT(!valid());
ANGLE_VK_TRY(vkAllocateCommandBuffers(device, &createInfo, &mHandle));
return NoError();
}
Error CommandBuffer::begin(const VkCommandBufferBeginInfo &info) Error CommandBuffer::begin(const VkCommandBufferBeginInfo &info)
{ {
ASSERT(valid()); ASSERT(valid());
...@@ -366,13 +383,6 @@ void CommandBuffer::destroy(VkDevice device, const vk::CommandPool &commandPool) ...@@ -366,13 +383,6 @@ void CommandBuffer::destroy(VkDevice device, const vk::CommandPool &commandPool)
} }
} }
Error CommandBuffer::init(VkDevice device, const VkCommandBufferAllocateInfo &createInfo)
{
ASSERT(!valid());
ANGLE_VK_TRY(vkAllocateCommandBuffers(device, &createInfo, &mHandle));
return NoError();
}
void CommandBuffer::copyBuffer(const vk::Buffer &srcBuffer, void CommandBuffer::copyBuffer(const vk::Buffer &srcBuffer,
const vk::Buffer &destBuffer, const vk::Buffer &destBuffer,
uint32_t regionCount, uint32_t regionCount,
...@@ -440,27 +450,11 @@ void CommandBuffer::copyImage(const vk::Image &srcImage, ...@@ -440,27 +450,11 @@ void CommandBuffer::copyImage(const vk::Image &srcImage,
dstImage.getCurrentLayout(), 1, regions); dstImage.getCurrentLayout(), 1, regions);
} }
void CommandBuffer::beginRenderPass(const RenderPass &renderPass, void CommandBuffer::beginRenderPass(const VkRenderPassBeginInfo &beginInfo,
const Framebuffer &framebuffer, VkSubpassContents subpassContents)
const gl::Rectangle &renderArea,
uint32_t clearValueCount,
const VkClearValue *clearValues)
{ {
ASSERT(mHandle != VK_NULL_HANDLE); ASSERT(valid());
vkCmdBeginRenderPass(mHandle, &beginInfo, subpassContents);
VkRenderPassBeginInfo beginInfo;
beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
beginInfo.pNext = nullptr;
beginInfo.renderPass = renderPass.getHandle();
beginInfo.framebuffer = framebuffer.getHandle();
beginInfo.renderArea.offset.x = static_cast<uint32_t>(renderArea.x);
beginInfo.renderArea.offset.y = static_cast<uint32_t>(renderArea.y);
beginInfo.renderArea.extent.width = static_cast<uint32_t>(renderArea.width);
beginInfo.renderArea.extent.height = static_cast<uint32_t>(renderArea.height);
beginInfo.clearValueCount = clearValueCount;
beginInfo.pClearValues = clearValues;
vkCmdBeginRenderPass(mHandle, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
} }
void CommandBuffer::endRenderPass() void CommandBuffer::endRenderPass()
...@@ -525,6 +519,13 @@ void CommandBuffer::bindDescriptorSets(VkPipelineBindPoint bindPoint, ...@@ -525,6 +519,13 @@ void CommandBuffer::bindDescriptorSets(VkPipelineBindPoint bindPoint,
descriptorSets, dynamicOffsetCount, dynamicOffsets); descriptorSets, dynamicOffsetCount, dynamicOffsets);
} }
void CommandBuffer::executeCommands(uint32_t commandBufferCount,
const vk::CommandBuffer *commandBuffers)
{
ASSERT(valid());
vkCmdExecuteCommands(mHandle, commandBufferCount, commandBuffers[0].ptr());
}
// Image implementation. // Image implementation.
Image::Image() : mCurrentLayout(VK_IMAGE_LAYOUT_UNDEFINED) Image::Image() : mCurrentLayout(VK_IMAGE_LAYOUT_UNDEFINED)
{ {
...@@ -704,6 +705,11 @@ Error Framebuffer::init(VkDevice device, const VkFramebufferCreateInfo &createIn ...@@ -704,6 +705,11 @@ Error Framebuffer::init(VkDevice device, const VkFramebufferCreateInfo &createIn
return NoError(); return NoError();
} }
void Framebuffer::setHandle(VkFramebuffer handle)
{
mHandle = handle;
}
// DeviceMemory implementation. // DeviceMemory implementation.
DeviceMemory::DeviceMemory() DeviceMemory::DeviceMemory()
{ {
...@@ -1240,58 +1246,6 @@ void GarbageObject::destroy(VkDevice device) ...@@ -1240,58 +1246,6 @@ void GarbageObject::destroy(VkDevice device)
} }
} }
// CommandBufferAndState implementation.
CommandBufferAndState::CommandBufferAndState() : mStarted(false)
{
}
Error CommandBufferAndState::ensureStarted(VkDevice device,
const vk::CommandPool &commandPool,
VkCommandBufferLevel level)
{
ASSERT(commandPool.valid());
if (valid() && mStarted)
{
return NoError();
}
if (!valid())
{
VkCommandBufferAllocateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
createInfo.pNext = nullptr;
createInfo.commandPool = commandPool.getHandle();
createInfo.level = level;
createInfo.commandBufferCount = 1;
ANGLE_TRY(init(device, createInfo));
}
else
{
reset();
}
VkCommandBufferBeginInfo beginInfo;
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.pNext = nullptr;
// TODO(jmadill): Use other flags?
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
ANGLE_TRY(begin(beginInfo));
mStarted = true;
return NoError();
}
Error CommandBufferAndState::ensureFinished()
{
ANGLE_TRY(end());
mStarted = false;
return NoError();
}
// RenderPassDesc implementation. // RenderPassDesc implementation.
RenderPassDesc::RenderPassDesc() RenderPassDesc::RenderPassDesc()
{ {
...@@ -1557,6 +1511,95 @@ VkFrontFace GetFrontFace(GLenum frontFace) ...@@ -1557,6 +1511,95 @@ VkFrontFace GetFrontFace(GLenum frontFace)
} // namespace gl_vk } // namespace gl_vk
ResourceVk::ResourceVk() : mCurrentWriteNode(nullptr)
{
}
ResourceVk::~ResourceVk()
{
}
void ResourceVk::updateQueueSerial(Serial queueSerial)
{
ASSERT(queueSerial >= mStoredQueueSerial);
if (queueSerial > mStoredQueueSerial)
{
mCurrentWriteNode = nullptr;
mCurrentReadNodes.clear();
mStoredQueueSerial = queueSerial;
}
}
Serial ResourceVk::getQueueSerial() const
{
return mStoredQueueSerial;
}
bool ResourceVk::isCurrentlyRecording(Serial currentSerial) const
{
return (mStoredQueueSerial == currentSerial && mCurrentWriteNode != nullptr);
}
vk::CommandBufferNode *ResourceVk::getCurrentWriteNode(Serial currentSerial)
{
ASSERT(currentSerial == mStoredQueueSerial);
return mCurrentWriteNode;
}
vk::CommandBufferNode *ResourceVk::getNewWriteNode(RendererVk *renderer)
{
vk::CommandBufferNode *newCommands = renderer->allocateCommandNode();
setWriteNode(renderer->getCurrentQueueSerial(), newCommands);
return newCommands;
}
void ResourceVk::setWriteNode(Serial serial, vk::CommandBufferNode *newCommands)
{
updateQueueSerial(serial);
// Make sure any open reads and writes finish before we execute |newCommands|.
if (!mCurrentReadNodes.empty())
{
newCommands->addDependencies(mCurrentReadNodes);
mCurrentReadNodes.clear();
}
if (mCurrentWriteNode)
{
newCommands->addDependency(mCurrentWriteNode);
}
mCurrentWriteNode = newCommands;
}
vk::Error ResourceVk::recordWriteCommands(RendererVk *renderer,
vk::CommandBuffer **commandBufferOut)
{
vk::CommandBufferNode *commands = getNewWriteNode(renderer);
VkDevice device = renderer->getDevice();
ANGLE_TRY(commands->startRecording(device, renderer->getCommandPool(), commandBufferOut));
return vk::NoError();
}
void ResourceVk::updateDependencies(vk::CommandBufferNode *readNode, Serial serial)
{
if (isCurrentlyRecording(serial))
{
// Link the current write node to "readNode".
readNode->addDependency(getCurrentWriteNode(serial));
ASSERT(mStoredQueueSerial == serial);
}
else
{
updateQueueSerial(serial);
}
// Track "readNode" in this resource.
mCurrentReadNodes.push_back(readNode);
}
} // namespace rx } // namespace rx
std::ostream &operator<<(std::ostream &stream, const rx::vk::Error &error) std::ostream &operator<<(std::ostream &stream, const rx::vk::Error &error)
......
...@@ -49,6 +49,9 @@ ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_OBJECT); ...@@ -49,6 +49,9 @@ ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_OBJECT);
namespace rx namespace rx
{ {
class DisplayVk; class DisplayVk;
class RenderTargetVk;
class RendererVk;
class ResourceVk;
enum class DrawType enum class DrawType
{ {
...@@ -72,27 +75,9 @@ enum class TextureDimension ...@@ -72,27 +75,9 @@ enum class TextureDimension
TEX_2D_ARRAY, TEX_2D_ARRAY,
}; };
// This is a small helper mixin for any GL object used in Vk command buffers. It records a serial
// at command recording times indicating an order in the queue. We use Fences to detect when
// commands finish, and then release any unreferenced and deleted resources based on the stored
// queue serial in a special 'garbage' queue.
class ResourceVk
{
public:
void setQueueSerial(Serial queueSerial)
{
ASSERT(queueSerial >= mStoredQueueSerial);
mStoredQueueSerial = queueSerial;
}
Serial getQueueSerial() const { return mStoredQueueSerial; }
private:
Serial mStoredQueueSerial;
};
namespace vk namespace vk
{ {
class CommandBufferNode;
struct Format; struct Format;
template <typename T> template <typename T>
...@@ -317,6 +302,7 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer> ...@@ -317,6 +302,7 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer>
public: public:
CommandBuffer(); CommandBuffer();
VkCommandBuffer releaseHandle();
void destroy(VkDevice device, const vk::CommandPool &commandPool); void destroy(VkDevice device, const vk::CommandPool &commandPool);
Error init(VkDevice device, const VkCommandBufferAllocateInfo &createInfo); Error init(VkDevice device, const VkCommandBufferAllocateInfo &createInfo);
using WrappedObject::operator=; using WrappedObject::operator=;
...@@ -353,11 +339,7 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer> ...@@ -353,11 +339,7 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer>
uint32_t regionCount, uint32_t regionCount,
const VkImageCopy *regions); const VkImageCopy *regions);
void beginRenderPass(const RenderPass &renderPass, void beginRenderPass(const VkRenderPassBeginInfo &beginInfo, VkSubpassContents subpassContents);
const Framebuffer &framebuffer,
const gl::Rectangle &renderArea,
uint32_t clearValueCount,
const VkClearValue *clearValues);
void endRenderPass(); void endRenderPass();
void draw(uint32_t vertexCount, void draw(uint32_t vertexCount,
...@@ -384,6 +366,8 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer> ...@@ -384,6 +366,8 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer>
const VkDescriptorSet *descriptorSets, const VkDescriptorSet *descriptorSets,
uint32_t dynamicOffsetCount, uint32_t dynamicOffsetCount,
const uint32_t *dynamicOffsets); const uint32_t *dynamicOffsets);
void executeCommands(uint32_t commandBufferCount, const vk::CommandBuffer *commandBuffers);
}; };
class Image final : public WrappedObject<Image, VkImage> class Image final : public WrappedObject<Image, VkImage>
...@@ -446,6 +430,9 @@ class Framebuffer final : public WrappedObject<Framebuffer, VkFramebuffer> ...@@ -446,6 +430,9 @@ class Framebuffer final : public WrappedObject<Framebuffer, VkFramebuffer>
Framebuffer(); Framebuffer();
void destroy(VkDevice device); void destroy(VkDevice device);
// Use this method only in necessary cases. (RenderPass)
void setHandle(VkFramebuffer handle);
Error init(VkDevice device, const VkFramebufferCreateInfo &createInfo); Error init(VkDevice device, const VkFramebufferCreateInfo &createInfo);
}; };
...@@ -662,23 +649,7 @@ struct BufferAndMemory final : private angle::NonCopyable ...@@ -662,23 +649,7 @@ struct BufferAndMemory final : private angle::NonCopyable
vk::DeviceMemory memory; vk::DeviceMemory memory;
}; };
class CommandBufferAndState : public vk::CommandBuffer using CommandBufferAndSerial = ObjectAndSerial<CommandBuffer>;
{
public:
CommandBufferAndState();
Error ensureStarted(VkDevice device,
const vk::CommandPool &commandPool,
VkCommandBufferLevel level);
Error ensureFinished();
bool started() const { return mStarted; }
private:
bool mStarted;
};
using CommandBufferAndSerial = ObjectAndSerial<CommandBufferAndState>;
using FenceAndSerial = ObjectAndSerial<Fence>; using FenceAndSerial = ObjectAndSerial<Fence>;
using RenderPassAndSerial = ObjectAndSerial<RenderPass>; using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
...@@ -771,6 +742,46 @@ VkCullModeFlags GetCullMode(const gl::RasterizerState &rasterState); ...@@ -771,6 +742,46 @@ VkCullModeFlags GetCullMode(const gl::RasterizerState &rasterState);
VkFrontFace GetFrontFace(GLenum frontFace); VkFrontFace GetFrontFace(GLenum frontFace);
} // namespace gl_vk } // namespace gl_vk
// This is a helper class for back-end objects used in Vk command buffers. It records a serial
// at command recording times indicating an order in the queue. We use Fences to detect when
// commands finish, and then release any unreferenced and deleted resources based on the stored
// queue serial in a special 'garbage' queue. Resources also track current read and write
// dependencies. Only one command buffer node can be writing to the Resource at a time, but many
// can be reading from it. Together the dependencies will form a command graph at submission time.
class ResourceVk
{
public:
ResourceVk();
virtual ~ResourceVk();
void updateQueueSerial(Serial queueSerial);
Serial getQueueSerial() const;
// Returns true if any tracked read or write nodes match |currentSerial|.
bool isCurrentlyRecording(Serial currentSerial) const;
// Returns the active write node, and asserts |currentSerial| matches the stored serial.
vk::CommandBufferNode *getCurrentWriteNode(Serial currentSerial);
// Allocates a new write node and calls setWriteNode internally.
vk::CommandBufferNode *getNewWriteNode(RendererVk *renderer);
// Called on an operation that will modify this ResourceVk.
void setWriteNode(Serial serial, vk::CommandBufferNode *newCommands);
// Allocates a write node via getNewWriteNode and returns a started command buffer.
// The started command buffer will render outside of a RenderPass.
vk::Error recordWriteCommands(RendererVk *renderer, vk::CommandBuffer **commandBufferOut);
// Sets up the dependency relations. |readNode| has the commands that read from this object.
void updateDependencies(vk::CommandBufferNode *readNode, Serial serial);
private:
Serial mStoredQueueSerial;
std::vector<vk::CommandBufferNode *> mCurrentReadNodes;
vk::CommandBufferNode *mCurrentWriteNode;
};
} // namespace rx } // namespace rx
#define ANGLE_VK_TRY(command) \ #define ANGLE_VK_TRY(command) \
......
...@@ -707,6 +707,8 @@ ...@@ -707,6 +707,8 @@
[ [
'libANGLE/renderer/vulkan/BufferVk.cpp', 'libANGLE/renderer/vulkan/BufferVk.cpp',
'libANGLE/renderer/vulkan/BufferVk.h', 'libANGLE/renderer/vulkan/BufferVk.h',
'libANGLE/renderer/vulkan/CommandBufferNode.cpp',
'libANGLE/renderer/vulkan/CommandBufferNode.h',
'libANGLE/renderer/vulkan/CompilerVk.cpp', 'libANGLE/renderer/vulkan/CompilerVk.cpp',
'libANGLE/renderer/vulkan/CompilerVk.h', 'libANGLE/renderer/vulkan/CompilerVk.h',
'libANGLE/renderer/vulkan/ContextVk.cpp', 'libANGLE/renderer/vulkan/ContextVk.cpp',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment