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 22 enum class VisitedState 23 { 24 Unvisited, 25 Ready, 26 Visited, 27 }; 28 29 enum class CommandGraphResourceType 30 { 31 Buffer, 32 Framebuffer, 33 Image, 34 Query, 35 Dispatcher, 36 // Transform feedback queries could be handled entirely on the CPU (if not using 37 // VK_EXT_transform_feedback), but still need to generate a command graph barrier node. 38 EmulatedQuery, 39 FenceSync, 40 GraphBarrier, 41 DebugMarker, 42 HostAvailabilityOperation, 43 }; 44 45 // Certain functionality cannot be put in secondary command buffers, so they are special-cased in 46 // the node. 47 enum class CommandGraphNodeFunction 48 { 49 Generic, 50 BeginQuery, 51 EndQuery, 52 WriteTimestamp, 53 BeginTransformFeedbackQuery, 54 EndTransformFeedbackQuery, 55 SetFenceSync, 56 WaitFenceSync, 57 GraphBarrier, 58 InsertDebugMarker, 59 PushDebugMarker, 60 PopDebugMarker, 61 HostAvailabilityOperation, 62 }; 63 64 // Receives notifications when a render pass command buffer is no longer able to record. Can be 65 // used with inheritance. Faster than using an interface class since it has inlined methods. Could 66 // be used with composition by adding a getCommandBuffer method. 67 class RenderPassOwner 68 { 69 public: 70 RenderPassOwner() = default; ~RenderPassOwner()71 virtual ~RenderPassOwner() {} 72 onRenderPassFinished()73 ANGLE_INLINE void onRenderPassFinished() { mRenderPassCommandBuffer = nullptr; } 74 75 protected: 76 CommandBuffer *mRenderPassCommandBuffer = nullptr; 77 }; 78 79 // Only used internally in the command graph. Kept in the header for better inlining performance. 80 class CommandGraphNode final : angle::NonCopyable 81 { 82 public: 83 CommandGraphNode(CommandGraphNodeFunction function, angle::PoolAllocator *poolAllocator); 84 ~CommandGraphNode(); 85 86 // Immutable queries for when we're walking the commands tree. getOutsideRenderPassCommands()87 CommandBuffer *getOutsideRenderPassCommands() 88 { 89 ASSERT(!mHasChildren); 90 return &mOutsideRenderPassCommands; 91 } 92 getInsideRenderPassCommands()93 CommandBuffer *getInsideRenderPassCommands() 94 { 95 ASSERT(!mHasChildren); 96 return &mInsideRenderPassCommands; 97 } 98 99 // For outside the render pass (copies, transitions, etc). 100 angle::Result beginOutsideRenderPassRecording(ContextVk *context, 101 const CommandPool &commandPool, 102 CommandBuffer **commandsOut); 103 104 // For rendering commands (draws). 105 angle::Result beginInsideRenderPassRecording(ContextVk *context, CommandBuffer **commandsOut); 106 107 // storeRenderPassInfo and append*RenderTarget store info relevant to the RenderPass. 108 void storeRenderPassInfo(const Framebuffer &framebuffer, 109 const gl::Rectangle renderArea, 110 const vk::RenderPassDesc &renderPassDesc, 111 const AttachmentOpsArray &renderPassAttachmentOps, 112 const std::vector<VkClearValue> &clearValues); 113 clearRenderPassColorAttachment(size_t attachmentIndex,const VkClearColorValue & clearValue)114 void clearRenderPassColorAttachment(size_t attachmentIndex, const VkClearColorValue &clearValue) 115 { 116 mRenderPassAttachmentOps[attachmentIndex].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 117 mRenderPassClearValues[attachmentIndex].color = clearValue; 118 } 119 clearRenderPassDepthAttachment(size_t attachmentIndex,float depth)120 void clearRenderPassDepthAttachment(size_t attachmentIndex, float depth) 121 { 122 mRenderPassAttachmentOps[attachmentIndex].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 123 mRenderPassClearValues[attachmentIndex].depthStencil.depth = depth; 124 } 125 clearRenderPassStencilAttachment(size_t attachmentIndex,uint32_t stencil)126 void clearRenderPassStencilAttachment(size_t attachmentIndex, uint32_t stencil) 127 { 128 mRenderPassAttachmentOps[attachmentIndex].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 129 mRenderPassClearValues[attachmentIndex].depthStencil.stencil = stencil; 130 } 131 invalidateRenderPassColorAttachment(size_t attachmentIndex)132 void invalidateRenderPassColorAttachment(size_t attachmentIndex) 133 { 134 mRenderPassAttachmentOps[attachmentIndex].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 135 } 136 invalidateRenderPassDepthAttachment(size_t attachmentIndex)137 void invalidateRenderPassDepthAttachment(size_t attachmentIndex) 138 { 139 mRenderPassAttachmentOps[attachmentIndex].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 140 } 141 invalidateRenderPassStencilAttachment(size_t attachmentIndex)142 void invalidateRenderPassStencilAttachment(size_t attachmentIndex) 143 { 144 mRenderPassAttachmentOps[attachmentIndex].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 145 } 146 147 // Dependency commands order node execution in the command graph. 148 // Once a node has commands that must happen after it, recording is stopped and the node is 149 // frozen forever. SetHappensBeforeDependency(CommandGraphNode * beforeNode,CommandGraphNode * afterNode)150 static void SetHappensBeforeDependency(CommandGraphNode *beforeNode, 151 CommandGraphNode *afterNode) 152 { 153 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode)); 154 afterNode->mParents.emplace_back(beforeNode); 155 beforeNode->setHasChildren(); 156 } 157 158 static void SetHappensBeforeDependencies(CommandGraphNode **beforeNodes, 159 size_t beforeNodesCount, 160 CommandGraphNode *afterNode); 161 162 static void SetHappensBeforeDependencies(CommandGraphNode *beforeNode, 163 CommandGraphNode **afterNodes, 164 size_t afterNodesCount); 165 166 bool hasParents() const; hasChildren()167 bool hasChildren() const { return mHasChildren; } 168 169 // Commands for traversing the node on a flush operation. 170 VisitedState visitedState() const; 171 void visitParents(std::vector<CommandGraphNode *> *stack); 172 angle::Result visitAndExecute(Context *context, 173 Serial serial, 174 RenderPassCache *renderPassCache, 175 PrimaryCommandBuffer *primaryCommandBuffer); 176 177 // Only used in the command graph diagnostics. 178 const std::vector<CommandGraphNode *> &getParentsForDiagnostics() const; 179 void setDiagnosticInfo(CommandGraphResourceType resourceType, uintptr_t resourceID); 180 getResourceTypeForDiagnostics()181 CommandGraphResourceType getResourceTypeForDiagnostics() const { return mResourceType; } getResourceIDForDiagnostics()182 uintptr_t getResourceIDForDiagnostics() const { return mResourceID; } 183 bool hasDiagnosticID() const; 184 std::string dumpCommandsForDiagnostics(const char *separator) const; 185 getRenderPassRenderArea()186 const gl::Rectangle &getRenderPassRenderArea() const { return mRenderPassRenderArea; } 187 getFunction()188 CommandGraphNodeFunction getFunction() const { return mFunction; } 189 190 void setQueryPool(const QueryPool *queryPool, uint32_t queryIndex); getQueryPool()191 VkQueryPool getQueryPool() const { return mQueryPool; } getQueryIndex()192 uint32_t getQueryIndex() const { return mQueryIndex; } 193 void setFenceSync(const vk::Event &event); 194 void setDebugMarker(GLenum source, std::string &&marker); getDebugMarker()195 const std::string &getDebugMarker() const { return mDebugMarker; } 196 addGlobalMemoryBarrier(VkFlags srcAccess,VkFlags dstAccess,VkPipelineStageFlags stages)197 ANGLE_INLINE void addGlobalMemoryBarrier(VkFlags srcAccess, 198 VkFlags dstAccess, 199 VkPipelineStageFlags stages) 200 { 201 mGlobalMemoryBarrierSrcAccess |= srcAccess; 202 mGlobalMemoryBarrierDstAccess |= dstAccess; 203 mGlobalMemoryBarrierStages |= stages; 204 } 205 206 // This can only be set for RenderPass nodes. Each RenderPass node can have at most one owner. setRenderPassOwner(RenderPassOwner * owner)207 void setRenderPassOwner(RenderPassOwner *owner) 208 { 209 ASSERT(mRenderPassOwner == nullptr); 210 mRenderPassOwner = owner; 211 } 212 213 private: setHasChildren()214 ANGLE_INLINE void setHasChildren() 215 { 216 mHasChildren = true; 217 if (mRenderPassOwner) 218 { 219 mRenderPassOwner->onRenderPassFinished(); 220 } 221 } 222 223 // Used for testing only. 224 bool isChildOf(CommandGraphNode *parent); 225 226 // Only used if we need a RenderPass for these commands. 227 RenderPassDesc mRenderPassDesc; 228 AttachmentOpsArray mRenderPassAttachmentOps; 229 Framebuffer mRenderPassFramebuffer; 230 gl::Rectangle mRenderPassRenderArea; 231 gl::AttachmentArray<VkClearValue> mRenderPassClearValues; 232 233 CommandGraphNodeFunction mFunction; 234 angle::PoolAllocator *mPoolAllocator; 235 // Keep separate buffers for commands inside and outside a RenderPass. 236 // TODO(jmadill): We might not need inside and outside RenderPass commands separate. 237 CommandBuffer mOutsideRenderPassCommands; 238 CommandBuffer mInsideRenderPassCommands; 239 240 // Special-function additional data: 241 // Queries: 242 VkQueryPool mQueryPool; 243 uint32_t mQueryIndex; 244 // GLsync and EGLSync: 245 VkEvent mFenceSyncEvent; 246 // Debug markers: 247 GLenum mDebugMarkerSource; 248 std::string mDebugMarker; 249 250 // Parents are commands that must be submitted before 'this' CommandNode can be submitted. 251 std::vector<CommandGraphNode *> mParents; 252 253 // If this is true, other commands exist that must be submitted after 'this' command. 254 bool mHasChildren; 255 256 // Used when traversing the dependency graph. 257 VisitedState mVisitedState; 258 259 // Additional diagnostic information. 260 CommandGraphResourceType mResourceType; 261 uintptr_t mResourceID; 262 263 // For global memory barriers. 264 VkFlags mGlobalMemoryBarrierSrcAccess; 265 VkFlags mGlobalMemoryBarrierDstAccess; 266 VkPipelineStageFlags mGlobalMemoryBarrierStages; 267 268 // Render pass command buffer notifications. 269 RenderPassOwner *mRenderPassOwner; 270 }; 271 272 // This is a helper class for back-end objects used in Vk command buffers. It records a serial 273 // at command recording times indicating an order in the queue. We use Fences to detect when 274 // commands finish, and then release any unreferenced and deleted resources based on the stored 275 // queue serial in a special 'garbage' queue. Resources also track current read and write 276 // dependencies. Only one command buffer node can be writing to the Resource at a time, but many 277 // can be reading from it. Together the dependencies will form a command graph at submission time. 278 class CommandGraphResource : angle::NonCopyable 279 { 280 public: 281 virtual ~CommandGraphResource(); 282 283 // Returns true if the resource is in use by the renderer. 284 bool isResourceInUse(ContextVk *context) const; 285 286 // Get the current queue serial for this resource. Used to release resources, and for 287 // queries, to know if the queue they are submitted on has finished execution. getStoredQueueSerial()288 Serial getStoredQueueSerial() const { return mStoredQueueSerial; } 289 290 // Sets up dependency relations. 'this' resource is the resource being written to. 291 void addWriteDependency(CommandGraphResource *writingResource); 292 293 // Sets up dependency relations. 'this' resource is the resource being read. 294 void addReadDependency(CommandGraphResource *readingResource); 295 296 // Updates the in-use serial tracked for this resource. Will clear dependencies if the resource 297 // was not used in this set of command nodes. updateQueueSerial(Serial queueSerial)298 ANGLE_INLINE void updateQueueSerial(Serial queueSerial) 299 { 300 ASSERT(queueSerial >= mStoredQueueSerial); 301 302 if (queueSerial > mStoredQueueSerial) 303 { 304 mCurrentWritingNode = nullptr; 305 mCurrentReadingNodes.clear(); 306 mStoredQueueSerial = queueSerial; 307 } 308 } 309 310 // Reset the current queue serial for this resource. Will clear dependencies if the resource 311 // was not used in this set of command nodes. 312 void resetQueueSerial(); 313 314 // Allocates a write node via getNewWriteNode and returns a started command buffer. 315 // The started command buffer will render outside of a RenderPass. 316 // Will append to an existing command buffer/graph node if possible. 317 angle::Result recordCommands(ContextVk *context, CommandBuffer **commandBufferOut); 318 319 // Begins a command buffer on the current graph node for in-RenderPass rendering. 320 // Called from FramebufferVk::startNewRenderPass and UtilsVk functions. 321 angle::Result beginRenderPass(ContextVk *contextVk, 322 const Framebuffer &framebuffer, 323 const gl::Rectangle &renderArea, 324 const RenderPassDesc &renderPassDesc, 325 const AttachmentOpsArray &renderPassAttachmentOps, 326 const std::vector<VkClearValue> &clearValues, 327 CommandBuffer **commandBufferOut); 328 329 // Checks if we're in a RenderPass without children. hasStartedRenderPass()330 bool hasStartedRenderPass() const 331 { 332 return hasChildlessWritingNode() && 333 mCurrentWritingNode->getInsideRenderPassCommands()->valid(); 334 } 335 336 // Checks if we're in a RenderPass that encompasses renderArea, returning true if so. Updates 337 // serial internally. Returns the started command buffer in commandBufferOut. appendToStartedRenderPass(Serial currentQueueSerial,const gl::Rectangle & renderArea,CommandBuffer ** commandBufferOut)338 ANGLE_INLINE bool appendToStartedRenderPass(Serial currentQueueSerial, 339 const gl::Rectangle &renderArea, 340 CommandBuffer **commandBufferOut) 341 { 342 updateQueueSerial(currentQueueSerial); 343 if (hasStartedRenderPass()) 344 { 345 if (mCurrentWritingNode->getRenderPassRenderArea().encloses(renderArea)) 346 { 347 *commandBufferOut = mCurrentWritingNode->getInsideRenderPassCommands(); 348 return true; 349 } 350 } 351 352 return false; 353 } 354 355 // Returns true if the render pass is started, but there are no commands yet recorded in it. 356 // This is useful to know if the render pass ops can be modified. renderPassStartedButEmpty()357 bool renderPassStartedButEmpty() const 358 { 359 return hasStartedRenderPass() && 360 (!vk::CommandBuffer::CanKnowIfEmpty() || 361 mCurrentWritingNode->getInsideRenderPassCommands()->empty()); 362 } 363 clearRenderPassColorAttachment(size_t attachmentIndex,const VkClearColorValue & clearValue)364 void clearRenderPassColorAttachment(size_t attachmentIndex, const VkClearColorValue &clearValue) 365 { 366 ASSERT(renderPassStartedButEmpty()); 367 mCurrentWritingNode->clearRenderPassColorAttachment(attachmentIndex, clearValue); 368 } 369 clearRenderPassDepthAttachment(size_t attachmentIndex,float depth)370 void clearRenderPassDepthAttachment(size_t attachmentIndex, float depth) 371 { 372 ASSERT(renderPassStartedButEmpty()); 373 mCurrentWritingNode->clearRenderPassDepthAttachment(attachmentIndex, depth); 374 } 375 clearRenderPassStencilAttachment(size_t attachmentIndex,uint32_t stencil)376 void clearRenderPassStencilAttachment(size_t attachmentIndex, uint32_t stencil) 377 { 378 ASSERT(renderPassStartedButEmpty()); 379 mCurrentWritingNode->clearRenderPassStencilAttachment(attachmentIndex, stencil); 380 } 381 invalidateRenderPassColorAttachment(size_t attachmentIndex)382 void invalidateRenderPassColorAttachment(size_t attachmentIndex) 383 { 384 ASSERT(hasStartedRenderPass()); 385 mCurrentWritingNode->invalidateRenderPassColorAttachment(attachmentIndex); 386 } 387 invalidateRenderPassDepthAttachment(size_t attachmentIndex)388 void invalidateRenderPassDepthAttachment(size_t attachmentIndex) 389 { 390 ASSERT(hasStartedRenderPass()); 391 mCurrentWritingNode->invalidateRenderPassDepthAttachment(attachmentIndex); 392 } 393 invalidateRenderPassStencilAttachment(size_t attachmentIndex)394 void invalidateRenderPassStencilAttachment(size_t attachmentIndex) 395 { 396 ASSERT(hasStartedRenderPass()); 397 mCurrentWritingNode->invalidateRenderPassStencilAttachment(attachmentIndex); 398 } 399 400 // Accessor for RenderPass RenderArea. getRenderPassRenderArea()401 const gl::Rectangle &getRenderPassRenderArea() const 402 { 403 ASSERT(hasStartedRenderPass()); 404 return mCurrentWritingNode->getRenderPassRenderArea(); 405 } 406 407 // Called when 'this' object changes, but we'd like to start a new command buffer later. 408 void finishCurrentCommands(ContextVk *contextVk); 409 410 // Store a deferred memory barrier. Will be recorded into a primary command buffer at submit. addGlobalMemoryBarrier(VkFlags srcAccess,VkFlags dstAccess,VkPipelineStageFlags stages)411 void addGlobalMemoryBarrier(VkFlags srcAccess, VkFlags dstAccess, VkPipelineStageFlags stages) 412 { 413 ASSERT(mCurrentWritingNode); 414 mCurrentWritingNode->addGlobalMemoryBarrier(srcAccess, dstAccess, stages); 415 } 416 417 protected: 418 explicit CommandGraphResource(CommandGraphResourceType resourceType); 419 420 private: 421 // Returns true if this node has a current writing node with no children. hasChildlessWritingNode()422 ANGLE_INLINE bool hasChildlessWritingNode() const 423 { 424 // Note: currently, we don't have a resource that can issue both generic and special 425 // commands. We don't create read/write dependencies between mixed generic/special 426 // resources either. As such, we expect the function to always be generic here. If such a 427 // resource is added in the future, this can add a check for function == generic and fail if 428 // false. 429 ASSERT(mCurrentWritingNode == nullptr || 430 mCurrentWritingNode->getFunction() == CommandGraphNodeFunction::Generic); 431 return (mCurrentWritingNode != nullptr && !mCurrentWritingNode->hasChildren()); 432 } 433 434 void startNewCommands(ContextVk *contextVk); 435 436 void onWriteImpl(CommandGraphNode *writingNode, Serial currentSerial); 437 438 Serial mStoredQueueSerial; 439 440 std::vector<CommandGraphNode *> mCurrentReadingNodes; 441 442 // Current command graph writing node. 443 CommandGraphNode *mCurrentWritingNode; 444 445 // Additional diagnostic information. 446 CommandGraphResourceType mResourceType; 447 }; 448 449 // Translating OpenGL commands into Vulkan and submitting them immediately loses out on some 450 // of the powerful flexiblity Vulkan offers in RenderPasses. Load/Store ops can automatically 451 // clear RenderPass attachments, or preserve the contents. RenderPass automatic layout transitions 452 // can improve certain performance cases. Also, we can remove redundant RenderPass Begin and Ends 453 // when processing interleaved draw operations on independent Framebuffers. 454 // 455 // ANGLE's CommandGraph (and CommandGraphNode) attempt to solve these problems using deferred 456 // command submission. We also sometimes call this command re-ordering. A brief summary: 457 // 458 // During GL command processing, we record Vulkan commands into SecondaryCommandBuffers, which 459 // are stored in CommandGraphNodes, and these nodes are chained together via dependencies to 460 // form a directed acyclic CommandGraph. When we need to submit the CommandGraph, say during a 461 // SwapBuffers or ReadPixels call, we begin a primary Vulkan CommandBuffer, and walk the 462 // CommandGraph, starting at the most senior nodes, recording SecondaryCommandBuffers inside 463 // and outside RenderPasses as necessary, filled with the right load/store operations. Once 464 // the primary CommandBuffer has recorded all of the SecondaryCommandBuffers from all the open 465 // CommandGraphNodes, we submit the primary CommandBuffer to the VkQueue on the device. 466 // 467 // The Command Graph consists of an array of open Command Graph Nodes. It supports allocating new 468 // nodes for the graph, which are linked via dependency relation calls in CommandGraphNode, and 469 // also submitting the whole command graph via submitCommands. 470 class CommandGraph final : angle::NonCopyable 471 { 472 public: 473 explicit CommandGraph(bool enableGraphDiagnostics, angle::PoolAllocator *poolAllocator); 474 ~CommandGraph(); 475 476 // Allocates a new CommandGraphNode and adds it to the list of current open nodes. No ordering 477 // relations exist in the node by default. Call CommandGraphNode::SetHappensBeforeDependency 478 // to set up dependency relations. If the node is a barrier, it will automatically add 479 // dependencies between the previous barrier, the new barrier and all nodes in between. 480 CommandGraphNode *allocateNode(CommandGraphNodeFunction function); 481 482 angle::Result submitCommands(ContextVk *context, 483 Serial serial, 484 RenderPassCache *renderPassCache, 485 PrimaryCommandBuffer *primaryCommandBuffer); 486 bool empty() const; 487 void clear(); 488 489 // The following create special-function nodes that don't require a graph resource. 490 // Queries: 491 void beginQuery(const QueryPool *queryPool, uint32_t queryIndex); 492 void endQuery(const QueryPool *queryPool, uint32_t queryIndex); 493 void writeTimestamp(const QueryPool *queryPool, uint32_t queryIndex); 494 void beginTransformFeedbackEmulatedQuery(); 495 void endTransformFeedbackEmulatedQuery(); 496 // GLsync and EGLSync: 497 void setFenceSync(const vk::Event &event); 498 void waitFenceSync(const vk::Event &event); 499 // Memory barriers: 500 void memoryBarrier(VkFlags srcAccess, VkFlags dstAccess, VkPipelineStageFlags stages); 501 // Debug markers: 502 void insertDebugMarker(GLenum source, std::string &&marker); 503 void pushDebugMarker(GLenum source, std::string &&marker); 504 void popDebugMarker(); 505 // Host-visible buffer write availability operation: 506 void makeHostVisibleBufferWriteAvailable(); 507 508 private: 509 CommandGraphNode *allocateBarrierNode(CommandGraphNodeFunction function, 510 CommandGraphResourceType resourceType, 511 uintptr_t resourceID); 512 void setNewBarrier(CommandGraphNode *newBarrier); 513 CommandGraphNode *getLastBarrierNode(size_t *indexOut); 514 void addDependenciesToNextBarrier(size_t begin, size_t end, CommandGraphNode *nextBarrier); 515 516 void dumpGraphDotFile(std::ostream &out) const; 517 518 std::vector<CommandGraphNode *> mNodes; 519 bool mEnableGraphDiagnostics; 520 angle::PoolAllocator *mPoolAllocator; 521 522 // A set of nodes (eventually) exist that act as barriers to guarantee submission order. For 523 // example, a glMemoryBarrier() calls would lead to such a barrier or beginning and ending a 524 // query. This is because the graph can reorder operations if it sees fit. Let's call a barrier 525 // node Bi, and the other nodes Ni. The edges between Ni don't interest us. Before a barrier is 526 // inserted, we have: 527 // 528 // N0 N1 ... Na 529 // \___\__/_/ (dependency egdes, which we don't care about so I'll stop drawing them. 530 // \/ 531 // 532 // When the first barrier is inserted, we will have: 533 // 534 // ______ 535 // / ____\ 536 // / / \ 537 // / / /\ 538 // N0 N1 ... Na B0 539 // 540 // This makes sure all N0..Na are called before B0. From then on, B0 will be the current 541 // "barrier point" which extends an edge to every next node: 542 // 543 // ______ 544 // / ____\ 545 // / / \ 546 // / / /\ 547 // N0 N1 ... Na B0 Na+1 ... Nb 548 // \/ / 549 // \______/ 550 // 551 // 552 // When the next barrier B1 is met, all nodes between B0 and B1 will add a depenency on B1 as 553 // well, and the "barrier point" is updated. 554 // 555 // ______ 556 // / ____\ ______ ______ 557 // / / \ / \ / \ 558 // / / /\ / /\ / /\ 559 // N0 N1 ... Na B0 Na+1 ... Nb B1 Nb+1 ... Nc B2 ... 560 // \/ / / \/ / / 561 // \______/ / \______/ / 562 // \_______/ \_______/ 563 // 564 // 565 // When barrier Bi is introduced, all nodes added since Bi-1 need to add a dependency to Bi 566 // (including Bi-1). We therefore keep track of the node index of the last barrier that was 567 // issued. 568 static constexpr size_t kInvalidNodeIndex = std::numeric_limits<std::size_t>::max(); 569 size_t mLastBarrierIndex; 570 }; 571 } // namespace vk 572 } // namespace rx 573 574 #endif // LIBANGLE_RENDERER_VULKAN_COMMAND_GRAPH_H_ 575