1 //
2 // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // CommandGraph:
7 // Deferred work constructed by GL calls, that will later be flushed to Vulkan.
8 //
9
10 #include "libANGLE/renderer/vulkan/CommandGraph.h"
11
12 #include <iostream>
13
14 #include "libANGLE/Overlay.h"
15 #include "libANGLE/renderer/vulkan/ContextVk.h"
16 #include "libANGLE/renderer/vulkan/RenderTargetVk.h"
17 #include "libANGLE/renderer/vulkan/RendererVk.h"
18 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
19 #include "libANGLE/renderer/vulkan/vk_helpers.h"
20
21 #include "libANGLE/trace.h"
22
23 namespace rx
24 {
25 namespace vk
26 {
27 namespace
28 {
29 ANGLE_MAYBE_UNUSED
InitAndBeginCommandBuffer(ContextVk * context,const CommandPool & commandPool,const VkCommandBufferInheritanceInfo & inheritanceInfo,VkCommandBufferUsageFlags flags,angle::PoolAllocator * poolAllocator,priv::SecondaryCommandBuffer * commandBuffer)30 angle::Result InitAndBeginCommandBuffer(ContextVk *context,
31 const CommandPool &commandPool,
32 const VkCommandBufferInheritanceInfo &inheritanceInfo,
33 VkCommandBufferUsageFlags flags,
34 angle::PoolAllocator *poolAllocator,
35 priv::SecondaryCommandBuffer *commandBuffer)
36 {
37 ASSERT(!commandBuffer->valid());
38 commandBuffer->initialize(poolAllocator);
39 return angle::Result::Continue;
40 }
41
42 ANGLE_MAYBE_UNUSED
InitAndBeginCommandBuffer(vk::Context * context,const CommandPool & commandPool,const VkCommandBufferInheritanceInfo & inheritanceInfo,VkCommandBufferUsageFlags flags,angle::PoolAllocator * poolAllocator,priv::CommandBuffer * commandBuffer)43 angle::Result InitAndBeginCommandBuffer(vk::Context *context,
44 const CommandPool &commandPool,
45 const VkCommandBufferInheritanceInfo &inheritanceInfo,
46 VkCommandBufferUsageFlags flags,
47 angle::PoolAllocator *poolAllocator,
48 priv::CommandBuffer *commandBuffer)
49 {
50 ASSERT(!commandBuffer->valid());
51 ASSERT(commandPool.valid());
52 VkCommandBufferAllocateInfo createInfo = {};
53 createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
54 createInfo.commandPool = commandPool.getHandle();
55 createInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
56 createInfo.commandBufferCount = 1;
57
58 ANGLE_VK_TRY(context, commandBuffer->init(context->getDevice(), createInfo));
59
60 VkCommandBufferBeginInfo beginInfo = {};
61 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
62 beginInfo.flags = flags | VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
63 beginInfo.pInheritanceInfo = &inheritanceInfo;
64
65 ANGLE_VK_TRY(context, commandBuffer->begin(beginInfo));
66 return angle::Result::Continue;
67 }
68
GetResourceTypeName(CommandGraphResourceType resourceType,CommandGraphNodeFunction function)69 const char *GetResourceTypeName(CommandGraphResourceType resourceType,
70 CommandGraphNodeFunction function)
71 {
72 switch (resourceType)
73 {
74 case CommandGraphResourceType::Buffer:
75 return "Buffer";
76 case CommandGraphResourceType::Framebuffer:
77 return "Framebuffer";
78 case CommandGraphResourceType::Image:
79 return "Image";
80 case CommandGraphResourceType::Query:
81 switch (function)
82 {
83 case CommandGraphNodeFunction::BeginQuery:
84 return "BeginQuery";
85 case CommandGraphNodeFunction::EndQuery:
86 return "EndQuery";
87 case CommandGraphNodeFunction::WriteTimestamp:
88 return "WriteTimestamp";
89 default:
90 UNREACHABLE();
91 return "Query";
92 }
93 case CommandGraphResourceType::Dispatcher:
94 return "Dispatcher";
95 case CommandGraphResourceType::EmulatedQuery:
96 switch (function)
97 {
98 case CommandGraphNodeFunction::BeginTransformFeedbackQuery:
99 return "BeginTransformFeedbackQuery";
100 case CommandGraphNodeFunction::EndTransformFeedbackQuery:
101 return "EndTransformFeedbackQuery";
102 default:
103 UNREACHABLE();
104 return "EmulatedQuery";
105 }
106 case CommandGraphResourceType::FenceSync:
107 switch (function)
108 {
109 case CommandGraphNodeFunction::SetFenceSync:
110 return "SetFenceSync";
111 case CommandGraphNodeFunction::WaitFenceSync:
112 return "WaitFenceSync";
113 default:
114 UNREACHABLE();
115 return "FenceSync";
116 }
117 case CommandGraphResourceType::GraphBarrier:
118 return "GraphBarrier";
119 case CommandGraphResourceType::DebugMarker:
120 switch (function)
121 {
122 case CommandGraphNodeFunction::InsertDebugMarker:
123 return "InsertDebugMarker";
124 case CommandGraphNodeFunction::PushDebugMarker:
125 return "PushDebugMarker";
126 case CommandGraphNodeFunction::PopDebugMarker:
127 return "PopDebugMarker";
128 default:
129 UNREACHABLE();
130 return "DebugMarker";
131 }
132 case CommandGraphResourceType::HostAvailabilityOperation:
133 switch (function)
134 {
135 case CommandGraphNodeFunction::HostAvailabilityOperation:
136 return "HostAvailabilityOperation";
137 default:
138 UNREACHABLE();
139 return "HostAvailabilityOperation";
140 }
141 default:
142 UNREACHABLE();
143 return "";
144 }
145 }
146
GetLoadOpShorthand(uint32_t loadOp)147 const char *GetLoadOpShorthand(uint32_t loadOp)
148 {
149 switch (loadOp)
150 {
151 case VK_ATTACHMENT_LOAD_OP_CLEAR:
152 return "C";
153 case VK_ATTACHMENT_LOAD_OP_LOAD:
154 return "L";
155 default:
156 return "D";
157 }
158 }
159
GetStoreOpShorthand(uint32_t storeOp)160 const char *GetStoreOpShorthand(uint32_t storeOp)
161 {
162 switch (storeOp)
163 {
164 case VK_ATTACHMENT_STORE_OP_STORE:
165 return "S";
166 default:
167 return "D";
168 }
169 }
170
MakeDebugUtilsLabel(GLenum source,const char * marker,VkDebugUtilsLabelEXT * label)171 void MakeDebugUtilsLabel(GLenum source, const char *marker, VkDebugUtilsLabelEXT *label)
172 {
173 static constexpr angle::ColorF kLabelColors[6] = {
174 angle::ColorF(1.0f, 0.5f, 0.5f, 1.0f), // DEBUG_SOURCE_API
175 angle::ColorF(0.5f, 1.0f, 0.5f, 1.0f), // DEBUG_SOURCE_WINDOW_SYSTEM
176 angle::ColorF(0.5f, 0.5f, 1.0f, 1.0f), // DEBUG_SOURCE_SHADER_COMPILER
177 angle::ColorF(0.7f, 0.7f, 0.7f, 1.0f), // DEBUG_SOURCE_THIRD_PARTY
178 angle::ColorF(0.5f, 0.8f, 0.9f, 1.0f), // DEBUG_SOURCE_APPLICATION
179 angle::ColorF(0.9f, 0.8f, 0.5f, 1.0f), // DEBUG_SOURCE_OTHER
180 };
181
182 int colorIndex = source - GL_DEBUG_SOURCE_API;
183 ASSERT(colorIndex >= 0 && static_cast<size_t>(colorIndex) < ArraySize(kLabelColors));
184
185 label->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
186 label->pNext = nullptr;
187 label->pLabelName = marker;
188 kLabelColors[colorIndex].writeData(label->color);
189 }
190
191 constexpr VkSubpassContents kRenderPassContents =
192 CommandBuffer::ExecutesInline() ? VK_SUBPASS_CONTENTS_INLINE
193 : VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS;
194
195 // Helpers to unify executeCommands call based on underlying cmd buffer type
196 ANGLE_MAYBE_UNUSED
ExecuteCommands(PrimaryCommandBuffer * primCmdBuffer,priv::SecondaryCommandBuffer * secCmdBuffer)197 void ExecuteCommands(PrimaryCommandBuffer *primCmdBuffer,
198 priv::SecondaryCommandBuffer *secCmdBuffer)
199 {
200 secCmdBuffer->executeCommands(primCmdBuffer->getHandle());
201 }
202
203 ANGLE_MAYBE_UNUSED
ExecuteCommands(PrimaryCommandBuffer * primCmdBuffer,priv::CommandBuffer * secCmdBuffer)204 void ExecuteCommands(PrimaryCommandBuffer *primCmdBuffer, priv::CommandBuffer *secCmdBuffer)
205 {
206 primCmdBuffer->executeCommands(1, secCmdBuffer);
207 }
208
209 ANGLE_MAYBE_UNUSED
InsertBeginTransformFeedback(PrimaryCommandBuffer * primCmdBuffer,priv::SecondaryCommandBuffer & commandBuffer,uint32_t validBufferCount,const VkBuffer * counterBuffers,bool rebindBuffer)210 void InsertBeginTransformFeedback(PrimaryCommandBuffer *primCmdBuffer,
211 priv::SecondaryCommandBuffer &commandBuffer,
212 uint32_t validBufferCount,
213 const VkBuffer *counterBuffers,
214 bool rebindBuffer)
215 {
216 gl::TransformFeedbackBuffersArray<VkDeviceSize> offsets = {0, 0, 0, 0};
217 uint32_t counterBufferSize = (rebindBuffer) ? 0 : validBufferCount;
218
219 vkCmdBeginTransformFeedbackEXT(primCmdBuffer->getHandle(), 0, counterBufferSize, counterBuffers,
220 offsets.data());
221 }
222
223 ANGLE_MAYBE_UNUSED
InsertEndTransformFeedback(PrimaryCommandBuffer * primCmdBuffer,priv::SecondaryCommandBuffer & commandBuffer,uint32_t validBufferCount,const VkBuffer * counterBuffers)224 void InsertEndTransformFeedback(PrimaryCommandBuffer *primCmdBuffer,
225 priv::SecondaryCommandBuffer &commandBuffer,
226 uint32_t validBufferCount,
227 const VkBuffer *counterBuffers)
228 {
229 gl::TransformFeedbackBuffersArray<VkDeviceSize> offsets = {0, 0, 0, 0};
230
231 vkCmdEndTransformFeedbackEXT(primCmdBuffer->getHandle(), 0, validBufferCount, counterBuffers,
232 offsets.data());
233 }
234
235 ANGLE_MAYBE_UNUSED
InsertCounterBufferPipelineBarrier(PrimaryCommandBuffer * primCmdBuffer,priv::SecondaryCommandBuffer & commandBuffer,const VkBuffer * counterBuffers)236 void InsertCounterBufferPipelineBarrier(PrimaryCommandBuffer *primCmdBuffer,
237 priv::SecondaryCommandBuffer &commandBuffer,
238 const VkBuffer *counterBuffers)
239 {
240 VkBufferMemoryBarrier bufferBarrier = {};
241 bufferBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
242 bufferBarrier.pNext = nullptr;
243 bufferBarrier.srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
244 bufferBarrier.dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;
245 bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
246 bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
247 bufferBarrier.buffer = counterBuffers[0];
248 bufferBarrier.offset = 0;
249 bufferBarrier.size = VK_WHOLE_SIZE;
250
251 vkCmdPipelineBarrier(primCmdBuffer->getHandle(), VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
252 VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0u, 0u, nullptr, 1u, &bufferBarrier,
253 0u, nullptr);
254 }
255
256 ANGLE_MAYBE_UNUSED
DumpCommands(const priv::SecondaryCommandBuffer & commandBuffer,const char * separator)257 std::string DumpCommands(const priv::SecondaryCommandBuffer &commandBuffer, const char *separator)
258 {
259 return commandBuffer.dumpCommands(separator);
260 }
261
262 ANGLE_MAYBE_UNUSED
DumpCommands(const priv::CommandBuffer & commandBuffer,const char * separator)263 std::string DumpCommands(const priv::CommandBuffer &commandBuffer, const char *separator)
264 {
265 return "--blob--";
266 }
267
CalculateSecondaryCommandBufferPoolWaste(const std::vector<CommandGraphNode * > nodes)268 float CalculateSecondaryCommandBufferPoolWaste(const std::vector<CommandGraphNode *> nodes)
269 {
270 size_t used = 0;
271 size_t allocated = 0;
272
273 for (const CommandGraphNode *node : nodes)
274 {
275 size_t nodeUsed;
276 size_t nodeAllocated;
277 node->getMemoryUsageStatsForDiagnostics(&nodeUsed, &nodeAllocated);
278 used += nodeUsed;
279 allocated += nodeAllocated;
280 }
281
282 allocated = std::max<size_t>(allocated, 1);
283 return static_cast<float>(used) / static_cast<float>(allocated);
284 }
285
286 } // anonymous namespace
287
288 // CommandGraphResource implementation.
CommandGraphResource(CommandGraphResourceType resourceType)289 CommandGraphResource::CommandGraphResource(CommandGraphResourceType resourceType)
290 : mCurrentWritingNode(nullptr), mResourceType(resourceType)
291 {
292 mUse.init();
293 }
294
~CommandGraphResource()295 CommandGraphResource::~CommandGraphResource()
296 {
297 mUse.release();
298 }
299
finishRunningCommands(ContextVk * contextVk)300 angle::Result CommandGraphResource::finishRunningCommands(ContextVk *contextVk)
301 {
302 return contextVk->finishToSerial(mUse.getSerial());
303 }
304
recordCommands(ContextVk * contextVk,CommandBuffer ** commandBufferOut)305 angle::Result CommandGraphResource::recordCommands(ContextVk *contextVk,
306 CommandBuffer **commandBufferOut)
307 {
308 ASSERT(contextVk->commandGraphEnabled());
309
310 updateCurrentAccessNodes();
311
312 if (!hasChildlessWritingNode() || hasStartedRenderPass())
313 {
314 startNewCommands(contextVk);
315 return mCurrentWritingNode->beginOutsideRenderPassRecording(
316 contextVk, contextVk->getCommandPool(), commandBufferOut);
317 }
318
319 CommandBuffer *outsideRenderPassCommands = mCurrentWritingNode->getOutsideRenderPassCommands();
320 if (!outsideRenderPassCommands->valid())
321 {
322 ANGLE_TRY(mCurrentWritingNode->beginOutsideRenderPassRecording(
323 contextVk, contextVk->getCommandPool(), commandBufferOut));
324 }
325 else
326 {
327 *commandBufferOut = outsideRenderPassCommands;
328 }
329
330 // Store reference to usage in graph.
331 contextVk->getResourceUseList().add(mUse);
332
333 return angle::Result::Continue;
334 }
335
beginRenderPass(ContextVk * contextVk,const Framebuffer & framebuffer,const gl::Rectangle & renderArea,const RenderPassDesc & renderPassDesc,const AttachmentOpsArray & renderPassAttachmentOps,const std::vector<VkClearValue> & clearValues,CommandBuffer ** commandBufferOut)336 angle::Result CommandGraphResource::beginRenderPass(
337 ContextVk *contextVk,
338 const Framebuffer &framebuffer,
339 const gl::Rectangle &renderArea,
340 const RenderPassDesc &renderPassDesc,
341 const AttachmentOpsArray &renderPassAttachmentOps,
342 const std::vector<VkClearValue> &clearValues,
343 CommandBuffer **commandBufferOut)
344 {
345 // If a barrier has been inserted in the meantime, stop the command buffer.
346 if (!hasChildlessWritingNode())
347 {
348 startNewCommands(contextVk);
349 }
350
351 mCurrentWritingNode->storeRenderPassInfo(framebuffer, renderArea, renderPassDesc,
352 renderPassAttachmentOps, clearValues);
353
354 mCurrentWritingNode->setRenderPassOwner(contextVk);
355
356 return mCurrentWritingNode->beginInsideRenderPassRecording(contextVk, commandBufferOut);
357 }
358
addWriteDependency(ContextVk * contextVk,CommandGraphResource * writingResource)359 void CommandGraphResource::addWriteDependency(ContextVk *contextVk,
360 CommandGraphResource *writingResource)
361 {
362 ASSERT(contextVk->commandGraphEnabled());
363
364 CommandGraphNode *writingNode = writingResource->mCurrentWritingNode;
365 ASSERT(writingNode);
366
367 onWriteImpl(contextVk, writingNode);
368 }
369
addReadDependency(ContextVk * contextVk,CommandGraphResource * readingResource)370 void CommandGraphResource::addReadDependency(ContextVk *contextVk,
371 CommandGraphResource *readingResource)
372 {
373 ASSERT(contextVk->commandGraphEnabled());
374
375 onResourceAccess(&contextVk->getResourceUseList());
376
377 CommandGraphNode *readingNode = readingResource->mCurrentWritingNode;
378 ASSERT(readingNode);
379
380 if (mCurrentWritingNode)
381 {
382 // Ensure 'readingNode' happens after the current writing node.
383 CommandGraphNode::SetHappensBeforeDependency(mCurrentWritingNode, readingNode);
384 }
385
386 // Add the read node to the list of nodes currently reading this resource.
387 mCurrentReadingNodes.push_back(readingNode);
388 }
389
finishCurrentCommands(ContextVk * contextVk)390 void CommandGraphResource::finishCurrentCommands(ContextVk *contextVk)
391 {
392 ASSERT(contextVk->commandGraphEnabled());
393 startNewCommands(contextVk);
394 }
395
startNewCommands(ContextVk * contextVk)396 void CommandGraphResource::startNewCommands(ContextVk *contextVk)
397 {
398 ASSERT(contextVk->commandGraphEnabled());
399 CommandGraphNode *newCommands =
400 contextVk->getCommandGraph()->allocateNode(CommandGraphNodeFunction::Generic);
401 newCommands->setDiagnosticInfo(mResourceType, reinterpret_cast<uintptr_t>(this));
402 onWriteImpl(contextVk, newCommands);
403 }
404
onWriteImpl(ContextVk * contextVk,CommandGraphNode * writingNode)405 void CommandGraphResource::onWriteImpl(ContextVk *contextVk, CommandGraphNode *writingNode)
406 {
407 onResourceAccess(&contextVk->getResourceUseList());
408
409 // Make sure any open reads and writes finish before we execute 'writingNode'.
410 if (!mCurrentReadingNodes.empty())
411 {
412 CommandGraphNode::SetHappensBeforeDependencies(mCurrentReadingNodes.data(),
413 mCurrentReadingNodes.size(), writingNode);
414 mCurrentReadingNodes.clear();
415 }
416
417 if (mCurrentWritingNode && mCurrentWritingNode != writingNode)
418 {
419 CommandGraphNode::SetHappensBeforeDependency(mCurrentWritingNode, writingNode);
420 }
421
422 mCurrentWritingNode = writingNode;
423 }
424
425 // CommandGraphNode implementation.
CommandGraphNode(CommandGraphNodeFunction function,angle::PoolAllocator * poolAllocator)426 CommandGraphNode::CommandGraphNode(CommandGraphNodeFunction function,
427 angle::PoolAllocator *poolAllocator)
428 : mRenderPassClearValues{},
429 mFunction(function),
430 mPoolAllocator(poolAllocator),
431 mQueryPool(VK_NULL_HANDLE),
432 mQueryIndex(0),
433 mFenceSyncEvent(VK_NULL_HANDLE),
434 mHasChildren(false),
435 mVisitedState(VisitedState::Unvisited),
436 mGlobalMemoryBarrierSrcAccess(0),
437 mGlobalMemoryBarrierDstAccess(0),
438 mGlobalMemoryBarrierStages(0),
439 mRenderPassOwner(nullptr),
440 mValidTransformFeedbackBufferCount(0)
441 {}
442
~CommandGraphNode()443 CommandGraphNode::~CommandGraphNode()
444 {
445 mRenderPassFramebuffer.setHandle(VK_NULL_HANDLE);
446 // Command buffers are managed by the command pool, so don't need to be freed.
447 mOutsideRenderPassCommands.releaseHandle();
448 mInsideRenderPassCommands.releaseHandle();
449 }
450
beginOutsideRenderPassRecording(ContextVk * context,const CommandPool & commandPool,CommandBuffer ** commandsOut)451 angle::Result CommandGraphNode::beginOutsideRenderPassRecording(ContextVk *context,
452 const CommandPool &commandPool,
453 CommandBuffer **commandsOut)
454 {
455 ASSERT(!mHasChildren);
456
457 VkCommandBufferInheritanceInfo inheritanceInfo = {};
458 inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
459 inheritanceInfo.renderPass = VK_NULL_HANDLE;
460 inheritanceInfo.subpass = 0;
461 inheritanceInfo.framebuffer = VK_NULL_HANDLE;
462 inheritanceInfo.occlusionQueryEnable =
463 CommandBuffer::SupportsQueries(context->getRenderer()->getPhysicalDeviceFeatures());
464 inheritanceInfo.queryFlags = 0;
465 inheritanceInfo.pipelineStatistics = 0;
466
467 ANGLE_TRY(InitAndBeginCommandBuffer(context, commandPool, inheritanceInfo, 0, mPoolAllocator,
468 &mOutsideRenderPassCommands));
469
470 *commandsOut = &mOutsideRenderPassCommands;
471 return angle::Result::Continue;
472 }
473
beginInsideRenderPassRecording(ContextVk * context,CommandBuffer ** commandsOut)474 angle::Result CommandGraphNode::beginInsideRenderPassRecording(ContextVk *context,
475 CommandBuffer **commandsOut)
476 {
477 ASSERT(!mHasChildren);
478
479 // Get a compatible RenderPass from the cache so we can initialize the inheritance info.
480 // TODO(jmadill): Support query for compatible/conformant render pass. http://anglebug.com/2361
481 RenderPass *compatibleRenderPass;
482 ANGLE_TRY(context->getCompatibleRenderPass(mRenderPassDesc, &compatibleRenderPass));
483
484 VkCommandBufferInheritanceInfo inheritanceInfo = {};
485 inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
486 inheritanceInfo.renderPass = compatibleRenderPass->getHandle();
487 inheritanceInfo.subpass = 0;
488 inheritanceInfo.framebuffer = mRenderPassFramebuffer.getHandle();
489 inheritanceInfo.occlusionQueryEnable =
490 CommandBuffer::SupportsQueries(context->getRenderer()->getPhysicalDeviceFeatures());
491 inheritanceInfo.queryFlags = 0;
492 inheritanceInfo.pipelineStatistics = 0;
493
494 ANGLE_TRY(InitAndBeginCommandBuffer(context, context->getCommandPool(), inheritanceInfo,
495 VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT,
496 mPoolAllocator, &mInsideRenderPassCommands));
497
498 *commandsOut = &mInsideRenderPassCommands;
499 return angle::Result::Continue;
500 }
501
storeRenderPassInfo(const Framebuffer & framebuffer,const gl::Rectangle renderArea,const vk::RenderPassDesc & renderPassDesc,const AttachmentOpsArray & renderPassAttachmentOps,const std::vector<VkClearValue> & clearValues)502 void CommandGraphNode::storeRenderPassInfo(const Framebuffer &framebuffer,
503 const gl::Rectangle renderArea,
504 const vk::RenderPassDesc &renderPassDesc,
505 const AttachmentOpsArray &renderPassAttachmentOps,
506 const std::vector<VkClearValue> &clearValues)
507 {
508 mRenderPassDesc = renderPassDesc;
509 mRenderPassAttachmentOps = renderPassAttachmentOps;
510 mRenderPassFramebuffer.setHandle(framebuffer.getHandle());
511 mRenderPassRenderArea = renderArea;
512 std::copy(clearValues.begin(), clearValues.end(), mRenderPassClearValues.begin());
513 }
514
515 // static
SetHappensBeforeDependencies(CommandGraphNode ** beforeNodes,size_t beforeNodesCount,CommandGraphNode * afterNode)516 void CommandGraphNode::SetHappensBeforeDependencies(CommandGraphNode **beforeNodes,
517 size_t beforeNodesCount,
518 CommandGraphNode *afterNode)
519 {
520 afterNode->mParents.insert(afterNode->mParents.end(), beforeNodes,
521 beforeNodes + beforeNodesCount);
522
523 // TODO(jmadill): is there a faster way to do this?
524 for (size_t i = 0; i < beforeNodesCount; ++i)
525 {
526 beforeNodes[i]->setHasChildren();
527
528 ASSERT(beforeNodes[i] != afterNode && !beforeNodes[i]->isChildOf(afterNode));
529 }
530 }
531
SetHappensBeforeDependencies(CommandGraphNode * beforeNode,CommandGraphNode ** afterNodes,size_t afterNodesCount)532 void CommandGraphNode::SetHappensBeforeDependencies(CommandGraphNode *beforeNode,
533 CommandGraphNode **afterNodes,
534 size_t afterNodesCount)
535 {
536 for (size_t i = 0; i < afterNodesCount; ++i)
537 {
538 SetHappensBeforeDependency(beforeNode, afterNodes[i]);
539 }
540 }
541
hasParents() const542 bool CommandGraphNode::hasParents() const
543 {
544 return !mParents.empty();
545 }
546
setQueryPool(const QueryPool * queryPool,uint32_t queryIndex)547 void CommandGraphNode::setQueryPool(const QueryPool *queryPool, uint32_t queryIndex)
548 {
549 ASSERT(mFunction == CommandGraphNodeFunction::BeginQuery ||
550 mFunction == CommandGraphNodeFunction::EndQuery ||
551 mFunction == CommandGraphNodeFunction::WriteTimestamp ||
552 mFunction == CommandGraphNodeFunction::BeginTransformFeedbackQuery ||
553 mFunction == CommandGraphNodeFunction::EndTransformFeedbackQuery);
554 mQueryPool = queryPool->getHandle();
555 mQueryIndex = queryIndex;
556 }
557
setFenceSync(const vk::Event & event)558 void CommandGraphNode::setFenceSync(const vk::Event &event)
559 {
560 ASSERT(mFunction == CommandGraphNodeFunction::SetFenceSync ||
561 mFunction == CommandGraphNodeFunction::WaitFenceSync);
562 mFenceSyncEvent = event.getHandle();
563 }
564
setDebugMarker(GLenum source,std::string && marker)565 void CommandGraphNode::setDebugMarker(GLenum source, std::string &&marker)
566 {
567 ASSERT(mFunction == CommandGraphNodeFunction::InsertDebugMarker ||
568 mFunction == CommandGraphNodeFunction::PushDebugMarker);
569 mDebugMarkerSource = source;
570 mDebugMarker = std::move(marker);
571 }
572
573 // Do not call this in anything but testing code, since it's slow.
isChildOf(CommandGraphNode * parent)574 bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
575 {
576 std::set<CommandGraphNode *> visitedList;
577 std::vector<CommandGraphNode *> openList;
578 openList.insert(openList.begin(), mParents.begin(), mParents.end());
579 while (!openList.empty())
580 {
581 CommandGraphNode *current = openList.back();
582 openList.pop_back();
583 if (visitedList.count(current) == 0)
584 {
585 if (current == parent)
586 {
587 return true;
588 }
589 visitedList.insert(current);
590 openList.insert(openList.end(), current->mParents.begin(), current->mParents.end());
591 }
592 }
593
594 return false;
595 }
596
visitedState() const597 VisitedState CommandGraphNode::visitedState() const
598 {
599 return mVisitedState;
600 }
601
visitParents(std::vector<CommandGraphNode * > * stack)602 void CommandGraphNode::visitParents(std::vector<CommandGraphNode *> *stack)
603 {
604 ASSERT(mVisitedState == VisitedState::Unvisited);
605 stack->insert(stack->end(), mParents.begin(), mParents.end());
606 mVisitedState = VisitedState::Ready;
607 }
608
visitAndExecute(vk::Context * context,Serial serial,RenderPassCache * renderPassCache,PrimaryCommandBuffer * primaryCommandBuffer)609 angle::Result CommandGraphNode::visitAndExecute(vk::Context *context,
610 Serial serial,
611 RenderPassCache *renderPassCache,
612 PrimaryCommandBuffer *primaryCommandBuffer)
613 {
614 // Record the deferred pipeline barrier if necessary.
615 ASSERT((mGlobalMemoryBarrierDstAccess == 0) == (mGlobalMemoryBarrierSrcAccess == 0));
616 if (mGlobalMemoryBarrierSrcAccess)
617 {
618 VkMemoryBarrier memoryBarrier = {};
619 memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
620 memoryBarrier.srcAccessMask = mGlobalMemoryBarrierSrcAccess;
621 memoryBarrier.dstAccessMask = mGlobalMemoryBarrierDstAccess;
622
623 primaryCommandBuffer->memoryBarrier(mGlobalMemoryBarrierStages, mGlobalMemoryBarrierStages,
624 &memoryBarrier);
625 }
626
627 switch (mFunction)
628 {
629 case CommandGraphNodeFunction::Generic:
630 ASSERT(mQueryPool == VK_NULL_HANDLE && mFenceSyncEvent == VK_NULL_HANDLE);
631
632 if (mOutsideRenderPassCommands.valid())
633 {
634 ANGLE_VK_TRY(context, mOutsideRenderPassCommands.end());
635 ExecuteCommands(primaryCommandBuffer, &mOutsideRenderPassCommands);
636 }
637
638 if (mInsideRenderPassCommands.valid())
639 {
640 // Pull a RenderPass from the cache.
641 // TODO(jmadill): Insert layout transitions.
642 RenderPass *renderPass = nullptr;
643 ANGLE_TRY(renderPassCache->getRenderPassWithOps(
644 context, serial, mRenderPassDesc, mRenderPassAttachmentOps, &renderPass));
645
646 ANGLE_VK_TRY(context, mInsideRenderPassCommands.end());
647
648 VkRenderPassBeginInfo beginInfo = {};
649 beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
650 beginInfo.renderPass = renderPass->getHandle();
651 beginInfo.framebuffer = mRenderPassFramebuffer.getHandle();
652 beginInfo.renderArea.offset.x = static_cast<uint32_t>(mRenderPassRenderArea.x);
653 beginInfo.renderArea.offset.y = static_cast<uint32_t>(mRenderPassRenderArea.y);
654 beginInfo.renderArea.extent.width =
655 static_cast<uint32_t>(mRenderPassRenderArea.width);
656 beginInfo.renderArea.extent.height =
657 static_cast<uint32_t>(mRenderPassRenderArea.height);
658 beginInfo.clearValueCount =
659 static_cast<uint32_t>(mRenderPassDesc.attachmentCount());
660 beginInfo.pClearValues = mRenderPassClearValues.data();
661
662 primaryCommandBuffer->beginRenderPass(beginInfo, kRenderPassContents);
663 if (mValidTransformFeedbackBufferCount == 0)
664 {
665 ExecuteCommands(primaryCommandBuffer, &mInsideRenderPassCommands);
666 primaryCommandBuffer->endRenderPass();
667 }
668 else
669 {
670 InsertBeginTransformFeedback(primaryCommandBuffer, mInsideRenderPassCommands,
671 mValidTransformFeedbackBufferCount,
672 mTransformFeedbackCounterBuffers.data(),
673 mRebindTransformFeedbackBuffers);
674 ExecuteCommands(primaryCommandBuffer, &mInsideRenderPassCommands);
675 InsertEndTransformFeedback(primaryCommandBuffer, mInsideRenderPassCommands,
676 mValidTransformFeedbackBufferCount,
677 mTransformFeedbackCounterBuffers.data());
678 primaryCommandBuffer->endRenderPass();
679 InsertCounterBufferPipelineBarrier(primaryCommandBuffer,
680 mInsideRenderPassCommands,
681 mTransformFeedbackCounterBuffers.data());
682 }
683 }
684 break;
685
686 case CommandGraphNodeFunction::BeginQuery:
687 ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
688 ASSERT(mQueryPool != VK_NULL_HANDLE);
689
690 primaryCommandBuffer->resetQueryPool(mQueryPool, mQueryIndex, 1);
691 primaryCommandBuffer->beginQuery(mQueryPool, mQueryIndex, 0);
692
693 break;
694
695 case CommandGraphNodeFunction::EndQuery:
696 ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
697 ASSERT(mQueryPool != VK_NULL_HANDLE);
698
699 primaryCommandBuffer->endQuery(mQueryPool, mQueryIndex);
700
701 break;
702
703 case CommandGraphNodeFunction::WriteTimestamp:
704 ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
705 ASSERT(mQueryPool != VK_NULL_HANDLE);
706
707 primaryCommandBuffer->resetQueryPool(mQueryPool, mQueryIndex, 1);
708 primaryCommandBuffer->writeTimestamp(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mQueryPool,
709 mQueryIndex);
710
711 break;
712
713 case CommandGraphNodeFunction::BeginTransformFeedbackQuery:
714 // Unless using VK_EXT_transform_feedback (not implemented currently), there's nothing
715 // to do.
716 break;
717
718 case CommandGraphNodeFunction::EndTransformFeedbackQuery:
719 // Same as BeginTransformFeedbackQuery.
720 break;
721
722 case CommandGraphNodeFunction::SetFenceSync:
723 ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
724 ASSERT(mFenceSyncEvent != VK_NULL_HANDLE);
725
726 primaryCommandBuffer->setEvent(mFenceSyncEvent, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
727
728 break;
729
730 case CommandGraphNodeFunction::WaitFenceSync:
731 ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
732 ASSERT(mFenceSyncEvent != VK_NULL_HANDLE);
733
734 // Fence Syncs are purely execution barriers, so there are no memory barriers attached.
735 primaryCommandBuffer->waitEvents(
736 1, &mFenceSyncEvent, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
737 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, nullptr, 0, nullptr, 0, nullptr);
738
739 break;
740
741 case CommandGraphNodeFunction::GraphBarrier:
742 // Nothing to do. The memory barrier, if any, is already handled above through global
743 // memory barrier flags.
744 break;
745
746 case CommandGraphNodeFunction::InsertDebugMarker:
747 ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
748
749 if (vkCmdInsertDebugUtilsLabelEXT)
750 {
751 VkDebugUtilsLabelEXT label;
752 MakeDebugUtilsLabel(mDebugMarkerSource, mDebugMarker.c_str(), &label);
753
754 vkCmdInsertDebugUtilsLabelEXT(primaryCommandBuffer->getHandle(), &label);
755 }
756 break;
757
758 case CommandGraphNodeFunction::PushDebugMarker:
759 ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
760
761 if (vkCmdBeginDebugUtilsLabelEXT)
762 {
763 VkDebugUtilsLabelEXT label;
764 MakeDebugUtilsLabel(mDebugMarkerSource, mDebugMarker.c_str(), &label);
765
766 vkCmdBeginDebugUtilsLabelEXT(primaryCommandBuffer->getHandle(), &label);
767 }
768 break;
769
770 case CommandGraphNodeFunction::PopDebugMarker:
771 ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
772
773 if (vkCmdEndDebugUtilsLabelEXT)
774 {
775 vkCmdEndDebugUtilsLabelEXT(primaryCommandBuffer->getHandle());
776 }
777 break;
778
779 case CommandGraphNodeFunction::HostAvailabilityOperation:
780 // Make sure all writes to host-visible buffers are flushed. We have no way of knowing
781 // whether any buffer will be mapped for readback in the future, and we can't afford to
782 // flush and wait on a one-pipeline-barrier command buffer on every map().
783 {
784 VkMemoryBarrier memoryBarrier = {};
785 memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
786 memoryBarrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
787 memoryBarrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
788
789 primaryCommandBuffer->memoryBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
790 VK_PIPELINE_STAGE_HOST_BIT, &memoryBarrier);
791 }
792 break;
793
794 default:
795 UNREACHABLE();
796 }
797
798 mVisitedState = VisitedState::Visited;
799 return angle::Result::Continue;
800 }
801
getParentsForDiagnostics() const802 const std::vector<CommandGraphNode *> &CommandGraphNode::getParentsForDiagnostics() const
803 {
804 return mParents;
805 }
806
setDiagnosticInfo(CommandGraphResourceType resourceType,uintptr_t resourceID)807 void CommandGraphNode::setDiagnosticInfo(CommandGraphResourceType resourceType,
808 uintptr_t resourceID)
809 {
810 mResourceType = resourceType;
811 mResourceID = resourceID;
812 }
813
hasDiagnosticID() const814 bool CommandGraphNode::hasDiagnosticID() const
815 {
816 // All nodes have diagnostic IDs to differentiate them except the following select few.
817 return mResourceType != CommandGraphResourceType::HostAvailabilityOperation &&
818 mResourceType != CommandGraphResourceType::GraphBarrier;
819 }
820
dumpCommandsForDiagnostics(const char * separator) const821 std::string CommandGraphNode::dumpCommandsForDiagnostics(const char *separator) const
822 {
823 std::string result;
824 if (mGlobalMemoryBarrierSrcAccess != 0 || mGlobalMemoryBarrierDstAccess != 0)
825 {
826 result += separator;
827
828 std::ostringstream out;
829 out << "Memory Barrier Src: 0x" << std::hex << mGlobalMemoryBarrierSrcAccess
830 << " → Dst: 0x" << std::hex << mGlobalMemoryBarrierDstAccess;
831 result += out.str();
832 }
833 if (mOutsideRenderPassCommands.valid())
834 {
835 result += separator;
836 result += "Outside RP:";
837 result += DumpCommands(mOutsideRenderPassCommands, separator);
838 }
839 if (mInsideRenderPassCommands.valid())
840 {
841 result += separator;
842 result += "Inside RP:";
843
844 size_t attachmentCount = mRenderPassDesc.attachmentCount();
845 size_t depthStencilAttachmentCount = mRenderPassDesc.hasDepthStencilAttachment();
846 size_t colorAttachmentCount = attachmentCount - depthStencilAttachmentCount;
847
848 std::string loadOps, storeOps;
849
850 if (colorAttachmentCount > 0)
851 {
852 loadOps += " Color: ";
853 storeOps += " Color: ";
854
855 for (size_t i = 0; i < colorAttachmentCount; ++i)
856 {
857 loadOps += GetLoadOpShorthand(mRenderPassAttachmentOps[i].loadOp);
858 storeOps += GetStoreOpShorthand(mRenderPassAttachmentOps[i].storeOp);
859 }
860 }
861
862 if (depthStencilAttachmentCount > 0)
863 {
864 ASSERT(depthStencilAttachmentCount == 1);
865
866 loadOps += " Depth/Stencil: ";
867 storeOps += " Depth/Stencil: ";
868 size_t dsIndex = colorAttachmentCount;
869
870 loadOps += GetLoadOpShorthand(mRenderPassAttachmentOps[dsIndex].loadOp);
871 loadOps += GetLoadOpShorthand(mRenderPassAttachmentOps[dsIndex].stencilLoadOp);
872
873 storeOps += GetStoreOpShorthand(mRenderPassAttachmentOps[dsIndex].storeOp);
874 storeOps += GetStoreOpShorthand(mRenderPassAttachmentOps[dsIndex].stencilStoreOp);
875 }
876
877 if (attachmentCount > 0)
878 {
879 result += " LoadOp: " + loadOps;
880 result += separator;
881 result += "------------ StoreOp: " + storeOps;
882 }
883
884 result += DumpCommands(mInsideRenderPassCommands, separator);
885 }
886 return result;
887 }
888
getMemoryUsageStatsForDiagnostics(size_t * usedMemoryOut,size_t * allocatedMemoryOut) const889 void CommandGraphNode::getMemoryUsageStatsForDiagnostics(size_t *usedMemoryOut,
890 size_t *allocatedMemoryOut) const
891 {
892 size_t commandBufferUsed;
893 size_t commandBufferAllocated;
894
895 mOutsideRenderPassCommands.getMemoryUsageStats(usedMemoryOut, allocatedMemoryOut);
896 mInsideRenderPassCommands.getMemoryUsageStats(&commandBufferUsed, &commandBufferAllocated);
897
898 *usedMemoryOut += commandBufferUsed;
899 *allocatedMemoryOut += commandBufferAllocated;
900 }
901
902 // SharedGarbage implementation.
903 SharedGarbage::SharedGarbage() = default;
904
SharedGarbage(SharedGarbage && other)905 SharedGarbage::SharedGarbage(SharedGarbage &&other)
906 {
907 *this = std::move(other);
908 }
909
SharedGarbage(SharedResourceUse && use,std::vector<GarbageObject> && garbage)910 SharedGarbage::SharedGarbage(SharedResourceUse &&use, std::vector<GarbageObject> &&garbage)
911 : mLifetime(std::move(use)), mGarbage(std::move(garbage))
912 {}
913
914 SharedGarbage::~SharedGarbage() = default;
915
operator =(SharedGarbage && rhs)916 SharedGarbage &SharedGarbage::operator=(SharedGarbage &&rhs)
917 {
918 std::swap(mLifetime, rhs.mLifetime);
919 std::swap(mGarbage, rhs.mGarbage);
920 return *this;
921 }
922
destroyIfComplete(VkDevice device,Serial completedSerial)923 bool SharedGarbage::destroyIfComplete(VkDevice device, Serial completedSerial)
924 {
925 if (mLifetime.isCurrentlyInUse(completedSerial))
926 return false;
927
928 mLifetime.release();
929
930 for (GarbageObject &object : mGarbage)
931 {
932 object.destroy(device);
933 }
934
935 return true;
936 }
937
938 // CommandGraph implementation.
CommandGraph(bool enableGraphDiagnostics,angle::PoolAllocator * poolAllocator)939 CommandGraph::CommandGraph(bool enableGraphDiagnostics, angle::PoolAllocator *poolAllocator)
940 : mEnableGraphDiagnostics(enableGraphDiagnostics),
941 mPoolAllocator(poolAllocator),
942 mLastBarrierIndex(kInvalidNodeIndex)
943 {
944 // Push so that allocations made from here will be recycled in clear() below.
945 mPoolAllocator->push();
946 }
947
~CommandGraph()948 CommandGraph::~CommandGraph()
949 {
950 ASSERT(empty());
951 }
952
allocateNode(CommandGraphNodeFunction function)953 CommandGraphNode *CommandGraph::allocateNode(CommandGraphNodeFunction function)
954 {
955 // TODO(jmadill): Use a pool allocator for the CPU node allocations.
956 CommandGraphNode *newCommands = new CommandGraphNode(function, mPoolAllocator);
957 mNodes.emplace_back(newCommands);
958 return newCommands;
959 }
960
allocateBarrierNode(CommandGraphNodeFunction function,CommandGraphResourceType resourceType,uintptr_t resourceID)961 CommandGraphNode *CommandGraph::allocateBarrierNode(CommandGraphNodeFunction function,
962 CommandGraphResourceType resourceType,
963 uintptr_t resourceID)
964 {
965 CommandGraphNode *newNode = allocateNode(function);
966 newNode->setDiagnosticInfo(resourceType, resourceID);
967 setNewBarrier(newNode);
968
969 return newNode;
970 }
971
setNewBarrier(CommandGraphNode * newBarrier)972 void CommandGraph::setNewBarrier(CommandGraphNode *newBarrier)
973 {
974 size_t previousBarrierIndex = 0;
975 CommandGraphNode *previousBarrier = getLastBarrierNode(&previousBarrierIndex);
976
977 // Add a dependency from previousBarrier to all nodes in (previousBarrier, newBarrier).
978 if (previousBarrier && previousBarrierIndex + 1 < mNodes.size())
979 {
980 size_t afterNodesCount = mNodes.size() - (previousBarrierIndex + 2);
981 CommandGraphNode::SetHappensBeforeDependencies(
982 previousBarrier, &mNodes[previousBarrierIndex + 1], afterNodesCount);
983 }
984
985 // Add a dependency from all nodes in [previousBarrier, newBarrier) to newBarrier.
986 addDependenciesToNextBarrier(previousBarrierIndex, mNodes.size() - 1, newBarrier);
987
988 mLastBarrierIndex = mNodes.size() - 1;
989 }
990
submitCommands(ContextVk * context,Serial serial,RenderPassCache * renderPassCache,PrimaryCommandBuffer * primaryCommandBuffer)991 angle::Result CommandGraph::submitCommands(ContextVk *context,
992 Serial serial,
993 RenderPassCache *renderPassCache,
994 PrimaryCommandBuffer *primaryCommandBuffer)
995 {
996 // There is no point in submitting an empty command buffer, so make sure not to call this
997 // function if there's nothing to do.
998 ASSERT(!mNodes.empty());
999
1000 updateOverlay(context);
1001
1002 size_t previousBarrierIndex = 0;
1003 CommandGraphNode *previousBarrier = getLastBarrierNode(&previousBarrierIndex);
1004
1005 // Add a dependency from previousBarrier to all nodes in (previousBarrier, end].
1006 if (previousBarrier && previousBarrierIndex + 1 < mNodes.size())
1007 {
1008 size_t afterNodesCount = mNodes.size() - (previousBarrierIndex + 1);
1009 CommandGraphNode::SetHappensBeforeDependencies(
1010 previousBarrier, &mNodes[previousBarrierIndex + 1], afterNodesCount);
1011 }
1012
1013 if (mEnableGraphDiagnostics)
1014 {
1015 dumpGraphDotFile(std::cout);
1016 }
1017
1018 std::vector<CommandGraphNode *> nodeStack;
1019
1020 VkCommandBufferBeginInfo beginInfo = {};
1021 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1022 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1023 beginInfo.pInheritanceInfo = nullptr;
1024
1025 ANGLE_VK_TRY(context, primaryCommandBuffer->begin(beginInfo));
1026
1027 ANGLE_TRY(context->traceGpuEvent(primaryCommandBuffer, TRACE_EVENT_PHASE_BEGIN,
1028 "Primary Command Buffer"));
1029
1030 for (CommandGraphNode *topLevelNode : mNodes)
1031 {
1032 // Only process commands that don't have child commands. The others will be pulled in
1033 // automatically. Also skip commands that have already been visited.
1034 if (topLevelNode->hasChildren() || topLevelNode->visitedState() != VisitedState::Unvisited)
1035 continue;
1036
1037 nodeStack.push_back(topLevelNode);
1038
1039 while (!nodeStack.empty())
1040 {
1041 CommandGraphNode *node = nodeStack.back();
1042
1043 switch (node->visitedState())
1044 {
1045 case VisitedState::Unvisited:
1046 node->visitParents(&nodeStack);
1047 break;
1048 case VisitedState::Ready:
1049 ANGLE_TRY(node->visitAndExecute(context, serial, renderPassCache,
1050 primaryCommandBuffer));
1051 nodeStack.pop_back();
1052 break;
1053 case VisitedState::Visited:
1054 nodeStack.pop_back();
1055 break;
1056 default:
1057 UNREACHABLE();
1058 break;
1059 }
1060 }
1061 }
1062
1063 ANGLE_TRY(context->traceGpuEvent(primaryCommandBuffer, TRACE_EVENT_PHASE_END,
1064 "Primary Command Buffer"));
1065
1066 ANGLE_VK_TRY(context, primaryCommandBuffer->end());
1067
1068 clear();
1069
1070 return angle::Result::Continue;
1071 }
1072
empty() const1073 bool CommandGraph::empty() const
1074 {
1075 return mNodes.empty();
1076 }
1077
clear()1078 void CommandGraph::clear()
1079 {
1080 mLastBarrierIndex = kInvalidNodeIndex;
1081 // Release cmd graph pool memory now that cmds are submitted
1082 // NOTE: This frees all memory since last push. Right now only the CommandGraph
1083 // will push the allocator (at creation and below). If other people start
1084 // pushing the allocator this (and/or the allocator) will need to be updated.
1085 mPoolAllocator->pop();
1086 mPoolAllocator->push();
1087
1088 // TODO(jmadill): Use pool allocator for performance. http://anglebug.com/2951
1089 for (CommandGraphNode *node : mNodes)
1090 {
1091 delete node;
1092 }
1093 mNodes.clear();
1094 }
1095
beginQuery(const QueryPool * queryPool,uint32_t queryIndex)1096 void CommandGraph::beginQuery(const QueryPool *queryPool, uint32_t queryIndex)
1097 {
1098 CommandGraphNode *newNode = allocateBarrierNode(CommandGraphNodeFunction::BeginQuery,
1099 CommandGraphResourceType::Query, 0);
1100 newNode->setQueryPool(queryPool, queryIndex);
1101 }
1102
endQuery(const QueryPool * queryPool,uint32_t queryIndex)1103 void CommandGraph::endQuery(const QueryPool *queryPool, uint32_t queryIndex)
1104 {
1105 CommandGraphNode *newNode =
1106 allocateBarrierNode(CommandGraphNodeFunction::EndQuery, CommandGraphResourceType::Query, 0);
1107 newNode->setQueryPool(queryPool, queryIndex);
1108 }
1109
writeTimestamp(const QueryPool * queryPool,uint32_t queryIndex)1110 void CommandGraph::writeTimestamp(const QueryPool *queryPool, uint32_t queryIndex)
1111 {
1112 CommandGraphNode *newNode = allocateBarrierNode(CommandGraphNodeFunction::WriteTimestamp,
1113 CommandGraphResourceType::Query, 0);
1114 newNode->setQueryPool(queryPool, queryIndex);
1115 }
1116
beginTransformFeedbackEmulatedQuery()1117 void CommandGraph::beginTransformFeedbackEmulatedQuery()
1118 {
1119 allocateBarrierNode(CommandGraphNodeFunction::BeginTransformFeedbackQuery,
1120 CommandGraphResourceType::EmulatedQuery, 0);
1121 }
1122
endTransformFeedbackEmulatedQuery()1123 void CommandGraph::endTransformFeedbackEmulatedQuery()
1124 {
1125 allocateBarrierNode(CommandGraphNodeFunction::EndTransformFeedbackQuery,
1126 CommandGraphResourceType::EmulatedQuery, 0);
1127 }
1128
setFenceSync(const vk::Event & event)1129 void CommandGraph::setFenceSync(const vk::Event &event)
1130 {
1131 CommandGraphNode *newNode = allocateBarrierNode(CommandGraphNodeFunction::SetFenceSync,
1132 CommandGraphResourceType::FenceSync,
1133 reinterpret_cast<uintptr_t>(&event));
1134 newNode->setFenceSync(event);
1135 }
1136
waitFenceSync(const vk::Event & event)1137 void CommandGraph::waitFenceSync(const vk::Event &event)
1138 {
1139 CommandGraphNode *newNode = allocateBarrierNode(CommandGraphNodeFunction::WaitFenceSync,
1140 CommandGraphResourceType::FenceSync,
1141 reinterpret_cast<uintptr_t>(&event));
1142 newNode->setFenceSync(event);
1143 }
1144
memoryBarrier(VkFlags srcAccess,VkFlags dstAccess,VkPipelineStageFlags stages)1145 void CommandGraph::memoryBarrier(VkFlags srcAccess, VkFlags dstAccess, VkPipelineStageFlags stages)
1146 {
1147 CommandGraphNode *newNode = allocateBarrierNode(CommandGraphNodeFunction::GraphBarrier,
1148 CommandGraphResourceType::GraphBarrier, 0);
1149 newNode->addGlobalMemoryBarrier(srcAccess, dstAccess, stages);
1150 }
1151
insertDebugMarker(GLenum source,std::string && marker)1152 void CommandGraph::insertDebugMarker(GLenum source, std::string &&marker)
1153 {
1154 CommandGraphNode *newNode = allocateBarrierNode(CommandGraphNodeFunction::InsertDebugMarker,
1155 CommandGraphResourceType::DebugMarker, 0);
1156 newNode->setDebugMarker(source, std::move(marker));
1157 }
1158
pushDebugMarker(GLenum source,std::string && marker)1159 void CommandGraph::pushDebugMarker(GLenum source, std::string &&marker)
1160 {
1161 CommandGraphNode *newNode = allocateBarrierNode(CommandGraphNodeFunction::PushDebugMarker,
1162 CommandGraphResourceType::DebugMarker, 0);
1163 newNode->setDebugMarker(source, std::move(marker));
1164 }
1165
popDebugMarker()1166 void CommandGraph::popDebugMarker()
1167 {
1168 allocateBarrierNode(CommandGraphNodeFunction::PopDebugMarker,
1169 CommandGraphResourceType::DebugMarker, 0);
1170 }
1171
makeHostVisibleBufferWriteAvailable()1172 void CommandGraph::makeHostVisibleBufferWriteAvailable()
1173 {
1174 allocateBarrierNode(CommandGraphNodeFunction::HostAvailabilityOperation,
1175 CommandGraphResourceType::HostAvailabilityOperation, 0);
1176 }
1177
syncExternalMemory()1178 void CommandGraph::syncExternalMemory()
1179 {
1180 // Add an all-inclusive memory barrier.
1181 memoryBarrier(VK_ACCESS_MEMORY_WRITE_BIT,
1182 VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
1183 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
1184 }
1185
1186 // Dumps the command graph into a dot file that works with graphviz.
dumpGraphDotFile(std::ostream & out) const1187 void CommandGraph::dumpGraphDotFile(std::ostream &out) const
1188 {
1189 // This ID maps a node pointer to a monotonic ID. It allows us to look up parent node IDs.
1190 std::map<const CommandGraphNode *, int> nodeIDMap;
1191 std::map<uintptr_t, int> objectIDMap;
1192 std::map<std::pair<VkQueryPool, uint32_t>, int> queryIDMap;
1193
1194 // Map nodes to ids.
1195 for (size_t nodeIndex = 0; nodeIndex < mNodes.size(); ++nodeIndex)
1196 {
1197 const CommandGraphNode *node = mNodes[nodeIndex];
1198 nodeIDMap[node] = static_cast<int>(nodeIndex) + 1;
1199 }
1200
1201 int bufferIDCounter = 1;
1202 int framebufferIDCounter = 1;
1203 int imageIDCounter = 1;
1204 int queryIDCounter = 1;
1205 int dispatcherIDCounter = 1;
1206 int fenceIDCounter = 1;
1207 int xfbIDCounter = 1;
1208
1209 out << "digraph {" << std::endl;
1210
1211 for (const CommandGraphNode *node : mNodes)
1212 {
1213 int nodeID = nodeIDMap[node];
1214
1215 std::stringstream strstr;
1216 strstr << GetResourceTypeName(node->getResourceTypeForDiagnostics(), node->getFunction());
1217
1218 if (node->getResourceTypeForDiagnostics() == CommandGraphResourceType::DebugMarker)
1219 {
1220 // For debug markers, use the string from the debug marker itself.
1221 if (node->getFunction() != CommandGraphNodeFunction::PopDebugMarker)
1222 {
1223 strstr << " " << node->getDebugMarker();
1224 }
1225 }
1226 else if (node->getResourceTypeForDiagnostics() == CommandGraphResourceType::Query)
1227 {
1228 // Special case for queries as they cannot generate a resource ID at creation time that
1229 // would reliably fit in a uintptr_t.
1230 strstr << " ";
1231
1232 ASSERT(node->getResourceIDForDiagnostics() == 0);
1233
1234 auto queryID = std::make_pair(node->getQueryPool(), node->getQueryIndex());
1235
1236 auto it = queryIDMap.find(queryID);
1237 if (it != queryIDMap.end())
1238 {
1239 strstr << it->second;
1240 }
1241 else
1242 {
1243 int id = queryIDCounter++;
1244
1245 queryIDMap[queryID] = id;
1246 strstr << id;
1247 }
1248 }
1249 else if (!node->hasDiagnosticID())
1250 {
1251 // Nothing to append for these special nodes. The name is sufficient.
1252 }
1253 else
1254 {
1255 strstr << " ";
1256
1257 // Otherwise assign each object an ID, so all the nodes of the same object have the same
1258 // label.
1259 ASSERT(node->getResourceIDForDiagnostics() != 0);
1260 auto it = objectIDMap.find(node->getResourceIDForDiagnostics());
1261 if (it != objectIDMap.end())
1262 {
1263 strstr << it->second;
1264 }
1265 else
1266 {
1267 int id = 0;
1268
1269 switch (node->getResourceTypeForDiagnostics())
1270 {
1271 case CommandGraphResourceType::Buffer:
1272 id = bufferIDCounter++;
1273 break;
1274 case CommandGraphResourceType::Framebuffer:
1275 id = framebufferIDCounter++;
1276 break;
1277 case CommandGraphResourceType::Image:
1278 id = imageIDCounter++;
1279 break;
1280 case CommandGraphResourceType::Dispatcher:
1281 id = dispatcherIDCounter++;
1282 break;
1283 case CommandGraphResourceType::FenceSync:
1284 id = fenceIDCounter++;
1285 break;
1286 case CommandGraphResourceType::EmulatedQuery:
1287 id = xfbIDCounter++;
1288 break;
1289 default:
1290 UNREACHABLE();
1291 break;
1292 }
1293
1294 objectIDMap[node->getResourceIDForDiagnostics()] = id;
1295 strstr << id;
1296 }
1297 }
1298
1299 const std::string &label = strstr.str();
1300 out << " " << nodeID << "[label =<" << label << "<BR/><FONT POINT-SIZE=\"10\">Node ID "
1301 << nodeID << node->dumpCommandsForDiagnostics("<BR/>") << "</FONT>>];" << std::endl;
1302 }
1303
1304 for (const CommandGraphNode *node : mNodes)
1305 {
1306 int nodeID = nodeIDMap[node];
1307
1308 for (const CommandGraphNode *parent : node->getParentsForDiagnostics())
1309 {
1310 int parentID = nodeIDMap[parent];
1311 out << " " << parentID << " -> " << nodeID << ";" << std::endl;
1312 }
1313 }
1314
1315 out << "}" << std::endl;
1316 }
1317
updateOverlay(ContextVk * contextVk) const1318 void CommandGraph::updateOverlay(ContextVk *contextVk) const
1319 {
1320 const gl::OverlayType *overlay = contextVk->getOverlay();
1321
1322 overlay->getRunningGraphWidget(gl::WidgetId::VulkanCommandGraphSize)->add(mNodes.size());
1323
1324 overlay->getRunningHistogramWidget(gl::WidgetId::VulkanSecondaryCommandBufferPoolWaste)
1325 ->set(CalculateSecondaryCommandBufferPoolWaste(mNodes));
1326 overlay->getRunningHistogramWidget(gl::WidgetId::VulkanSecondaryCommandBufferPoolWaste)->next();
1327 }
1328
getLastBarrierNode(size_t * indexOut)1329 CommandGraphNode *CommandGraph::getLastBarrierNode(size_t *indexOut)
1330 {
1331 *indexOut = mLastBarrierIndex == kInvalidNodeIndex ? 0 : mLastBarrierIndex;
1332 return mLastBarrierIndex == kInvalidNodeIndex ? nullptr : mNodes[mLastBarrierIndex];
1333 }
1334
addDependenciesToNextBarrier(size_t begin,size_t end,CommandGraphNode * nextBarrier)1335 void CommandGraph::addDependenciesToNextBarrier(size_t begin,
1336 size_t end,
1337 CommandGraphNode *nextBarrier)
1338 {
1339 for (size_t i = begin; i < end; ++i)
1340 {
1341 // As a small optimization, only add edges to childless nodes. The others have an
1342 // indirect dependency.
1343 if (!mNodes[i]->hasChildren())
1344 {
1345 CommandGraphNode::SetHappensBeforeDependency(mNodes[i], nextBarrier);
1346 }
1347 }
1348 }
1349
1350 // ResourceUseList implementation.
1351 ResourceUseList::ResourceUseList() = default;
1352
~ResourceUseList()1353 ResourceUseList::~ResourceUseList()
1354 {
1355 ASSERT(mResourceUses.empty());
1356 }
1357
releaseResourceUses()1358 void ResourceUseList::releaseResourceUses()
1359 {
1360 for (SharedResourceUse &use : mResourceUses)
1361 {
1362 use.release();
1363 }
1364
1365 mResourceUses.clear();
1366 }
1367
releaseResourceUsesAndUpdateSerials(Serial serial)1368 void ResourceUseList::releaseResourceUsesAndUpdateSerials(Serial serial)
1369 {
1370 for (SharedResourceUse &use : mResourceUses)
1371 {
1372 use.releaseAndUpdateSerial(serial);
1373 }
1374
1375 mResourceUses.clear();
1376 }
1377 } // namespace vk
1378 } // namespace rx
1379