• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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