1 // 2 // Copyright 2020 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 // CommandProcessor.h: 7 // A class to process and submit Vulkan command buffers that can be 8 // used in an asynchronous worker thread. 9 // 10 11 #ifndef LIBANGLE_RENDERER_VULKAN_COMMAND_PROCESSOR_H_ 12 #define LIBANGLE_RENDERER_VULKAN_COMMAND_PROCESSOR_H_ 13 14 #include <condition_variable> 15 #include <mutex> 16 #include <queue> 17 #include <thread> 18 19 #include "common/vulkan/vk_headers.h" 20 #include "libANGLE/renderer/vulkan/PersistentCommandPool.h" 21 #include "libANGLE/renderer/vulkan/vk_helpers.h" 22 23 namespace rx 24 { 25 class RendererVk; 26 class CommandProcessor; 27 28 namespace vk 29 { 30 enum class SubmitPolicy 31 { 32 AllowDeferred, 33 EnsureSubmitted, 34 }; 35 36 class FenceRecycler 37 { 38 public: FenceRecycler()39 FenceRecycler() {} ~FenceRecycler()40 ~FenceRecycler() {} 41 void destroy(vk::Context *context); 42 43 angle::Result newSharedFence(vk::Context *context, vk::Shared<vk::Fence> *sharedFenceOut); resetSharedFence(vk::Shared<vk::Fence> * sharedFenceIn)44 inline void resetSharedFence(vk::Shared<vk::Fence> *sharedFenceIn) 45 { 46 std::lock_guard<std::mutex> lock(mMutex); 47 sharedFenceIn->resetAndRecycle(&mRecyler); 48 } 49 50 private: 51 std::mutex mMutex; 52 vk::Recycler<vk::Fence> mRecyler; 53 }; 54 55 enum class CustomTask 56 { 57 Invalid = 0, 58 // Process SecondaryCommandBuffer commands into the primary CommandBuffer. 59 ProcessCommands, 60 // End the current command buffer and submit commands to the queue 61 FlushAndQueueSubmit, 62 // Submit custom command buffer, excludes some state management 63 OneOffQueueSubmit, 64 // Finish queue commands up to given serial value, process garbage 65 FinishToSerial, 66 // Execute QueuePresent 67 Present, 68 // do cleanup processing on completed commands 69 // TODO: https://issuetracker.google.com/170312581 - should be able to remove 70 // checkCompletedCommands command with fence refactor. 71 CheckCompletedCommands, 72 // Exit the command processor thread 73 Exit, 74 }; 75 76 // CommandProcessorTask interface 77 class CommandProcessorTask 78 { 79 public: CommandProcessorTask()80 CommandProcessorTask() { initTask(); } 81 82 void initTask(); 83 initTask(CustomTask command)84 void initTask(CustomTask command) { mTask = command; } 85 86 void initProcessCommands(bool hasProtectedContent, 87 CommandBufferHelper *commandBuffer, 88 const RenderPass *renderPass); 89 90 void initPresent(egl::ContextPriority priority, const VkPresentInfoKHR &presentInfo); 91 92 void initFinishToSerial(Serial serial); 93 94 void initFlushAndQueueSubmit(const std::vector<VkSemaphore> &waitSemaphores, 95 const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks, 96 const Semaphore *semaphore, 97 bool hasProtectedContent, 98 egl::ContextPriority priority, 99 GarbageList &¤tGarbage, 100 Serial submitQueueSerial); 101 102 void initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle, 103 bool hasProtectedContent, 104 egl::ContextPriority priority, 105 const Fence *fence, 106 Serial submitQueueSerial); 107 108 CommandProcessorTask &operator=(CommandProcessorTask &&rhs); 109 CommandProcessorTask(CommandProcessorTask && other)110 CommandProcessorTask(CommandProcessorTask &&other) : CommandProcessorTask() 111 { 112 *this = std::move(other); 113 } 114 setQueueSerial(Serial serial)115 void setQueueSerial(Serial serial) { mSerial = serial; } getQueueSerial()116 Serial getQueueSerial() const { return mSerial; } getTaskCommand()117 CustomTask getTaskCommand() { return mTask; } getWaitSemaphores()118 std::vector<VkSemaphore> &getWaitSemaphores() { return mWaitSemaphores; } getWaitSemaphoreStageMasks()119 std::vector<VkPipelineStageFlags> &getWaitSemaphoreStageMasks() 120 { 121 return mWaitSemaphoreStageMasks; 122 } getSemaphore()123 const Semaphore *getSemaphore() { return mSemaphore; } getGarbage()124 GarbageList &getGarbage() { return mGarbage; } getPriority()125 egl::ContextPriority getPriority() const { return mPriority; } hasProtectedContent()126 bool hasProtectedContent() const { return mHasProtectedContent; } getOneOffCommandBufferVk()127 VkCommandBuffer getOneOffCommandBufferVk() const { return mOneOffCommandBufferVk; } getOneOffFence()128 const Fence *getOneOffFence() { return mOneOffFence; } getPresentInfo()129 const VkPresentInfoKHR &getPresentInfo() const { return mPresentInfo; } getRenderPass()130 const RenderPass *getRenderPass() const { return mRenderPass; } getCommandBuffer()131 CommandBufferHelper *getCommandBuffer() const { return mCommandBuffer; } 132 133 private: 134 void copyPresentInfo(const VkPresentInfoKHR &other); 135 136 CustomTask mTask; 137 138 // ProcessCommands 139 const RenderPass *mRenderPass; 140 CommandBufferHelper *mCommandBuffer; 141 142 // Flush data 143 std::vector<VkSemaphore> mWaitSemaphores; 144 std::vector<VkPipelineStageFlags> mWaitSemaphoreStageMasks; 145 const Semaphore *mSemaphore; 146 GarbageList mGarbage; 147 148 // FinishToSerial & Flush command data 149 Serial mSerial; 150 151 // Present command data 152 VkPresentInfoKHR mPresentInfo; 153 VkSwapchainKHR mSwapchain; 154 VkSemaphore mWaitSemaphore; 155 uint32_t mImageIndex; 156 // Used by Present if supportsIncrementalPresent is enabled 157 VkPresentRegionKHR mPresentRegion; 158 VkPresentRegionsKHR mPresentRegions; 159 std::vector<VkRectLayerKHR> mRects; 160 161 // Used by OneOffQueueSubmit 162 VkCommandBuffer mOneOffCommandBufferVk; 163 const Fence *mOneOffFence; 164 165 // Flush, Present & QueueWaitIdle data 166 egl::ContextPriority mPriority; 167 bool mHasProtectedContent; 168 }; 169 170 struct CommandBatch final : angle::NonCopyable 171 { 172 CommandBatch(); 173 ~CommandBatch(); 174 CommandBatch(CommandBatch &&other); 175 CommandBatch &operator=(CommandBatch &&other); 176 177 void destroy(VkDevice device); 178 179 PrimaryCommandBuffer primaryCommands; 180 // commandPool is for secondary CommandBuffer allocation 181 CommandPool commandPool; 182 Shared<Fence> fence; 183 Serial serial; 184 bool hasProtectedContent; 185 }; 186 187 class DeviceQueueMap; 188 189 class QueueFamily final : angle::NonCopyable 190 { 191 public: 192 static const uint32_t kInvalidIndex = std::numeric_limits<uint32_t>::max(); 193 194 static uint32_t FindIndex(const std::vector<VkQueueFamilyProperties> &queueFamilyProperties, 195 VkQueueFlags flags, 196 int32_t matchNumber, // 0 = first match, 1 = second match ... 197 uint32_t *matchCount); 198 static const uint32_t kQueueCount = static_cast<uint32_t>(egl::ContextPriority::EnumCount); 199 static const float kQueuePriorities[static_cast<uint32_t>(egl::ContextPriority::EnumCount)]; 200 QueueFamily()201 QueueFamily() : mProperties{}, mIndex(kInvalidIndex) {} ~QueueFamily()202 ~QueueFamily() {} 203 204 void initialize(const VkQueueFamilyProperties &queueFamilyProperties, uint32_t index); valid()205 bool valid() const { return (mIndex != kInvalidIndex); } getIndex()206 uint32_t getIndex() const { return mIndex; } getProperties()207 const VkQueueFamilyProperties *getProperties() const { return &mProperties; } isGraphics()208 bool isGraphics() const { return ((mProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) > 0); } isCompute()209 bool isCompute() const { return ((mProperties.queueFlags & VK_QUEUE_COMPUTE_BIT) > 0); } supportsProtected()210 bool supportsProtected() const 211 { 212 return ((mProperties.queueFlags & VK_QUEUE_PROTECTED_BIT) > 0); 213 } getDeviceQueueCount()214 uint32_t getDeviceQueueCount() const { return mProperties.queueCount; } 215 216 DeviceQueueMap initializeQueueMap(VkDevice device, 217 bool makeProtected, 218 uint32_t queueIndex, 219 uint32_t queueCount); 220 221 private: 222 VkQueueFamilyProperties mProperties; 223 uint32_t mIndex; 224 225 void getDeviceQueue(VkDevice device, bool makeProtected, uint32_t queueIndex, VkQueue *queue); 226 }; 227 228 class DeviceQueueMap : public angle::PackedEnumMap<egl::ContextPriority, VkQueue> 229 { 230 friend QueueFamily; 231 232 public: DeviceQueueMap()233 DeviceQueueMap() : mIndex(vk::QueueFamily::kInvalidIndex), mIsProtected(false) {} DeviceQueueMap(uint32_t queueFamilyIndex,bool isProtected)234 DeviceQueueMap(uint32_t queueFamilyIndex, bool isProtected) 235 : mIndex(queueFamilyIndex), mIsProtected(isProtected) 236 {} 237 ~DeviceQueueMap(); 238 DeviceQueueMap &operator=(const DeviceQueueMap &other); 239 valid()240 bool valid() const { return (mIndex != QueueFamily::kInvalidIndex); } getIndex()241 uint32_t getIndex() const { return mIndex; } isProtected()242 bool isProtected() const { return mIsProtected; } 243 egl::ContextPriority getDevicePriority(egl::ContextPriority priority) const; 244 245 private: 246 uint32_t mIndex; 247 bool mIsProtected; 248 angle::PackedEnumMap<egl::ContextPriority, egl::ContextPriority> mPriorities; 249 }; 250 251 class CommandQueueInterface : angle::NonCopyable 252 { 253 public: ~CommandQueueInterface()254 virtual ~CommandQueueInterface() {} 255 256 virtual angle::Result init(Context *context, const DeviceQueueMap &queueMap) = 0; 257 virtual void destroy(Context *context) = 0; 258 259 virtual void handleDeviceLost(RendererVk *renderer) = 0; 260 261 // Wait until the desired serial has been completed. 262 virtual angle::Result finishToSerial(Context *context, 263 Serial finishSerial, 264 uint64_t timeout) = 0; 265 virtual Serial reserveSubmitSerial() = 0; 266 virtual angle::Result submitFrame( 267 Context *context, 268 bool hasProtectedContent, 269 egl::ContextPriority priority, 270 const std::vector<VkSemaphore> &waitSemaphores, 271 const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks, 272 const Semaphore *signalSemaphore, 273 GarbageList &¤tGarbage, 274 CommandPool *commandPool, 275 Serial submitQueueSerial) = 0; 276 virtual angle::Result queueSubmitOneOff(Context *context, 277 bool hasProtectedContent, 278 egl::ContextPriority contextPriority, 279 VkCommandBuffer commandBufferHandle, 280 const Fence *fence, 281 SubmitPolicy submitPolicy, 282 Serial submitQueueSerial) = 0; 283 virtual VkResult queuePresent(egl::ContextPriority contextPriority, 284 const VkPresentInfoKHR &presentInfo) = 0; 285 286 virtual angle::Result waitForSerialWithUserTimeout(vk::Context *context, 287 Serial serial, 288 uint64_t timeout, 289 VkResult *result) = 0; 290 291 // Check to see which batches have finished completion (forward progress for 292 // the last completed serial, for example for when the application busy waits on a query 293 // result). It would be nice if we didn't have to expose this for QueryVk::getResult. 294 virtual angle::Result checkCompletedCommands(Context *context) = 0; 295 296 virtual angle::Result flushOutsideRPCommands(Context *context, 297 bool hasProtectedContent, 298 CommandBufferHelper **outsideRPCommands) = 0; 299 virtual angle::Result flushRenderPassCommands(Context *context, 300 bool hasProtectedContent, 301 const RenderPass &renderPass, 302 CommandBufferHelper **renderPassCommands) = 0; 303 304 virtual Serial getLastSubmittedQueueSerial() const = 0; 305 virtual Serial getLastCompletedQueueSerial() const = 0; 306 virtual Serial getCurrentQueueSerial() const = 0; 307 }; 308 309 class CommandQueue final : public CommandQueueInterface 310 { 311 public: 312 CommandQueue(); 313 ~CommandQueue() override; 314 315 angle::Result init(Context *context, const DeviceQueueMap &queueMap) override; 316 void destroy(Context *context) override; 317 void clearAllGarbage(RendererVk *renderer); 318 319 void handleDeviceLost(RendererVk *renderer) override; 320 321 angle::Result finishToSerial(Context *context, Serial finishSerial, uint64_t timeout) override; 322 323 Serial reserveSubmitSerial() override; 324 325 angle::Result submitFrame(Context *context, 326 bool hasProtectedContent, 327 egl::ContextPriority priority, 328 const std::vector<VkSemaphore> &waitSemaphores, 329 const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks, 330 const Semaphore *signalSemaphore, 331 GarbageList &¤tGarbage, 332 CommandPool *commandPool, 333 Serial submitQueueSerial) override; 334 335 angle::Result queueSubmitOneOff(Context *context, 336 bool hasProtectedContent, 337 egl::ContextPriority contextPriority, 338 VkCommandBuffer commandBufferHandle, 339 const Fence *fence, 340 SubmitPolicy submitPolicy, 341 Serial submitQueueSerial) override; 342 343 VkResult queuePresent(egl::ContextPriority contextPriority, 344 const VkPresentInfoKHR &presentInfo) override; 345 346 angle::Result waitForSerialWithUserTimeout(vk::Context *context, 347 Serial serial, 348 uint64_t timeout, 349 VkResult *result) override; 350 351 angle::Result checkCompletedCommands(Context *context) override; 352 353 angle::Result flushOutsideRPCommands(Context *context, 354 bool hasProtectedContent, 355 CommandBufferHelper **outsideRPCommands) override; 356 angle::Result flushRenderPassCommands(Context *context, 357 bool hasProtectedContent, 358 const RenderPass &renderPass, 359 CommandBufferHelper **renderPassCommands) override; 360 361 Serial getLastSubmittedQueueSerial() const override; 362 Serial getLastCompletedQueueSerial() const override; 363 Serial getCurrentQueueSerial() const override; 364 365 angle::Result queueSubmit(Context *context, 366 egl::ContextPriority contextPriority, 367 const VkSubmitInfo &submitInfo, 368 const Fence *fence, 369 Serial submitQueueSerial); 370 getDriverPriority(egl::ContextPriority priority)371 egl::ContextPriority getDriverPriority(egl::ContextPriority priority) 372 { 373 return mQueueMap.getDevicePriority(priority); 374 } 375 376 private: 377 angle::Result releaseToCommandBatch(Context *context, 378 bool hasProtectedContent, 379 PrimaryCommandBuffer &&commandBuffer, 380 CommandPool *commandPool, 381 CommandBatch *batch); 382 angle::Result retireFinishedCommands(Context *context, size_t finishedCount); 383 angle::Result ensurePrimaryCommandBufferValid(Context *context, bool hasProtectedContent); 384 385 bool allInFlightCommandsAreAfterSerial(Serial serial); 386 getQueue(egl::ContextPriority priority)387 VkQueue getQueue(egl::ContextPriority priority) { return mQueueMap[priority]; } 388 getCommandBuffer(bool hasProtectedContent)389 PrimaryCommandBuffer &getCommandBuffer(bool hasProtectedContent) 390 { 391 if (hasProtectedContent) 392 { 393 return mProtectedCommands; 394 } 395 else 396 { 397 return mPrimaryCommands; 398 } 399 } 400 getCommandPool(bool hasProtectedContent)401 PersistentCommandPool &getCommandPool(bool hasProtectedContent) 402 { 403 if (hasProtectedContent) 404 { 405 return mProtectedCommandPool; 406 } 407 else 408 { 409 return mPrimaryCommandPool; 410 } 411 } 412 413 GarbageQueue mGarbageQueue; 414 415 std::vector<CommandBatch> mInFlightCommands; 416 417 // Keeps a free list of reusable primary command buffers. 418 PrimaryCommandBuffer mPrimaryCommands; 419 PersistentCommandPool mPrimaryCommandPool; 420 PrimaryCommandBuffer mProtectedCommands; 421 PersistentCommandPool mProtectedCommandPool; 422 423 // Queue serial management. 424 AtomicSerialFactory mQueueSerialFactory; 425 Serial mLastCompletedQueueSerial; 426 Serial mLastSubmittedQueueSerial; 427 Serial mCurrentQueueSerial; 428 429 // QueueMap 430 DeviceQueueMap mQueueMap; 431 432 FenceRecycler mFenceRecycler; 433 }; 434 435 // CommandProcessor is used to dispatch work to the GPU when the asyncCommandQueue feature is 436 // enabled. Issuing the |destroy| command will cause the worker thread to clean up it's resources 437 // and shut down. This command is sent when the renderer instance shuts down. Tasks are defined by 438 // the CommandQueue interface. 439 440 class CommandProcessor : public Context, public CommandQueueInterface 441 { 442 public: 443 CommandProcessor(RendererVk *renderer); 444 ~CommandProcessor() override; 445 446 // Used by main thread to wait for worker thread to complete all outstanding work. 447 // TODO(jmadill): Make private. b/172704839 448 angle::Result waitForWorkComplete(Context *context); 449 angle::Result finishAllWork(Context *context); 450 getLastPresentResult(VkSwapchainKHR swapchain)451 VkResult getLastPresentResult(VkSwapchainKHR swapchain) 452 { 453 return getLastAndClearPresentResult(swapchain); 454 } 455 456 // vk::Context 457 void handleError(VkResult result, 458 const char *file, 459 const char *function, 460 unsigned int line) override; 461 462 // CommandQueueInterface 463 angle::Result init(Context *context, const DeviceQueueMap &qeueMap) override; 464 465 void destroy(Context *context) override; 466 467 void handleDeviceLost(RendererVk *renderer) override; 468 469 angle::Result finishToSerial(Context *context, Serial finishSerial, uint64_t timeout) override; 470 471 Serial reserveSubmitSerial() override; 472 473 angle::Result submitFrame(Context *context, 474 bool hasProtectedContent, 475 egl::ContextPriority priority, 476 const std::vector<VkSemaphore> &waitSemaphores, 477 const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks, 478 const Semaphore *signalSemaphore, 479 GarbageList &¤tGarbage, 480 CommandPool *commandPool, 481 Serial submitQueueSerial) override; 482 483 angle::Result queueSubmitOneOff(Context *context, 484 bool hasProtectedContent, 485 egl::ContextPriority contextPriority, 486 VkCommandBuffer commandBufferHandle, 487 const Fence *fence, 488 SubmitPolicy submitPolicy, 489 Serial submitQueueSerial) override; 490 VkResult queuePresent(egl::ContextPriority contextPriority, 491 const VkPresentInfoKHR &presentInfo) override; 492 493 angle::Result waitForSerialWithUserTimeout(vk::Context *context, 494 Serial serial, 495 uint64_t timeout, 496 VkResult *result) override; 497 498 angle::Result checkCompletedCommands(Context *context) override; 499 500 angle::Result flushOutsideRPCommands(Context *context, 501 bool hasProtectedContent, 502 CommandBufferHelper **outsideRPCommands) override; 503 angle::Result flushRenderPassCommands(Context *context, 504 bool hasProtectedContent, 505 const RenderPass &renderPass, 506 CommandBufferHelper **renderPassCommands) override; 507 508 Serial getLastSubmittedQueueSerial() const override; 509 Serial getLastCompletedQueueSerial() const override; 510 Serial getCurrentQueueSerial() const override; 511 512 private: hasPendingError()513 bool hasPendingError() const 514 { 515 std::lock_guard<std::mutex> queueLock(mErrorMutex); 516 return !mErrors.empty(); 517 } 518 angle::Result checkAndPopPendingError(Context *errorHandlingContext); 519 520 // Entry point for command processor thread, calls processTasksImpl to do the 521 // work. called by RendererVk::initializeDevice on main thread 522 void processTasks(); 523 524 // Called asynchronously from main thread to queue work that is then processed by the worker 525 // thread 526 void queueCommand(CommandProcessorTask &&task); 527 528 // Command processor thread, called by processTasks. The loop waits for work to 529 // be submitted from a separate thread. 530 angle::Result processTasksImpl(bool *exitThread); 531 532 // Command processor thread, process a task 533 angle::Result processTask(CommandProcessorTask *task); 534 535 VkResult getLastAndClearPresentResult(VkSwapchainKHR swapchain); 536 VkResult present(egl::ContextPriority priority, const VkPresentInfoKHR &presentInfo); 537 538 std::queue<CommandProcessorTask> mTasks; 539 mutable std::mutex mWorkerMutex; 540 // Signal worker thread when work is available 541 std::condition_variable mWorkAvailableCondition; 542 // Signal main thread when all work completed 543 mutable std::condition_variable mWorkerIdleCondition; 544 // Track worker thread Idle state for assertion purposes 545 bool mWorkerThreadIdle; 546 // Command pool to allocate processor thread primary command buffers from 547 CommandPool mCommandPool; 548 CommandQueue mCommandQueue; 549 550 mutable std::mutex mQueueSerialMutex; 551 552 mutable std::mutex mErrorMutex; 553 std::queue<Error> mErrors; 554 555 // Track present info 556 std::mutex mSwapchainStatusMutex; 557 std::condition_variable mSwapchainStatusCondition; 558 std::map<VkSwapchainKHR, VkResult> mSwapchainStatus; 559 560 // Command queue worker thread. 561 std::thread mTaskThread; 562 }; 563 564 } // namespace vk 565 566 } // namespace rx 567 568 #endif // LIBANGLE_RENDERER_VULKAN_COMMAND_PROCESSOR_H_ 569