Commit 0e65454d by Jamie Madill Committed by Commit Bot

Vulkan: Fix circular dependency with resource updates.

The old implementation would try to keep recording draw commands to the same framebuffer write operation even if the vertex array buffer data changed. This would lead to a broken dependency graph. Fix this by forcing any current render operations to create a new node in this case, giving a correct command graph. Old design: - render (creates a CommandBufferNode A) - update buffer (creates a CommandBufferNode B which happens after A) - render (to CommandBuffer A, and gives a circular dependency with B) New design - render (CommandBufferNode A) - update buffer (CommandBufferNode B, happens after A) - render (CommandBufferNode C, happens after B) This also renames some methods to try to clarify them. Bug: angleproject:2350 Change-Id: I6559bed4ed3f58f68771662422c5bef6a505282b Reviewed-on: https://chromium-review.googlesource.com/907416Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 469708e7
...@@ -195,8 +195,10 @@ vk::Error BufferVk::setDataImpl(ContextVk *contextVk, ...@@ -195,8 +195,10 @@ 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.
// 'beginWriteOperation' will stop any subsequent rendering from using the old buffer data,
// by marking any current read operations / command buffers as 'finished'.
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer)); ANGLE_TRY(beginWriteOperation(renderer, &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.
......
...@@ -54,7 +54,9 @@ Error InitAndBeginCommandBuffer(VkDevice device, ...@@ -54,7 +54,9 @@ Error InitAndBeginCommandBuffer(VkDevice device,
// CommandBufferNode implementation. // CommandBufferNode implementation.
CommandBufferNode::CommandBufferNode() CommandBufferNode::CommandBufferNode()
: mIsDependency(false), mVisitedState(VisitedState::Unvisited) : mHasHappensAfterDependencies(false),
mVisitedState(VisitedState::Unvisited),
mIsFinishedRecording(false)
{ {
} }
...@@ -69,11 +71,13 @@ CommandBufferNode::~CommandBufferNode() ...@@ -69,11 +71,13 @@ CommandBufferNode::~CommandBufferNode()
CommandBuffer *CommandBufferNode::getOutsideRenderPassCommands() CommandBuffer *CommandBufferNode::getOutsideRenderPassCommands()
{ {
ASSERT(!mIsFinishedRecording);
return &mOutsideRenderPassCommands; return &mOutsideRenderPassCommands;
} }
CommandBuffer *CommandBufferNode::getInsideRenderPassCommands() CommandBuffer *CommandBufferNode::getInsideRenderPassCommands()
{ {
ASSERT(!mIsFinishedRecording);
return &mInsideRenderPassCommands; return &mInsideRenderPassCommands;
} }
...@@ -81,6 +85,8 @@ Error CommandBufferNode::startRecording(VkDevice device, ...@@ -81,6 +85,8 @@ Error CommandBufferNode::startRecording(VkDevice device,
const CommandPool &commandPool, const CommandPool &commandPool,
CommandBuffer **commandsOut) CommandBuffer **commandsOut)
{ {
ASSERT(!mIsFinishedRecording);
VkCommandBufferInheritanceInfo inheritanceInfo; VkCommandBufferInheritanceInfo inheritanceInfo;
inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
inheritanceInfo.pNext = nullptr; inheritanceInfo.pNext = nullptr;
...@@ -100,6 +106,8 @@ Error CommandBufferNode::startRecording(VkDevice device, ...@@ -100,6 +106,8 @@ Error CommandBufferNode::startRecording(VkDevice device,
Error CommandBufferNode::startRenderPassRecording(RendererVk *renderer, CommandBuffer **commandsOut) Error CommandBufferNode::startRenderPassRecording(RendererVk *renderer, CommandBuffer **commandsOut)
{ {
ASSERT(!mIsFinishedRecording);
// Get a compatible RenderPass from the cache so we can initialize the inheritance info. // Get a compatible RenderPass from the cache so we can initialize the inheritance info.
// TODO(jmadill): Use different query method for compatible vs conformant render pass. // TODO(jmadill): Use different query method for compatible vs conformant render pass.
vk::RenderPass *compatibleRenderPass; vk::RenderPass *compatibleRenderPass;
...@@ -123,6 +131,16 @@ Error CommandBufferNode::startRenderPassRecording(RendererVk *renderer, CommandB ...@@ -123,6 +131,16 @@ Error CommandBufferNode::startRenderPassRecording(RendererVk *renderer, CommandB
return NoError(); return NoError();
} }
bool CommandBufferNode::isFinishedRecording() const
{
return mIsFinishedRecording;
}
void CommandBufferNode::finishRecording()
{
mIsFinishedRecording = true;
}
void CommandBufferNode::storeRenderPassInfo(const Framebuffer &framebuffer, void CommandBufferNode::storeRenderPassInfo(const Framebuffer &framebuffer,
const gl::Rectangle renderArea, const gl::Rectangle renderArea,
const std::vector<VkClearValue> &clearValues) const std::vector<VkClearValue> &clearValues)
...@@ -136,7 +154,7 @@ void CommandBufferNode::appendColorRenderTarget(Serial serial, RenderTargetVk *c ...@@ -136,7 +154,7 @@ void CommandBufferNode::appendColorRenderTarget(Serial serial, RenderTargetVk *c
{ {
// TODO(jmadill): Layout transition? // TODO(jmadill): Layout transition?
mRenderPassDesc.packColorAttachment(*colorRenderTarget->format, colorRenderTarget->samples); mRenderPassDesc.packColorAttachment(*colorRenderTarget->format, colorRenderTarget->samples);
colorRenderTarget->resource->setWriteNode(this, serial); colorRenderTarget->resource->onWriteResource(this, serial);
} }
void CommandBufferNode::appendDepthStencilRenderTarget(Serial serial, void CommandBufferNode::appendDepthStencilRenderTarget(Serial serial,
...@@ -145,7 +163,7 @@ void CommandBufferNode::appendDepthStencilRenderTarget(Serial serial, ...@@ -145,7 +163,7 @@ void CommandBufferNode::appendDepthStencilRenderTarget(Serial serial,
// TODO(jmadill): Layout transition? // TODO(jmadill): Layout transition?
mRenderPassDesc.packDepthStencilAttachment(*depthStencilRenderTarget->format, mRenderPassDesc.packDepthStencilAttachment(*depthStencilRenderTarget->format,
depthStencilRenderTarget->samples); depthStencilRenderTarget->samples);
depthStencilRenderTarget->resource->setWriteNode(this, serial); depthStencilRenderTarget->resource->onWriteResource(this, serial);
} }
void CommandBufferNode::initAttachmentDesc(VkAttachmentDescription *desc) void CommandBufferNode::initAttachmentDesc(VkAttachmentDescription *desc)
...@@ -161,58 +179,69 @@ void CommandBufferNode::initAttachmentDesc(VkAttachmentDescription *desc) ...@@ -161,58 +179,69 @@ void CommandBufferNode::initAttachmentDesc(VkAttachmentDescription *desc)
desc->finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; desc->finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
} }
void CommandBufferNode::addDependency(CommandBufferNode *node) // static
void CommandBufferNode::SetHappensBeforeDependency(CommandBufferNode *beforeNode,
CommandBufferNode *afterNode)
{ {
mDependencies.emplace_back(node); afterNode->mHappensBeforeDependencies.emplace_back(beforeNode);
node->markAsDependency(); beforeNode->setHasHappensAfterDependencies();
ASSERT(node != this && !node->hasDependency(this)); beforeNode->finishRecording();
ASSERT(beforeNode != afterNode && !beforeNode->happensAfter(afterNode));
} }
void CommandBufferNode::addDependencies(const std::vector<CommandBufferNode *> &nodes) // static
void CommandBufferNode::SetHappensBeforeDependencies(
const std::vector<CommandBufferNode *> &beforeNodes,
CommandBufferNode *afterNode)
{ {
mDependencies.insert(mDependencies.end(), nodes.begin(), nodes.end()); afterNode->mHappensBeforeDependencies.insert(afterNode->mHappensBeforeDependencies.end(),
beforeNodes.begin(), beforeNodes.end());
// TODO(jmadill): is there a faster way to do this? // TODO(jmadill): is there a faster way to do this?
for (CommandBufferNode *node : nodes) for (CommandBufferNode *beforeNode : beforeNodes)
{ {
node->markAsDependency(); beforeNode->setHasHappensAfterDependencies();
ASSERT(node != this && !node->hasDependency(this)); beforeNode->finishRecording();
ASSERT(beforeNode != afterNode && !beforeNode->happensAfter(afterNode));
} }
} }
bool CommandBufferNode::hasDependencies() const bool CommandBufferNode::hasHappensBeforeDependencies() const
{ {
return !mDependencies.empty(); return !mHappensBeforeDependencies.empty();
} }
void CommandBufferNode::markAsDependency() void CommandBufferNode::setHasHappensAfterDependencies()
{ {
mIsDependency = true; mHasHappensAfterDependencies = true;
} }
bool CommandBufferNode::isDependency() const bool CommandBufferNode::hasHappensAfterDependencies() const
{ {
return mIsDependency; return mHasHappensAfterDependencies;
} }
// Do not call this in anything but testing code, since it's slow. // Do not call this in anything but testing code, since it's slow.
bool CommandBufferNode::hasDependency(CommandBufferNode *searchNode) bool CommandBufferNode::happensAfter(CommandBufferNode *beforeNode)
{ {
std::set<CommandBufferNode *> visitedList; std::set<CommandBufferNode *> visitedList;
std::vector<CommandBufferNode *> openList; std::vector<CommandBufferNode *> openList;
openList.insert(openList.begin(), mDependencies.begin(), mDependencies.end()); openList.insert(openList.begin(), mHappensBeforeDependencies.begin(),
mHappensBeforeDependencies.end());
while (!openList.empty()) while (!openList.empty())
{ {
CommandBufferNode *node = openList.back(); CommandBufferNode *checkNode = openList.back();
openList.pop_back(); openList.pop_back();
if (visitedList.count(node) == 0) if (visitedList.count(checkNode) == 0)
{ {
if (node == searchNode) if (checkNode == beforeNode)
{ {
return true; return true;
} }
visitedList.insert(node); visitedList.insert(checkNode);
openList.insert(openList.end(), node->mDependencies.begin(), node->mDependencies.end()); openList.insert(openList.end(), checkNode->mHappensBeforeDependencies.begin(),
checkNode->mHappensBeforeDependencies.end());
} }
} }
...@@ -227,7 +256,8 @@ VisitedState CommandBufferNode::visitedState() const ...@@ -227,7 +256,8 @@ VisitedState CommandBufferNode::visitedState() const
void CommandBufferNode::visitDependencies(std::vector<CommandBufferNode *> *stack) void CommandBufferNode::visitDependencies(std::vector<CommandBufferNode *> *stack)
{ {
ASSERT(mVisitedState == VisitedState::Unvisited); ASSERT(mVisitedState == VisitedState::Unvisited);
stack->insert(stack->end(), mDependencies.begin(), mDependencies.end()); stack->insert(stack->end(), mHappensBeforeDependencies.begin(),
mHappensBeforeDependencies.end());
mVisitedState = VisitedState::Ready; mVisitedState = VisitedState::Ready;
} }
......
...@@ -43,6 +43,9 @@ class CommandBufferNode final : angle::NonCopyable ...@@ -43,6 +43,9 @@ class CommandBufferNode final : angle::NonCopyable
// For rendering commands (draws). // For rendering commands (draws).
Error startRenderPassRecording(RendererVk *renderer, CommandBuffer **commandsOut); Error startRenderPassRecording(RendererVk *renderer, CommandBuffer **commandsOut);
bool isFinishedRecording() const;
void finishRecording();
// Commands for storing info relevant to the RenderPass. // Commands for storing info relevant to the RenderPass.
// RenderTargets must be added in order, with the depth/stencil being added last. // RenderTargets must be added in order, with the depth/stencil being added last.
void storeRenderPassInfo(const Framebuffer &framebuffer, void storeRenderPassInfo(const Framebuffer &framebuffer,
...@@ -52,13 +55,15 @@ class CommandBufferNode final : angle::NonCopyable ...@@ -52,13 +55,15 @@ class CommandBufferNode final : angle::NonCopyable
void appendDepthStencilRenderTarget(Serial serial, RenderTargetVk *depthStencilRenderTarget); void appendDepthStencilRenderTarget(Serial serial, RenderTargetVk *depthStencilRenderTarget);
// Commands for linking nodes in the dependency graph. // Commands for linking nodes in the dependency graph.
void addDependency(CommandBufferNode *node); static void SetHappensBeforeDependency(CommandBufferNode *beforeNode,
void addDependencies(const std::vector<CommandBufferNode *> &nodes); CommandBufferNode *afterNode);
bool hasDependencies() const; static void SetHappensBeforeDependencies(const std::vector<CommandBufferNode *> &beforeNodes,
bool isDependency() const; CommandBufferNode *afterNode);
bool hasHappensBeforeDependencies() const;
bool hasHappensAfterDependencies() const;
// Used for testing only. // Used for testing only.
bool hasDependency(CommandBufferNode *searchNode); bool happensAfter(CommandBufferNode *beforeNode);
// Commands for traversing the node on a flush operation. // Commands for traversing the node on a flush operation.
VisitedState visitedState() const; VisitedState visitedState() const;
...@@ -67,7 +72,7 @@ class CommandBufferNode final : angle::NonCopyable ...@@ -67,7 +72,7 @@ class CommandBufferNode final : angle::NonCopyable
private: private:
void initAttachmentDesc(VkAttachmentDescription *desc); void initAttachmentDesc(VkAttachmentDescription *desc);
void markAsDependency(); void setHasHappensAfterDependencies();
// Only used if we need a RenderPass for these commands. // Only used if we need a RenderPass for these commands.
RenderPassDesc mRenderPassDesc; RenderPassDesc mRenderPassDesc;
...@@ -80,12 +85,17 @@ class CommandBufferNode final : angle::NonCopyable ...@@ -80,12 +85,17 @@ class CommandBufferNode final : angle::NonCopyable
CommandBuffer mOutsideRenderPassCommands; CommandBuffer mOutsideRenderPassCommands;
CommandBuffer mInsideRenderPassCommands; CommandBuffer mInsideRenderPassCommands;
// Dependency commands must finish before these command can execute. // These commands must be submitted before 'this' command can be submitted correctly.
std::vector<CommandBufferNode *> mDependencies; std::vector<CommandBufferNode *> mHappensBeforeDependencies;
bool mIsDependency;
// If this is true, other commands exist that must be submitted after 'this' command.
bool mHasHappensAfterDependencies;
// Used when traversing the dependency graph. // Used when traversing the dependency graph.
VisitedState mVisitedState; VisitedState mVisitedState;
// Is recording currently enabled?
bool mIsFinishedRecording;
}; };
} // namespace vk } // namespace vk
} // namespace rx } // namespace rx
......
...@@ -226,7 +226,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, ...@@ -226,7 +226,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
ASSERT(texture); ASSERT(texture);
TextureVk *textureVk = vk::GetImpl(texture); TextureVk *textureVk = vk::GetImpl(texture);
textureVk->setReadNode(renderNode, mRenderer->getCurrentQueueSerial()); textureVk->onReadResource(renderNode, mRenderer->getCurrentQueueSerial());
} }
} }
......
...@@ -153,7 +153,7 @@ gl::Error FramebufferVk::clear(const gl::Context *context, GLbitfield mask) ...@@ -153,7 +153,7 @@ gl::Error FramebufferVk::clear(const gl::Context *context, GLbitfield mask)
RendererVk *renderer = vk::GetImpl(context)->getRenderer(); RendererVk *renderer = vk::GetImpl(context)->getRenderer();
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer)); ANGLE_TRY(beginWriteOperation(renderer, &commandBuffer));
Serial currentSerial = renderer->getCurrentQueueSerial(); Serial currentSerial = renderer->getCurrentQueueSerial();
...@@ -164,7 +164,8 @@ gl::Error FramebufferVk::clear(const gl::Context *context, GLbitfield mask) ...@@ -164,7 +164,8 @@ gl::Error FramebufferVk::clear(const gl::Context *context, GLbitfield mask)
RenderTargetVk *renderTarget = nullptr; RenderTargetVk *renderTarget = nullptr;
ANGLE_TRY(colorAttachment.getRenderTarget(context, &renderTarget)); ANGLE_TRY(colorAttachment.getRenderTarget(context, &renderTarget));
renderTarget->resource->setWriteNode(getCurrentWriteNode(currentSerial), currentSerial); renderTarget->resource->onWriteResource(getCurrentWriteOperation(currentSerial),
currentSerial);
renderTarget->image->changeLayoutWithStages( renderTarget->image->changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
...@@ -267,7 +268,7 @@ gl::Error FramebufferVk::readPixels(const gl::Context *context, ...@@ -267,7 +268,7 @@ gl::Error FramebufferVk::readPixels(const gl::Context *context,
renderTarget->extents, vk::StagingUsage::Read)); renderTarget->extents, vk::StagingUsage::Read));
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer)); ANGLE_TRY(beginWriteOperation(renderer, &commandBuffer));
stagingImage.getImage().changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL, stagingImage.getImage().changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL,
commandBuffer); commandBuffer);
...@@ -478,9 +479,9 @@ gl::Error FramebufferVk::getRenderNode(const gl::Context *context, vk::CommandBu ...@@ -478,9 +479,9 @@ gl::Error FramebufferVk::getRenderNode(const gl::Context *context, vk::CommandBu
RendererVk *renderer = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
Serial currentSerial = renderer->getCurrentQueueSerial(); Serial currentSerial = renderer->getCurrentQueueSerial();
if (isCurrentlyRecording(currentSerial) && mLastRenderNodeSerial == currentSerial) if (hasCurrentWriteOperation(currentSerial) && mLastRenderNodeSerial == currentSerial)
{ {
*nodeOut = getCurrentWriteNode(currentSerial); *nodeOut = getCurrentWriteOperation(currentSerial);
ASSERT((*nodeOut)->getInsideRenderPassCommands()->valid()); ASSERT((*nodeOut)->getInsideRenderPassCommands()->valid());
return gl::NoError(); return gl::NoError();
} }
......
...@@ -793,7 +793,7 @@ vk::Error RendererVk::flushCommandGraph(const gl::Context *context, vk::CommandB ...@@ -793,7 +793,7 @@ vk::Error RendererVk::flushCommandGraph(const gl::Context *context, vk::CommandB
{ {
// Only process commands that don't have child commands. The others will be pulled in // Only process commands that don't have child commands. The others will be pulled in
// automatically. Also skip commands that have already been visited. // automatically. Also skip commands that have already been visited.
if (topLevelNode->isDependency() || if (topLevelNode->hasHappensAfterDependencies() ||
topLevelNode->visitedState() != vk::VisitedState::Unvisited) topLevelNode->visitedState() != vk::VisitedState::Unvisited)
continue; continue;
......
...@@ -364,7 +364,7 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer) ...@@ -364,7 +364,7 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer)
// Allocate a command buffer for clearing our images to black. // Allocate a command buffer for clearing our images to black.
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer)); ANGLE_TRY(beginWriteOperation(renderer, &commandBuffer));
VkClearColorValue transparentBlack; VkClearColorValue transparentBlack;
transparentBlack.float32[0] = 0.0f; transparentBlack.float32[0] = 0.0f;
...@@ -429,7 +429,7 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context) ...@@ -429,7 +429,7 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context)
RendererVk *renderer = displayVk->getRenderer(); RendererVk *renderer = displayVk->getRenderer();
vk::CommandBuffer *swapCommands = nullptr; vk::CommandBuffer *swapCommands = nullptr;
ANGLE_TRY(recordWriteCommands(renderer, &swapCommands)); ANGLE_TRY(beginWriteOperation(renderer, &swapCommands));
auto &image = mSwapchainImages[mCurrentSwapchainImageIndex]; auto &image = mSwapchainImages[mCurrentSwapchainImageIndex];
......
...@@ -254,7 +254,7 @@ gl::Error TextureVk::setSubImageImpl(ContextVk *contextVk, ...@@ -254,7 +254,7 @@ gl::Error TextureVk::setSubImageImpl(ContextVk *contextVk,
stagingImage.getDeviceMemory().unmap(device); stagingImage.getDeviceMemory().unmap(device);
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(recordWriteCommands(renderer, &commandBuffer)); ANGLE_TRY(beginWriteOperation(renderer, &commandBuffer));
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,
......
...@@ -116,14 +116,14 @@ void VertexArrayVk::updateDrawDependencies(vk::CommandBufferNode *readNode, ...@@ -116,14 +116,14 @@ void VertexArrayVk::updateDrawDependencies(vk::CommandBufferNode *readNode,
for (auto attribIndex : activeAttribsMask) for (auto attribIndex : activeAttribsMask)
{ {
ASSERT(mCurrentArrayBufferResources[attribIndex]); ASSERT(mCurrentArrayBufferResources[attribIndex]);
mCurrentArrayBufferResources[attribIndex]->setReadNode(readNode, serial); mCurrentArrayBufferResources[attribIndex]->onReadResource(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->setReadNode(readNode, serial); mCurrentElementArrayBufferResource->onReadResource(readNode, serial);
} }
} }
......
...@@ -1346,7 +1346,7 @@ VkFrontFace GetFrontFace(GLenum frontFace) ...@@ -1346,7 +1346,7 @@ VkFrontFace GetFrontFace(GLenum frontFace)
} // namespace gl_vk } // namespace gl_vk
ResourceVk::ResourceVk() : mCurrentWriteNode(nullptr) ResourceVk::ResourceVk() : mCurrentWriteOperation(nullptr)
{ {
} }
...@@ -1360,8 +1360,8 @@ void ResourceVk::updateQueueSerial(Serial queueSerial) ...@@ -1360,8 +1360,8 @@ void ResourceVk::updateQueueSerial(Serial queueSerial)
if (queueSerial > mStoredQueueSerial) if (queueSerial > mStoredQueueSerial)
{ {
mCurrentWriteNode = nullptr; mCurrentWriteOperation = nullptr;
mCurrentReadNodes.clear(); mCurrentReadOperations.clear();
mStoredQueueSerial = queueSerial; mStoredQueueSerial = queueSerial;
} }
} }
...@@ -1371,25 +1371,26 @@ Serial ResourceVk::getQueueSerial() const ...@@ -1371,25 +1371,26 @@ Serial ResourceVk::getQueueSerial() const
return mStoredQueueSerial; return mStoredQueueSerial;
} }
bool ResourceVk::isCurrentlyRecording(Serial currentSerial) const bool ResourceVk::hasCurrentWriteOperation(Serial currentSerial) const
{ {
return (mStoredQueueSerial == currentSerial && mCurrentWriteNode != nullptr); return (mStoredQueueSerial == currentSerial && mCurrentWriteOperation != nullptr &&
!mCurrentWriteOperation->isFinishedRecording());
} }
vk::CommandBufferNode *ResourceVk::getCurrentWriteNode(Serial currentSerial) vk::CommandBufferNode *ResourceVk::getCurrentWriteOperation(Serial currentSerial)
{ {
ASSERT(currentSerial == mStoredQueueSerial); ASSERT(currentSerial == mStoredQueueSerial);
return mCurrentWriteNode; return mCurrentWriteOperation;
} }
vk::CommandBufferNode *ResourceVk::getNewWriteNode(RendererVk *renderer) vk::CommandBufferNode *ResourceVk::getNewWriteNode(RendererVk *renderer)
{ {
vk::CommandBufferNode *newCommands = renderer->allocateCommandNode(); vk::CommandBufferNode *newCommands = renderer->allocateCommandNode();
setWriteNode(newCommands, renderer->getCurrentQueueSerial()); onWriteResource(newCommands, renderer->getCurrentQueueSerial());
return newCommands; return newCommands;
} }
vk::Error ResourceVk::recordWriteCommands(RendererVk *renderer, vk::Error ResourceVk::beginWriteOperation(RendererVk *renderer,
vk::CommandBuffer **commandBufferOut) vk::CommandBuffer **commandBufferOut)
{ {
vk::CommandBufferNode *commands = getNewWriteNode(renderer); vk::CommandBufferNode *commands = getNewWriteNode(renderer);
...@@ -1399,31 +1400,32 @@ vk::Error ResourceVk::recordWriteCommands(RendererVk *renderer, ...@@ -1399,31 +1400,32 @@ vk::Error ResourceVk::recordWriteCommands(RendererVk *renderer,
return vk::NoError(); return vk::NoError();
} }
void ResourceVk::setWriteNode(vk::CommandBufferNode *writeNode, Serial serial) void ResourceVk::onWriteResource(vk::CommandBufferNode *writeOperation, Serial serial)
{ {
updateQueueSerial(serial); updateQueueSerial(serial);
// Make sure any open reads and writes finish before we execute |newCommands|. // Make sure any open reads and writes finish before we execute 'newCommands'.
if (!mCurrentReadNodes.empty()) if (!mCurrentReadOperations.empty())
{ {
writeNode->addDependencies(mCurrentReadNodes); vk::CommandBufferNode::SetHappensBeforeDependencies(mCurrentReadOperations, writeOperation);
mCurrentReadNodes.clear(); mCurrentReadOperations.clear();
} }
if (mCurrentWriteNode) if (mCurrentWriteOperation)
{ {
writeNode->addDependency(mCurrentWriteNode); vk::CommandBufferNode::SetHappensBeforeDependency(mCurrentWriteOperation, writeOperation);
} }
mCurrentWriteNode = writeNode; mCurrentWriteOperation = writeOperation;
} }
void ResourceVk::setReadNode(vk::CommandBufferNode *readNode, Serial serial) void ResourceVk::onReadResource(vk::CommandBufferNode *readOperation, Serial serial)
{ {
if (isCurrentlyRecording(serial)) if (hasCurrentWriteOperation(serial))
{ {
// Link the current write node to "readNode". // Ensure 'readOperation' happens after the current write commands.
readNode->addDependency(getCurrentWriteNode(serial)); vk::CommandBufferNode::SetHappensBeforeDependency(getCurrentWriteOperation(serial),
readOperation);
ASSERT(mStoredQueueSerial == serial); ASSERT(mStoredQueueSerial == serial);
} }
else else
...@@ -1431,8 +1433,8 @@ void ResourceVk::setReadNode(vk::CommandBufferNode *readNode, Serial serial) ...@@ -1431,8 +1433,8 @@ void ResourceVk::setReadNode(vk::CommandBufferNode *readNode, Serial serial)
updateQueueSerial(serial); updateQueueSerial(serial);
} }
// Track "readNode" in this resource. // Add the read operation to the list of nodes currently reading this resource.
mCurrentReadNodes.push_back(readNode); mCurrentReadOperations.push_back(readOperation);
} }
} // namespace rx } // namespace rx
......
...@@ -684,29 +684,29 @@ class ResourceVk ...@@ -684,29 +684,29 @@ class ResourceVk
void updateQueueSerial(Serial queueSerial); void updateQueueSerial(Serial queueSerial);
Serial getQueueSerial() const; Serial getQueueSerial() const;
// Returns true if any tracked read or write nodes match |currentSerial|. // Returns true if any tracked read or write nodes match 'currentSerial'.
bool isCurrentlyRecording(Serial currentSerial) const; bool hasCurrentWriteOperation(Serial currentSerial) const;
// Returns the active write node, and asserts |currentSerial| matches the stored serial. // Returns the active write node, and asserts 'currentSerial' matches the stored serial.
vk::CommandBufferNode *getCurrentWriteNode(Serial currentSerial); vk::CommandBufferNode *getCurrentWriteOperation(Serial currentSerial);
// Allocates a new write node and calls setWriteNode internally. // Allocates a new write node and calls onWriteResource internally.
vk::CommandBufferNode *getNewWriteNode(RendererVk *renderer); vk::CommandBufferNode *getNewWriteNode(RendererVk *renderer);
// Allocates a write node via getNewWriteNode and returns a started command buffer. // Allocates a write node via getNewWriteNode and returns a started command buffer.
// The started command buffer will render outside of a RenderPass. // The started command buffer will render outside of a RenderPass.
vk::Error recordWriteCommands(RendererVk *renderer, vk::CommandBuffer **commandBufferOut); vk::Error beginWriteOperation(RendererVk *renderer, vk::CommandBuffer **commandBufferOut);
// Called on an operation that will modify this ResourceVk. // Called on an operation that will modify this ResourceVk.
void setWriteNode(vk::CommandBufferNode *newCommands, Serial serial); void onWriteResource(vk::CommandBufferNode *writeOperation, Serial serial);
// Sets up the dependency relations. |readNode| has the commands that read from this object. // Sets up dependency relations. 'readOperation' has the commands that read from this object.
void setReadNode(vk::CommandBufferNode *readNode, Serial serial); void onReadResource(vk::CommandBufferNode *readOperation, Serial serial);
private: private:
Serial mStoredQueueSerial; Serial mStoredQueueSerial;
std::vector<vk::CommandBufferNode *> mCurrentReadNodes; std::vector<vk::CommandBufferNode *> mCurrentReadOperations;
vk::CommandBufferNode *mCurrentWriteNode; vk::CommandBufferNode *mCurrentWriteOperation;
}; };
} // namespace rx } // namespace rx
......
...@@ -252,6 +252,19 @@ TEST_P(SimpleOperationTest, DrawQuad) ...@@ -252,6 +252,19 @@ TEST_P(SimpleOperationTest, DrawQuad)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
// Simple double quad test.
TEST_P(SimpleOperationTest, DrawQuadTwice)
{
ANGLE_GL_PROGRAM(program, kBasicVertexShader, kGreenFragmentShader);
drawQuad(program.get(), "position", 0.5f, 1.0f, true);
drawQuad(program.get(), "position", 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Simple line test. // Simple line test.
TEST_P(SimpleOperationTest, DrawLine) TEST_P(SimpleOperationTest, DrawLine)
{ {
......
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