| // Copyright 2012 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_ | |
| #define GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_ | |
| #include <stdint.h> | |
| #include <array> | |
| #include <functional> | |
| #include <memory> | |
| #include <queue> | |
| #include <vector> | |
| #include "base/atomic_sequence_num.h" | |
| #include "base/check.h" | |
| #include "base/containers/flat_map.h" | |
| #include "base/containers/queue.h" | |
| #include "base/functional/callback.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/memory/ref_counted.h" | |
| #include "base/memory/scoped_refptr.h" | |
| #include "base/rand_util.h" | |
| #include "base/synchronization/atomic_flag.h" | |
| #include "base/synchronization/lock.h" | |
| #include "base/thread_annotations.h" | |
| #include "base/threading/thread_checker.h" | |
| #include "gpu/command_buffer/common/command_buffer_id.h" | |
| #include "gpu/command_buffer/common/constants.h" | |
| #include "gpu/command_buffer/common/sync_token.h" | |
| #include "gpu/command_buffer/service/gpu_command_buffer_service_export.h" | |
| #include "gpu/command_buffer/service/sequence_id.h" | |
| namespace gpu { | |
| class SyncPointClient; | |
| class SyncPointClientState; | |
| class SyncPointManager; | |
| // The cause of fence sync releases. | |
| // | |
| // These values are logged to UMA. Entries should not be renumbered and | |
| // numeric values should never be reused. Please keep in sync with ReleaseCause | |
| // in tools/metrics/histograms/metadata/gpu/enums.xml | |
| enum class ReleaseCause { | |
| // Releases done by clients explicitly during task execution. | |
| kExplicitClientRelease = 0, | |
| // Releases done automatically at task completion, according to task info | |
| // specified by clients. | |
| kTaskCompletionRelease = 1, | |
| // Releases done forcefully to resolve invalid waits. | |
| kForceRelease = 2, | |
| kMaxValue = kForceRelease | |
| }; | |
| class GPU_COMMAND_BUFFER_SERVICE_EXPORT SyncPointOrderData | |
| : public base::RefCountedThreadSafe<SyncPointOrderData> { | |
| public: | |
| SyncPointOrderData(const SyncPointOrderData&) = delete; | |
| SyncPointOrderData& operator=(const SyncPointOrderData&) = delete; | |
| // Helper function that calls SyncPointManager::DestroySyncPointOrderData. | |
| void Destroy() LOCKS_EXCLUDED(lock_); | |
| SequenceId sequence_id() { return sequence_id_; } | |
| uint64_t processed_order_num() const { | |
| base::AutoLock auto_lock(lock_); | |
| return processed_order_num_; | |
| } | |
| uint64_t unprocessed_order_num() const { | |
| base::AutoLock auto_lock(lock_); | |
| return last_unprocessed_order_num_; | |
| } | |
| uint64_t current_order_num() const { | |
| DCHECK(processing_thread_checker_.CalledOnValidThread()); | |
| return current_order_num_; | |
| } | |
| bool IsProcessingOrderNumber() { | |
| DCHECK(processing_thread_checker_.CalledOnValidThread()); | |
| return !paused_ && current_order_num_ > processed_order_num(); | |
| } | |
| uint64_t GenerateUnprocessedOrderNumber(); | |
| void BeginProcessingOrderNumber(uint64_t order_num); | |
| void PauseProcessingOrderNumber(uint64_t order_num); | |
| void FinishProcessingOrderNumber(uint64_t order_num); | |
| private: | |
| friend class base::RefCountedThreadSafe<SyncPointOrderData>; | |
| friend class SyncPointManager; | |
| friend class SyncPointClientState; | |
| struct OrderFence { | |
| uint64_t order_num; | |
| uint64_t fence_release; | |
| scoped_refptr<SyncPointClientState> client_state; | |
| // ID that is unique to the particular SyncPointOrderData. | |
| uint64_t callback_id; | |
| OrderFence(uint64_t order, | |
| uint64_t release, | |
| scoped_refptr<SyncPointClientState> state, | |
| uint64_t callback_id); | |
| OrderFence(const OrderFence& other); | |
| ~OrderFence(); | |
| bool operator>(const OrderFence& rhs) const { | |
| return std::tie(order_num, fence_release) > | |
| std::tie(rhs.order_num, rhs.fence_release); | |
| } | |
| }; | |
| typedef std:: | |
| priority_queue<OrderFence, std::vector<OrderFence>, std::greater<>> | |
| OrderFenceQueue; | |
| SyncPointOrderData(SyncPointManager* sync_point_manager, | |
| SequenceId seqeunce_id); | |
| ~SyncPointOrderData(); | |
| // Called by SyncPointManager after it has removed this SyncPointerOrderData | |
| // from its order_data_map_. | |
| void DestroyInternal() LOCKS_EXCLUDED(lock_); | |
| // Returns callback_id for created OrderFence on success, 0 on failure. | |
| uint64_t ValidateReleaseOrderNumber( | |
| scoped_refptr<SyncPointClientState> client_state, | |
| uint64_t wait_order_num, | |
| uint64_t fence_release) LOCKS_EXCLUDED(lock_); | |
| const raw_ptr<SyncPointManager> sync_point_manager_; | |
| const SequenceId sequence_id_; | |
| // Non thread-safe functions need to be called from a single thread. | |
| base::ThreadChecker processing_thread_checker_; | |
| // Current IPC order number being processed (only used on processing thread). | |
| uint64_t current_order_num_ = 0; | |
| // Whether or not the current order number is being processed or paused. | |
| bool paused_ = false; | |
| mutable base::Lock lock_; | |
| bool destroyed_ GUARDED_BY(lock_) = false; | |
| // Last finished IPC order number. | |
| uint64_t processed_order_num_ GUARDED_BY(lock_) = 0; | |
| // Last unprocessed order number. Updated in GenerateUnprocessedOrderNumber. | |
| uint64_t last_unprocessed_order_num_ GUARDED_BY(lock_) = 0; | |
| // Queue of unprocessed order numbers. Order numbers are enqueued in | |
| // GenerateUnprocessedOrderNumber, and dequeued in | |
| // FinishProcessingOrderNumber. | |
| base::queue<uint64_t> unprocessed_order_nums_ GUARDED_BY(lock_); | |
| // This variable is only used when graph-based validation is disabled. | |
| // | |
| // In situations where we are waiting on fence syncs that do not exist, we | |
| // validate by making sure the order number does not pass the order number | |
| // which the wait command was issued. If the order number reaches the | |
| // wait command's, we should automatically release up to the expected | |
| // release count. Note that this also releases other lower release counts, | |
| // so a single misbehaved fence sync is enough to invalidate/signal all | |
| // previous fence syncs. All order numbers (n) in order_fence_queue_ must | |
| // follow the invariant: | |
| // unprocessed_order_nums_.front() < n <= unprocessed_order_nums_.back(). | |
| OrderFenceQueue order_fence_queue_ GUARDED_BY(lock_); | |
| }; | |
| class GPU_COMMAND_BUFFER_SERVICE_EXPORT SyncPointClientState | |
| : public base::RefCountedThreadSafe<SyncPointClientState> { | |
| public: | |
| SyncPointClientState(const SyncPointClientState&) = delete; | |
| SyncPointClientState& operator=(const SyncPointClientState&) = delete; | |
| // Calls SyncPointManager::DestroySyncPointClientState. | |
| void Destroy() LOCKS_EXCLUDED(fence_sync_lock_); | |
| CommandBufferNamespace namespace_id() const { return namespace_id_; } | |
| CommandBufferId command_buffer_id() const { return command_buffer_id_; } | |
| SequenceId sequence_id() const { return order_data_->sequence_id(); } | |
| private: | |
| friend class base::RefCountedThreadSafe<SyncPointClientState>; | |
| friend class SyncPointManager; | |
| friend class SyncPointOrderData; | |
| struct ReleaseCallback { | |
| uint64_t release_count; | |
| base::OnceClosure callback_closure; | |
| uint64_t callback_id; | |
| ReleaseCallback(uint64_t release, | |
| base::OnceClosure callback, | |
| uint64_t callback_id); | |
| ReleaseCallback(ReleaseCallback&& other); | |
| ~ReleaseCallback(); | |
| ReleaseCallback& operator=(ReleaseCallback&& other) = default; | |
| bool operator>(const ReleaseCallback& rhs) const { | |
| return release_count > rhs.release_count; | |
| } | |
| }; | |
| typedef std::priority_queue<ReleaseCallback, | |
| std::vector<ReleaseCallback>, | |
| std::greater<>> | |
| ReleaseCallbackQueue; | |
| SyncPointClientState(SyncPointManager* sync_point_manager, | |
| scoped_refptr<SyncPointOrderData> order_data, | |
| CommandBufferNamespace namespace_id, | |
| CommandBufferId command_buffer_id); | |
| ~SyncPointClientState(); | |
| std::vector<base::OnceClosure> DestroyAndReturnCallbacks() | |
| LOCKS_EXCLUDED(fence_sync_lock_); | |
| // Returns true if fence sync has been released. | |
| bool IsFenceSyncReleased(uint64_t release) LOCKS_EXCLUDED(fence_sync_lock_); | |
| // Queues the callback to be called if the release is valid. If the release | |
| // is invalid this function will return False and the callback will never | |
| // be called. | |
| bool WaitForRelease(uint64_t release, | |
| uint64_t wait_order_num, | |
| base::OnceClosure callback) | |
| LOCKS_EXCLUDED(fence_sync_lock_); | |
| // Does not release the fence sync, but releases callbacks waiting on that | |
| // fence sync. | |
| void EnsureWaitReleased(uint64_t release, uint64_t callback_id) | |
| LOCKS_EXCLUDED(fence_sync_lock_); | |
| void EnsureFenceSyncReleased(uint64_t release, ReleaseCause cause) | |
| LOCKS_EXCLUDED(fence_sync_lock_); | |
| // Sync point manager is guaranteed to exist in the lifetime of the client. | |
| const raw_ptr<SyncPointManager> sync_point_manager_; | |
| // Global order data where releases will originate from. | |
| const scoped_refptr<SyncPointOrderData> order_data_; | |
| // Unique namespace/client id pair for this sync point client. | |
| const CommandBufferNamespace namespace_id_; | |
| const CommandBufferId command_buffer_id_; | |
| // Protects fence_sync_release_, fence_callback_queue_. | |
| base::Lock fence_sync_lock_; | |
| base::AtomicFlag destroyed_; | |
| // Current fence sync release that has been signaled. | |
| uint64_t fence_sync_release_ GUARDED_BY(fence_sync_lock_) = 0; | |
| // The fence sync release that has been signaled by clients, including both | |
| // ReleaseCause::kExplicitClientRelease and | |
| // ReleaseCause::kTaskCompletionRelease. | |
| // It is always true that | |
| // `client_fence_sync_release_` <= `fence_sync_release_`. | |
| // This variable is used to check that clients don't submit out of order | |
| // releases. | |
| uint64_t client_fence_sync_release_ GUARDED_BY(fence_sync_lock_) = 0; | |
| // In well defined fence sync operations, fence syncs are released in order | |
| // so simply having a priority queue for callbacks is enough. | |
| ReleaseCallbackQueue release_callback_queue_ GUARDED_BY(fence_sync_lock_); | |
| }; | |
| // This class manages the sync points, which allow cross-channel | |
| // synchronization. | |
| class GPU_COMMAND_BUFFER_SERVICE_EXPORT SyncPointManager { | |
| public: | |
| SyncPointManager(); | |
| SyncPointManager(const SyncPointManager&) = delete; | |
| SyncPointManager& operator=(const SyncPointManager&) = delete; | |
| ~SyncPointManager(); | |
| scoped_refptr<SyncPointOrderData> CreateSyncPointOrderData(); | |
| scoped_refptr<SyncPointClientState> CreateSyncPointClientState( | |
| CommandBufferNamespace namespace_id, | |
| CommandBufferId command_buffer_id, | |
| SequenceId sequence_id); | |
| // Returns true if the sync token has been released or if the command | |
| // buffer does not exist. | |
| bool IsSyncTokenReleased(const SyncToken& sync_token) LOCKS_EXCLUDED(lock_); | |
| // Returns the sequence ID that will release this sync token. | |
| SequenceId GetSyncTokenReleaseSequenceId(const SyncToken& sync_token) | |
| LOCKS_EXCLUDED(lock_); | |
| // Returns the global last processed order number. | |
| uint64_t GetProcessedOrderNum() const LOCKS_EXCLUDED(lock_); | |
| // // Returns the global last unprocessed order number. | |
| uint64_t GetUnprocessedOrderNum() const LOCKS_EXCLUDED(lock_); | |
| // If the wait is valid (sync token hasn't been processed or command buffer | |
| // does not exist), the callback is queued to run when the sync point is | |
| // released. If the wait is invalid, the callback is NOT run. The callback | |
| // runs on the thread the sync point is released. Clients should use | |
| // SyncPointClient::Wait because that uses order data to prevent deadlocks. | |
| // | |
| // Note: Should only be used to implement gpu::TaskGraph and its executors | |
| // (e.g., gpu::Scheduler, gpu::BlockingSequenceRunner). | |
| bool Wait(const SyncToken& sync_token, | |
| SequenceId sequence_id, | |
| uint64_t wait_order_num, | |
| base::OnceClosure callback) LOCKS_EXCLUDED(lock_); | |
| // Used by SyncPointOrderData. | |
| uint64_t GenerateOrderNumber(); | |
| // Is called by SyncPointOrderData::Destroy to remove `order_data` from | |
| // client_state_map_. | |
| void RemoveSyncPointOrderData(scoped_refptr<SyncPointOrderData> order_data) | |
| LOCKS_EXCLUDED(lock_); | |
| // Grabs any remaining callbacks in |client_state|'s release queue, destroys | |
| // |client_state|, then runs those remaining callbacks. | |
| void DestroySyncPointClientState( | |
| scoped_refptr<SyncPointClientState> client_state) | |
| LOCKS_EXCLUDED(lock_, client_state->fence_sync_lock_); | |
| // Ensures release count reaches `release`. | |
| // | |
| // Note: Should only be used to implement gpu::TaskGraph and its executors | |
| // (e.g., gpu::Scheduler, gpu::BlockingSequenceRunner). | |
| void EnsureFenceSyncReleased(const SyncToken& release, ReleaseCause cause) | |
| LOCKS_EXCLUDED(lock_); | |
| // Whether to rely on gpu::TaskGraph (instead of SyncPointOrderData) to | |
| // perform sync point validation. | |
| bool graph_validation_enabled() const { return graph_validation_enabled_; } | |
| // There are debugging fatal logs to ensure that clients don't submit | |
| // out-of-order releases. Tests that would like to explicitly test such | |
| // invalid release sequences should use this flag to suppress those fatal | |
| // logs. | |
| // | |
| // This method doesn't handle multi-thread access. Caller should set the flag | |
| // early when no one is accessing this class from multiple threads. | |
| void set_suppress_fatal_log_for_testing() { | |
| suppress_fatal_log_for_testing_ = true; | |
| } | |
| bool suppress_fatal_log_for_testing() const { | |
| return suppress_fatal_log_for_testing_; | |
| } | |
| private: | |
| using ClientStateMap = | |
| base::flat_map<CommandBufferId, scoped_refptr<SyncPointClientState>>; | |
| using OrderDataMap = | |
| base::flat_map<SequenceId, scoped_refptr<SyncPointOrderData>>; | |
| scoped_refptr<SyncPointOrderData> GetSyncPointOrderData( | |
| SequenceId sequence_id) EXCLUSIVE_LOCKS_REQUIRED(lock_); | |
| scoped_refptr<SyncPointClientState> GetSyncPointClientState( | |
| CommandBufferNamespace namespace_id, | |
| CommandBufferId command_buffer_id) EXCLUSIVE_LOCKS_REQUIRED(lock_); | |
| // Internal version of GetSyncTokenReleaseSequenceId that requires lock to be | |
| // acquired. | |
| SequenceId GetSyncTokenReleaseSequenceIdInternal(const SyncToken& sync_token) | |
| EXCLUSIVE_LOCKS_REQUIRED(lock_); | |
| // Order number is global for all clients. | |
| std::atomic<uint64_t> next_order_num_{1}; | |
| // The following are protected by |lock_|. | |
| // Map of command buffer id to client state for each namespace. | |
| std::array<ClientStateMap, NUM_COMMAND_BUFFER_NAMESPACES> client_state_maps_ | |
| GUARDED_BY(lock_); | |
| // Map of sequence id to order data. | |
| OrderDataMap order_data_map_ GUARDED_BY(lock_); | |
| SequenceId::Generator sequence_id_generator_ GUARDED_BY(lock_); | |
| mutable base::Lock lock_; | |
| const bool graph_validation_enabled_ = false; | |
| bool suppress_fatal_log_for_testing_ = false; | |
| }; | |
| } // namespace gpu | |
| #endif // GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_ |