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