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 #ifndef LIBANGLE_RENDERER_VULKAN_COMMAND_GRAPH_H_
11 #define LIBANGLE_RENDERER_VULKAN_COMMAND_GRAPH_H_
12
13 #include "libANGLE/renderer/vulkan/SecondaryCommandBuffer.h"
14 #include "libANGLE/renderer/vulkan/vk_cache_utils.h"
15
16 namespace rx
17 {
18
19 namespace vk
20 {
21 class CommandGraph;
22
23 enum class VisitedState
24 {
25 Unvisited,
26 Ready,
27 Visited,
28 };
29
30 enum class CommandGraphResourceType
31 {
32 Buffer,
33 Framebuffer,
34 Image,
35 Query,
36 Dispatcher,
37 // Transform feedback queries could be handled entirely on the CPU (if not using
38 // VK_EXT_transform_feedback), but still need to generate a command graph barrier node.
39 EmulatedQuery,
40 FenceSync,
41 GraphBarrier,
42 DebugMarker,
43 HostAvailabilityOperation,
44 };
45
46 // Certain functionality cannot be put in secondary command buffers, so they are special-cased in
47 // the node.
48 enum class CommandGraphNodeFunction
49 {
50 Generic,
51 BeginQuery,
52 EndQuery,
53 WriteTimestamp,
54 BeginTransformFeedbackQuery,
55 EndTransformFeedbackQuery,
56 SetFenceSync,
57 WaitFenceSync,
58 GraphBarrier,
59 InsertDebugMarker,
60 PushDebugMarker,
61 PopDebugMarker,
62 HostAvailabilityOperation,
63 };
64
65 // Receives notifications when a render pass command buffer is no longer able to record. Can be
66 // used with inheritance. Faster than using an interface class since it has inlined methods. Could
67 // be used with composition by adding a getCommandBuffer method.
68 class RenderPassOwner
69 {
70 public:
71 RenderPassOwner() = default;
~RenderPassOwner()72 virtual ~RenderPassOwner() {}
73
onRenderPassFinished()74 ANGLE_INLINE void onRenderPassFinished() { mRenderPassCommandBuffer = nullptr; }
75
76 protected:
77 CommandBuffer *mRenderPassCommandBuffer = nullptr;
78 };
79
80 // Only used internally in the command graph. Kept in the header for better inlining performance.
81 class CommandGraphNode final : angle::NonCopyable
82 {
83 public:
84 CommandGraphNode(CommandGraphNodeFunction function, angle::PoolAllocator *poolAllocator);
85 ~CommandGraphNode();
86
87 // Immutable queries for when we're walking the commands tree.
getOutsideRenderPassCommands()88 CommandBuffer *getOutsideRenderPassCommands()
89 {
90 ASSERT(!mHasChildren);
91 return &mOutsideRenderPassCommands;
92 }
93
getInsideRenderPassCommands()94 CommandBuffer *getInsideRenderPassCommands()
95 {
96 ASSERT(!mHasChildren);
97 return &mInsideRenderPassCommands;
98 }
99
100 // For outside the render pass (copies, transitions, etc).
101 angle::Result beginOutsideRenderPassRecording(ContextVk *context,
102 const CommandPool &commandPool,
103 CommandBuffer **commandsOut);
104
105 // For rendering commands (draws).
106 angle::Result beginInsideRenderPassRecording(ContextVk *context, CommandBuffer **commandsOut);
107
108 // storeRenderPassInfo and append*RenderTarget store info relevant to the RenderPass.
109 void storeRenderPassInfo(const Framebuffer &framebuffer,
110 const gl::Rectangle renderArea,
111 const vk::RenderPassDesc &renderPassDesc,
112 const AttachmentOpsArray &renderPassAttachmentOps,
113 const std::vector<VkClearValue> &clearValues);
114
clearRenderPassColorAttachment(size_t attachmentIndex,const VkClearColorValue & clearValue)115 void clearRenderPassColorAttachment(size_t attachmentIndex, const VkClearColorValue &clearValue)
116 {
117 mRenderPassAttachmentOps[attachmentIndex].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
118 mRenderPassClearValues[attachmentIndex].color = clearValue;
119 }
120
clearRenderPassDepthAttachment(size_t attachmentIndex,float depth)121 void clearRenderPassDepthAttachment(size_t attachmentIndex, float depth)
122 {
123 mRenderPassAttachmentOps[attachmentIndex].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
124 mRenderPassClearValues[attachmentIndex].depthStencil.depth = depth;
125 }
126
clearRenderPassStencilAttachment(size_t attachmentIndex,uint32_t stencil)127 void clearRenderPassStencilAttachment(size_t attachmentIndex, uint32_t stencil)
128 {
129 mRenderPassAttachmentOps[attachmentIndex].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
130 mRenderPassClearValues[attachmentIndex].depthStencil.stencil = stencil;
131 }
132
invalidateRenderPassColorAttachment(size_t attachmentIndex)133 void invalidateRenderPassColorAttachment(size_t attachmentIndex)
134 {
135 mRenderPassAttachmentOps[attachmentIndex].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
136 }
137
invalidateRenderPassDepthAttachment(size_t attachmentIndex)138 void invalidateRenderPassDepthAttachment(size_t attachmentIndex)
139 {
140 mRenderPassAttachmentOps[attachmentIndex].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
141 }
142
invalidateRenderPassStencilAttachment(size_t attachmentIndex)143 void invalidateRenderPassStencilAttachment(size_t attachmentIndex)
144 {
145 mRenderPassAttachmentOps[attachmentIndex].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
146 }
147
148 // Dependency commands order node execution in the command graph.
149 // Once a node has commands that must happen after it, recording is stopped and the node is
150 // frozen forever.
SetHappensBeforeDependency(CommandGraphNode * beforeNode,CommandGraphNode * afterNode)151 static void SetHappensBeforeDependency(CommandGraphNode *beforeNode,
152 CommandGraphNode *afterNode)
153 {
154 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
155 afterNode->mParents.emplace_back(beforeNode);
156 beforeNode->setHasChildren();
157 }
158
159 static void SetHappensBeforeDependencies(CommandGraphNode **beforeNodes,
160 size_t beforeNodesCount,
161 CommandGraphNode *afterNode);
162
163 static void SetHappensBeforeDependencies(CommandGraphNode *beforeNode,
164 CommandGraphNode **afterNodes,
165 size_t afterNodesCount);
166
167 bool hasParents() const;
hasChildren()168 bool hasChildren() const { return mHasChildren; }
169
170 // Commands for traversing the node on a flush operation.
171 VisitedState visitedState() const;
172 void visitParents(std::vector<CommandGraphNode *> *stack);
173 angle::Result visitAndExecute(Context *context,
174 Serial serial,
175 RenderPassCache *renderPassCache,
176 PrimaryCommandBuffer *primaryCommandBuffer);
177
178 // Only used in the command graph diagnostics.
179 const std::vector<CommandGraphNode *> &getParentsForDiagnostics() const;
180 void setDiagnosticInfo(CommandGraphResourceType resourceType, uintptr_t resourceID);
181
getResourceTypeForDiagnostics()182 CommandGraphResourceType getResourceTypeForDiagnostics() const { return mResourceType; }
getResourceIDForDiagnostics()183 uintptr_t getResourceIDForDiagnostics() const { return mResourceID; }
184 bool hasDiagnosticID() const;
185 std::string dumpCommandsForDiagnostics(const char *separator) const;
186 void getMemoryUsageStatsForDiagnostics(size_t *usedMemoryOut, size_t *allocatedMemoryOut) const;
187
getRenderPassRenderArea()188 const gl::Rectangle &getRenderPassRenderArea() const { return mRenderPassRenderArea; }
189
getFunction()190 CommandGraphNodeFunction getFunction() const { return mFunction; }
191
192 void setQueryPool(const QueryPool *queryPool, uint32_t queryIndex);
getQueryPool()193 VkQueryPool getQueryPool() const { return mQueryPool; }
getQueryIndex()194 uint32_t getQueryIndex() const { return mQueryIndex; }
195 void setFenceSync(const vk::Event &event);
196 void setDebugMarker(GLenum source, std::string &&marker);
getDebugMarker()197 const std::string &getDebugMarker() const { return mDebugMarker; }
198
addGlobalMemoryBarrier(VkFlags srcAccess,VkFlags dstAccess,VkPipelineStageFlags stages)199 ANGLE_INLINE void addGlobalMemoryBarrier(VkFlags srcAccess,
200 VkFlags dstAccess,
201 VkPipelineStageFlags stages)
202 {
203 mGlobalMemoryBarrierSrcAccess |= srcAccess;
204 mGlobalMemoryBarrierDstAccess |= dstAccess;
205 mGlobalMemoryBarrierStages |= stages;
206 }
207
setActiveTransformFeedbackInfo(size_t validBufferCount,const VkBuffer * counterBuffers,bool rebindBuffer)208 ANGLE_INLINE void setActiveTransformFeedbackInfo(size_t validBufferCount,
209 const VkBuffer *counterBuffers,
210 bool rebindBuffer)
211 {
212 mValidTransformFeedbackBufferCount = static_cast<uint32_t>(validBufferCount);
213 mRebindTransformFeedbackBuffers = rebindBuffer;
214
215 for (size_t index = 0; index < validBufferCount; index++)
216 {
217 mTransformFeedbackCounterBuffers[index] = counterBuffers[index];
218 }
219 }
220
221 // This can only be set for RenderPass nodes. Each RenderPass node can have at most one owner.
setRenderPassOwner(RenderPassOwner * owner)222 void setRenderPassOwner(RenderPassOwner *owner)
223 {
224 ASSERT(mRenderPassOwner == nullptr);
225 mRenderPassOwner = owner;
226 }
227
228 private:
setHasChildren()229 ANGLE_INLINE void setHasChildren()
230 {
231 mHasChildren = true;
232 if (mRenderPassOwner)
233 {
234 mRenderPassOwner->onRenderPassFinished();
235 }
236 }
237
238 // Used for testing only.
239 bool isChildOf(CommandGraphNode *parent);
240
241 // Only used if we need a RenderPass for these commands.
242 RenderPassDesc mRenderPassDesc;
243 AttachmentOpsArray mRenderPassAttachmentOps;
244 Framebuffer mRenderPassFramebuffer;
245 gl::Rectangle mRenderPassRenderArea;
246 gl::AttachmentArray<VkClearValue> mRenderPassClearValues;
247
248 CommandGraphNodeFunction mFunction;
249 angle::PoolAllocator *mPoolAllocator;
250 // Keep separate buffers for commands inside and outside a RenderPass.
251 // TODO(jmadill): We might not need inside and outside RenderPass commands separate.
252 CommandBuffer mOutsideRenderPassCommands;
253 CommandBuffer mInsideRenderPassCommands;
254
255 // Special-function additional data:
256 // Queries:
257 VkQueryPool mQueryPool;
258 uint32_t mQueryIndex;
259 // GLsync and EGLSync:
260 VkEvent mFenceSyncEvent;
261 // Debug markers:
262 GLenum mDebugMarkerSource;
263 std::string mDebugMarker;
264
265 // Parents are commands that must be submitted before 'this' CommandNode can be submitted.
266 std::vector<CommandGraphNode *> mParents;
267
268 // If this is true, other commands exist that must be submitted after 'this' command.
269 bool mHasChildren;
270
271 // Used when traversing the dependency graph.
272 VisitedState mVisitedState;
273
274 // Additional diagnostic information.
275 CommandGraphResourceType mResourceType;
276 uintptr_t mResourceID;
277
278 // For global memory barriers.
279 VkFlags mGlobalMemoryBarrierSrcAccess;
280 VkFlags mGlobalMemoryBarrierDstAccess;
281 VkPipelineStageFlags mGlobalMemoryBarrierStages;
282
283 // Render pass command buffer notifications.
284 RenderPassOwner *mRenderPassOwner;
285
286 // Active transform feedback state
287 gl::TransformFeedbackBuffersArray<VkBuffer> mTransformFeedbackCounterBuffers;
288 uint32_t mValidTransformFeedbackBufferCount;
289 bool mRebindTransformFeedbackBuffers;
290 };
291
292 // Tracks how a resource is used in a command graph and in a VkQueue. The reference count indicates
293 // the number of times a resource is used in the graph. The serial indicates the last current use
294 // of a resource in the VkQueue. The reference count and serial together can determine if a
295 // resource is in use.
296 struct ResourceUse
297 {
298 ResourceUse() = default;
299
300 uint32_t counter = 0;
301 Serial serial;
302 };
303
304 class SharedResourceUse final : angle::NonCopyable
305 {
306 public:
SharedResourceUse()307 SharedResourceUse() : mUse(nullptr) {}
~SharedResourceUse()308 ~SharedResourceUse() { ASSERT(!valid()); }
SharedResourceUse(SharedResourceUse && rhs)309 SharedResourceUse(SharedResourceUse &&rhs) : mUse(rhs.mUse) { rhs.mUse = nullptr; }
310 SharedResourceUse &operator=(SharedResourceUse &&rhs)
311 {
312 std::swap(mUse, rhs.mUse);
313 return *this;
314 }
315
valid()316 ANGLE_INLINE bool valid() const { return mUse != nullptr; }
317
init()318 void init()
319 {
320 ASSERT(!mUse);
321 mUse = new ResourceUse;
322 mUse->counter++;
323 }
324
release()325 ANGLE_INLINE void release()
326 {
327 ASSERT(valid());
328 ASSERT(mUse->counter > 0);
329 if (--mUse->counter == 0)
330 {
331 delete mUse;
332 }
333 mUse = nullptr;
334 }
335
releaseAndUpdateSerial(Serial serial)336 ANGLE_INLINE void releaseAndUpdateSerial(Serial serial)
337 {
338 ASSERT(valid());
339 ASSERT(mUse->counter > 0);
340 ASSERT(mUse->serial <= serial);
341 mUse->serial = serial;
342 release();
343 }
344
set(const SharedResourceUse & rhs)345 ANGLE_INLINE void set(const SharedResourceUse &rhs)
346 {
347 ASSERT(rhs.valid());
348 ASSERT(!valid());
349 ASSERT(rhs.mUse->counter < std::numeric_limits<uint32_t>::max());
350 mUse = rhs.mUse;
351 mUse->counter++;
352 }
353
354 // The base counter value for a live resource is "1". Any value greater than one indicates
355 // the resource is in use by a vk::CommandGraph.
hasRecordedCommands()356 ANGLE_INLINE bool hasRecordedCommands() const
357 {
358 ASSERT(valid());
359 return mUse->counter > 1;
360 }
361
hasRunningCommands(Serial lastCompletedSerial)362 ANGLE_INLINE bool hasRunningCommands(Serial lastCompletedSerial) const
363 {
364 ASSERT(valid());
365 return mUse->serial > lastCompletedSerial;
366 }
367
isCurrentlyInUse(Serial lastCompletedSerial)368 ANGLE_INLINE bool isCurrentlyInUse(Serial lastCompletedSerial) const
369 {
370 return hasRecordedCommands() || hasRunningCommands(lastCompletedSerial);
371 }
372
getSerial()373 ANGLE_INLINE Serial getSerial() const
374 {
375 ASSERT(valid());
376 return mUse->serial;
377 }
378
379 private:
380 ResourceUse *mUse;
381 };
382
383 class SharedGarbage
384 {
385 public:
386 SharedGarbage();
387 SharedGarbage(SharedGarbage &&other);
388 SharedGarbage(SharedResourceUse &&use, std::vector<GarbageObject> &&garbage);
389 ~SharedGarbage();
390 SharedGarbage &operator=(SharedGarbage &&rhs);
391
392 bool destroyIfComplete(VkDevice device, Serial completedSerial);
393
394 private:
395 SharedResourceUse mLifetime;
396 std::vector<GarbageObject> mGarbage;
397 };
398
399 using SharedGarbageList = std::vector<SharedGarbage>;
400
401 // Mixin to abstract away the resource use tracking.
402 class ResourceUseList final : angle::NonCopyable
403 {
404 public:
405 ResourceUseList();
406 virtual ~ResourceUseList();
407
408 void add(const SharedResourceUse &resourceUse);
409
410 void releaseResourceUses();
411 void releaseResourceUsesAndUpdateSerials(Serial serial);
412
413 private:
414 std::vector<SharedResourceUse> mResourceUses;
415 };
416
417 // ResourceUser inlines.
add(const SharedResourceUse & resourceUse)418 ANGLE_INLINE void ResourceUseList::add(const SharedResourceUse &resourceUse)
419 {
420 // Disabled the assert because of difficulties with ImageView references.
421 // TODO(jmadill): Clean up with graph redesign. http://anglebug.com/4029
422 // ASSERT(!empty());
423 SharedResourceUse newUse;
424 newUse.set(resourceUse);
425 mResourceUses.emplace_back(std::move(newUse));
426 }
427
428 // This is a helper class for back-end objects used in Vk command buffers. It records a serial
429 // at command recording times indicating an order in the queue. We use Fences to detect when
430 // commands finish, and then release any unreferenced and deleted resources based on the stored
431 // queue serial in a special 'garbage' queue. Resources also track current read and write
432 // dependencies. Only one command buffer node can be writing to the Resource at a time, but many
433 // can be reading from it. Together the dependencies will form a command graph at submission time.
434 class CommandGraphResource : angle::NonCopyable
435 {
436 public:
437 virtual ~CommandGraphResource();
438
439 // Returns true if the resource has commands in the graph. This is used to know if a flush
440 // should be performed, e.g. if we need to wait for the GPU to finish with the resource.
hasRecordedCommands()441 bool hasRecordedCommands() const { return mUse.hasRecordedCommands(); }
442
443 // Determine if the driver has finished execution with this resource.
hasRunningCommands(Serial lastCompletedSerial)444 bool hasRunningCommands(Serial lastCompletedSerial) const
445 {
446 return mUse.hasRunningCommands(lastCompletedSerial);
447 }
448
449 // Returns true if the resource is in use by ANGLE or the driver.
isCurrentlyInUse(Serial lastCompletedSerial)450 bool isCurrentlyInUse(Serial lastCompletedSerial) const
451 {
452 return mUse.isCurrentlyInUse(lastCompletedSerial);
453 }
454
455 // Ensures the driver is caught up to this resource and it is only in use by ANGLE.
456 angle::Result finishRunningCommands(ContextVk *contextVk);
457
458 // Sets up dependency relations. 'this' resource is the resource being written to.
459 void addWriteDependency(ContextVk *contextVk, CommandGraphResource *writingResource);
460
461 // Sets up dependency relations. 'this' resource is the resource being read.
462 void addReadDependency(ContextVk *contextVk, CommandGraphResource *readingResource);
463
464 // Updates the in-use serial tracked for this resource. Will clear dependencies if the resource
465 // was not used in this set of command nodes.
466 void onResourceAccess(ResourceUseList *resourceUseList);
467 void updateCurrentAccessNodes();
468
469 // If a resource is recreated, as in released and reinitialized, the next access to the
470 // resource will not create an edge from its last node and will create a new independent node.
471 // This is because mUse is reset and the graph believes it's an entirely new resource. In very
472 // particular cases, such as recreating an image with full mipchain or adding STORAGE_IMAGE flag
473 // to its uses, this function is used to preserve the link between the previous and new
474 // nodes allocated for this resource.
475 void onResourceRecreated(ResourceUseList *resourceUseList);
476
477 // Allocates a write node via getNewWriteNode and returns a started command buffer.
478 // The started command buffer will render outside of a RenderPass.
479 // Will append to an existing command buffer/graph node if possible.
480 angle::Result recordCommands(ContextVk *context, CommandBuffer **commandBufferOut);
481
482 // Begins a command buffer on the current graph node for in-RenderPass rendering.
483 // Called from FramebufferVk::startNewRenderPass and UtilsVk functions.
484 angle::Result beginRenderPass(ContextVk *contextVk,
485 const Framebuffer &framebuffer,
486 const gl::Rectangle &renderArea,
487 const RenderPassDesc &renderPassDesc,
488 const AttachmentOpsArray &renderPassAttachmentOps,
489 const std::vector<VkClearValue> &clearValues,
490 CommandBuffer **commandBufferOut);
491
492 // Checks if we're in a RenderPass without children.
493 bool hasStartedRenderPass() const;
494
495 // Checks if we're in a RenderPass that encompasses renderArea, returning true if so. Updates
496 // serial internally. Returns the started command buffer in commandBufferOut.
497 bool appendToStartedRenderPass(ResourceUseList *resourceUseList,
498 const gl::Rectangle &renderArea,
499 CommandBuffer **commandBufferOut);
500
501 // Returns true if the render pass is started, but there are no commands yet recorded in it.
502 // This is useful to know if the render pass ops can be modified.
503 bool renderPassStartedButEmpty() const;
504
505 void clearRenderPassColorAttachment(size_t attachmentIndex,
506 const VkClearColorValue &clearValue);
507 void clearRenderPassDepthAttachment(size_t attachmentIndex, float depth);
508 void clearRenderPassStencilAttachment(size_t attachmentIndex, uint32_t stencil);
509
510 void invalidateRenderPassColorAttachment(size_t attachmentIndex);
511 void invalidateRenderPassDepthAttachment(size_t attachmentIndex);
512 void invalidateRenderPassStencilAttachment(size_t attachmentIndex);
513
514 // Accessor for RenderPass RenderArea.
515 const gl::Rectangle &getRenderPassRenderArea() const;
516
517 // Called when 'this' object changes, but we'd like to start a new command buffer later.
518 void finishCurrentCommands(ContextVk *contextVk);
519
520 // Store a deferred memory barrier. Will be recorded into a primary command buffer at submit.
521 void addGlobalMemoryBarrier(VkFlags srcAccess, VkFlags dstAccess, VkPipelineStageFlags stages);
522
523 // Sets active transform feedback information to current writing node.
524 void setActiveTransformFeedbackInfo(size_t validBufferCount,
525 const VkBuffer *counterBuffers,
526 bool rebindBuffer);
527
528 protected:
529 explicit CommandGraphResource(CommandGraphResourceType resourceType);
530
531 // Current resource lifetime.
532 SharedResourceUse mUse;
533
534 private:
535 // Returns true if this node has a current writing node with no children.
536 ANGLE_INLINE bool hasChildlessWritingNode() const;
537
538 void startNewCommands(ContextVk *contextVk);
539
540 void onWriteImpl(ContextVk *contextVk, CommandGraphNode *writingNode);
541
542 std::vector<CommandGraphNode *> mCurrentReadingNodes;
543
544 // Current command graph writing node.
545 CommandGraphNode *mCurrentWritingNode;
546
547 // Additional diagnostic information.
548 CommandGraphResourceType mResourceType;
549 };
550
551 // Translating OpenGL commands into Vulkan and submitting them immediately loses out on some
552 // of the powerful flexiblity Vulkan offers in RenderPasses. Load/Store ops can automatically
553 // clear RenderPass attachments, or preserve the contents. RenderPass automatic layout transitions
554 // can improve certain performance cases. Also, we can remove redundant RenderPass Begin and Ends
555 // when processing interleaved draw operations on independent Framebuffers.
556 //
557 // ANGLE's CommandGraph (and CommandGraphNode) attempt to solve these problems using deferred
558 // command submission. We also sometimes call this command re-ordering. A brief summary:
559 //
560 // During GL command processing, we record Vulkan commands into SecondaryCommandBuffers, which
561 // are stored in CommandGraphNodes, and these nodes are chained together via dependencies to
562 // form a directed acyclic CommandGraph. When we need to submit the CommandGraph, say during a
563 // SwapBuffers or ReadPixels call, we begin a primary Vulkan CommandBuffer, and walk the
564 // CommandGraph, starting at the most senior nodes, recording SecondaryCommandBuffers inside
565 // and outside RenderPasses as necessary, filled with the right load/store operations. Once
566 // the primary CommandBuffer has recorded all of the SecondaryCommandBuffers from all the open
567 // CommandGraphNodes, we submit the primary CommandBuffer to the VkQueue on the device.
568 //
569 // The Command Graph consists of an array of open Command Graph Nodes. It supports allocating new
570 // nodes for the graph, which are linked via dependency relation calls in CommandGraphNode, and
571 // also submitting the whole command graph via submitCommands.
572 class CommandGraph final : angle::NonCopyable
573 {
574 public:
575 explicit CommandGraph(bool enableGraphDiagnostics, angle::PoolAllocator *poolAllocator);
576 ~CommandGraph();
577
578 // Allocates a new CommandGraphNode and adds it to the list of current open nodes. No ordering
579 // relations exist in the node by default. Call CommandGraphNode::SetHappensBeforeDependency
580 // to set up dependency relations. If the node is a barrier, it will automatically add
581 // dependencies between the previous barrier, the new barrier and all nodes in between.
582 CommandGraphNode *allocateNode(CommandGraphNodeFunction function);
583
584 angle::Result submitCommands(ContextVk *context,
585 Serial serial,
586 RenderPassCache *renderPassCache,
587 PrimaryCommandBuffer *primaryCommandBuffer);
588 bool empty() const;
589 void clear();
590
591 // The following create special-function nodes that don't require a graph resource.
592 // Queries:
593 void beginQuery(const QueryPool *queryPool, uint32_t queryIndex);
594 void endQuery(const QueryPool *queryPool, uint32_t queryIndex);
595 void writeTimestamp(const QueryPool *queryPool, uint32_t queryIndex);
596 void beginTransformFeedbackEmulatedQuery();
597 void endTransformFeedbackEmulatedQuery();
598 // GLsync and EGLSync:
599 void setFenceSync(const vk::Event &event);
600 void waitFenceSync(const vk::Event &event);
601 // Memory barriers:
602 void memoryBarrier(VkFlags srcAccess, VkFlags dstAccess, VkPipelineStageFlags stages);
603 // Debug markers:
604 void insertDebugMarker(GLenum source, std::string &&marker);
605 void pushDebugMarker(GLenum source, std::string &&marker);
606 void popDebugMarker();
607 // Host-visible buffer write availability operation:
608 void makeHostVisibleBufferWriteAvailable();
609 // External memory synchronization:
610 void syncExternalMemory();
611
612 private:
613 CommandGraphNode *allocateBarrierNode(CommandGraphNodeFunction function,
614 CommandGraphResourceType resourceType,
615 uintptr_t resourceID);
616 void setNewBarrier(CommandGraphNode *newBarrier);
617 CommandGraphNode *getLastBarrierNode(size_t *indexOut);
618 void addDependenciesToNextBarrier(size_t begin, size_t end, CommandGraphNode *nextBarrier);
619
620 void dumpGraphDotFile(std::ostream &out) const;
621 void updateOverlay(ContextVk *contextVk) const;
622
623 std::vector<CommandGraphNode *> mNodes;
624 bool mEnableGraphDiagnostics;
625 angle::PoolAllocator *mPoolAllocator;
626
627 // A set of nodes (eventually) exist that act as barriers to guarantee submission order. For
628 // example, a glMemoryBarrier() calls would lead to such a barrier or beginning and ending a
629 // query. This is because the graph can reorder operations if it sees fit. Let's call a barrier
630 // node Bi, and the other nodes Ni. The edges between Ni don't interest us. Before a barrier is
631 // inserted, we have:
632 //
633 // N0 N1 ... Na
634 // \___\__/_/ (dependency egdes, which we don't care about so I'll stop drawing them.
635 // \/
636 //
637 // When the first barrier is inserted, we will have:
638 //
639 // ______
640 // / ____\
641 // / / \
642 // / / /\
643 // N0 N1 ... Na B0
644 //
645 // This makes sure all N0..Na are called before B0. From then on, B0 will be the current
646 // "barrier point" which extends an edge to every next node:
647 //
648 // ______
649 // / ____\
650 // / / \
651 // / / /\
652 // N0 N1 ... Na B0 Na+1 ... Nb
653 // \/ /
654 // \______/
655 //
656 //
657 // When the next barrier B1 is met, all nodes between B0 and B1 will add a depenency on B1 as
658 // well, and the "barrier point" is updated.
659 //
660 // ______
661 // / ____\ ______ ______
662 // / / \ / \ / \
663 // / / /\ / /\ / /\
664 // N0 N1 ... Na B0 Na+1 ... Nb B1 Nb+1 ... Nc B2 ...
665 // \/ / / \/ / /
666 // \______/ / \______/ /
667 // \_______/ \_______/
668 //
669 //
670 // When barrier Bi is introduced, all nodes added since Bi-1 need to add a dependency to Bi
671 // (including Bi-1). We therefore keep track of the node index of the last barrier that was
672 // issued.
673 static constexpr size_t kInvalidNodeIndex = std::numeric_limits<std::size_t>::max();
674 size_t mLastBarrierIndex;
675 };
676
677 // CommandGraphResource inlines.
hasStartedRenderPass()678 ANGLE_INLINE bool CommandGraphResource::hasStartedRenderPass() const
679 {
680 return hasChildlessWritingNode() && mCurrentWritingNode->getInsideRenderPassCommands()->valid();
681 }
682
updateCurrentAccessNodes()683 ANGLE_INLINE void CommandGraphResource::updateCurrentAccessNodes()
684 {
685 // Clear dependencies if this is a new access.
686 if (!mUse.hasRecordedCommands())
687 {
688 mCurrentWritingNode = nullptr;
689 mCurrentReadingNodes.clear();
690 }
691 }
692
onResourceRecreated(ResourceUseList * resourceUseList)693 ANGLE_INLINE void CommandGraphResource::onResourceRecreated(ResourceUseList *resourceUseList)
694 {
695 // Store reference in resource list.
696 resourceUseList->add(mUse);
697 }
698
onResourceAccess(ResourceUseList * resourceUseList)699 ANGLE_INLINE void CommandGraphResource::onResourceAccess(ResourceUseList *resourceUseList)
700 {
701 updateCurrentAccessNodes();
702
703 // Store reference in resource list.
704 resourceUseList->add(mUse);
705 }
706
appendToStartedRenderPass(ResourceUseList * resourceUseList,const gl::Rectangle & renderArea,CommandBuffer ** commandBufferOut)707 ANGLE_INLINE bool CommandGraphResource::appendToStartedRenderPass(ResourceUseList *resourceUseList,
708 const gl::Rectangle &renderArea,
709 CommandBuffer **commandBufferOut)
710 {
711 updateCurrentAccessNodes();
712
713 if (hasStartedRenderPass())
714 {
715 // Store reference in resource list.
716 resourceUseList->add(mUse);
717
718 if (mCurrentWritingNode->getRenderPassRenderArea().encloses(renderArea))
719 {
720 *commandBufferOut = mCurrentWritingNode->getInsideRenderPassCommands();
721 return true;
722 }
723 }
724
725 return false;
726 }
727
renderPassStartedButEmpty()728 ANGLE_INLINE bool CommandGraphResource::renderPassStartedButEmpty() const
729 {
730 return hasStartedRenderPass() && (!vk::CommandBuffer::CanKnowIfEmpty() ||
731 mCurrentWritingNode->getInsideRenderPassCommands()->empty());
732 }
733
clearRenderPassColorAttachment(size_t attachmentIndex,const VkClearColorValue & clearValue)734 ANGLE_INLINE void CommandGraphResource::clearRenderPassColorAttachment(
735 size_t attachmentIndex,
736 const VkClearColorValue &clearValue)
737 {
738 ASSERT(renderPassStartedButEmpty());
739 mCurrentWritingNode->clearRenderPassColorAttachment(attachmentIndex, clearValue);
740 }
741
clearRenderPassDepthAttachment(size_t attachmentIndex,float depth)742 ANGLE_INLINE void CommandGraphResource::clearRenderPassDepthAttachment(size_t attachmentIndex,
743 float depth)
744 {
745 ASSERT(renderPassStartedButEmpty());
746 mCurrentWritingNode->clearRenderPassDepthAttachment(attachmentIndex, depth);
747 }
748
clearRenderPassStencilAttachment(size_t attachmentIndex,uint32_t stencil)749 ANGLE_INLINE void CommandGraphResource::clearRenderPassStencilAttachment(size_t attachmentIndex,
750 uint32_t stencil)
751 {
752 ASSERT(renderPassStartedButEmpty());
753 mCurrentWritingNode->clearRenderPassStencilAttachment(attachmentIndex, stencil);
754 }
755
invalidateRenderPassColorAttachment(size_t attachmentIndex)756 ANGLE_INLINE void CommandGraphResource::invalidateRenderPassColorAttachment(size_t attachmentIndex)
757 {
758 ASSERT(hasStartedRenderPass());
759 mCurrentWritingNode->invalidateRenderPassColorAttachment(attachmentIndex);
760 }
761
invalidateRenderPassDepthAttachment(size_t attachmentIndex)762 ANGLE_INLINE void CommandGraphResource::invalidateRenderPassDepthAttachment(size_t attachmentIndex)
763 {
764 ASSERT(hasStartedRenderPass());
765 mCurrentWritingNode->invalidateRenderPassDepthAttachment(attachmentIndex);
766 }
767
invalidateRenderPassStencilAttachment(size_t attachmentIndex)768 ANGLE_INLINE void CommandGraphResource::invalidateRenderPassStencilAttachment(
769 size_t attachmentIndex)
770 {
771 ASSERT(hasStartedRenderPass());
772 mCurrentWritingNode->invalidateRenderPassStencilAttachment(attachmentIndex);
773 }
774
getRenderPassRenderArea()775 ANGLE_INLINE const gl::Rectangle &CommandGraphResource::getRenderPassRenderArea() const
776 {
777 ASSERT(hasStartedRenderPass());
778 return mCurrentWritingNode->getRenderPassRenderArea();
779 }
780
addGlobalMemoryBarrier(VkFlags srcAccess,VkFlags dstAccess,VkPipelineStageFlags stages)781 ANGLE_INLINE void CommandGraphResource::addGlobalMemoryBarrier(VkFlags srcAccess,
782 VkFlags dstAccess,
783 VkPipelineStageFlags stages)
784 {
785 ASSERT(mCurrentWritingNode);
786 mCurrentWritingNode->addGlobalMemoryBarrier(srcAccess, dstAccess, stages);
787 }
788
setActiveTransformFeedbackInfo(size_t validBufferCount,const VkBuffer * counterBuffers,bool rebindBuffer)789 ANGLE_INLINE void CommandGraphResource::setActiveTransformFeedbackInfo(
790 size_t validBufferCount,
791 const VkBuffer *counterBuffers,
792 bool rebindBuffer)
793 {
794 ASSERT(mCurrentWritingNode);
795 mCurrentWritingNode->setActiveTransformFeedbackInfo(validBufferCount, counterBuffers,
796 rebindBuffer);
797 }
798
hasChildlessWritingNode()799 ANGLE_INLINE bool CommandGraphResource::hasChildlessWritingNode() const
800 {
801 // Note: currently, we don't have a resource that can issue both generic and special
802 // commands. We don't create read/write dependencies between mixed generic/special
803 // resources either. As such, we expect the function to always be generic here. If such a
804 // resource is added in the future, this can add a check for function == generic and fail if
805 // false.
806 ASSERT(mCurrentWritingNode == nullptr ||
807 mCurrentWritingNode->getFunction() == CommandGraphNodeFunction::Generic);
808 return (mCurrentWritingNode != nullptr && !mCurrentWritingNode->hasChildren());
809 }
810 } // namespace vk
811 } // namespace rx
812
813 #endif // LIBANGLE_RENDERER_VULKAN_COMMAND_GRAPH_H_
814