| // Copyright 2023 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #ifndef SRC_DAWN_WIRE_CLIENT_EVENTMANAGER_H_ |
| #define SRC_DAWN_WIRE_CLIENT_EVENTMANAGER_H_ |
| |
| #include <cstddef> |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <utility> |
| |
| #include "dawn/common/FutureUtils.h" |
| #include "dawn/common/MutexProtected.h" |
| #include "dawn/common/NonCopyable.h" |
| #include "dawn/webgpu.h" |
| #include "dawn/wire/WireResult.h" |
| #include "partition_alloc/pointers/raw_ptr.h" |
| |
| namespace dawn::wire::client { |
| |
| class Client; |
| |
| enum class EventType { |
| CompilationInfo, |
| CreateComputePipeline, |
| CreateRenderPipeline, |
| DeviceLost, |
| MapAsync, |
| PopErrorScope, |
| RequestAdapter, |
| RequestDevice, |
| WorkDone, |
| }; |
| |
| // Implementations of TrackedEvents must implement the GetType, CompleteImpl, and ReadyHook |
| // functions. In most scenarios, the CompleteImpl function should call the callbacks while the |
| // ReadyHook should process and copy memory (if necessary) from the wire deserialization buffer |
| // into a local copy that can be readily used by the user callback. Specifically, the wire |
| // deserialization data is guaranteed to be alive when the ReadyHook is called, but not when |
| // CompleteImpl is called. |
| class TrackedEvent : NonMovable { |
| public: |
| explicit TrackedEvent(WGPUCallbackMode mode); |
| virtual ~TrackedEvent(); |
| |
| virtual EventType GetType() = 0; |
| |
| WGPUCallbackMode GetCallbackMode() const; |
| bool IsReady() const; |
| |
| void SetReady(); |
| void Complete(FutureID futureID, EventCompletionType type); |
| |
| protected: |
| virtual void CompleteImpl(FutureID futureID, EventCompletionType type) = 0; |
| |
| const WGPUCallbackMode mMode; |
| enum class EventState { |
| Pending, |
| Ready, |
| Complete, |
| }; |
| EventState mEventState = EventState::Pending; |
| }; |
| |
| // Subcomponent which tracks callback events for the Future-based callback |
| // entrypoints. All events from this instance (regardless of whether from an adapter, device, queue, |
| // etc.) are tracked here, and used by the instance-wide ProcessEvents and WaitAny entrypoints. |
| // |
| // TODO(crbug.com/dawn/2060): This should probably be merged together with RequestTracker. |
| class EventManager final : NonMovable { |
| public: |
| ~EventManager(); |
| |
| // See mState for breakdown of these states. |
| enum class State { Nominal, InstanceDropped, ClientDropped }; |
| |
| // Returns a pair of the FutureID and a bool that is true iff the event was successfuly tracked, |
| // false otherwise. Events may not be tracked if the client is already disconnected. |
| std::pair<FutureID, bool> TrackEvent(std::unique_ptr<TrackedEvent> event); |
| |
| // Transitions the EventManager to the given state. Note that states can only go in one |
| // direction, i.e. once the EventManager transitions to InstanceDropped, it cannot transition |
| // back to Nominal, though it may transition to ClientDropped later on. |
| void TransitionTo(State state); |
| |
| template <typename Event, typename... ReadyArgs> |
| WireResult SetFutureReady(FutureID futureID, ReadyArgs&&... readyArgs) { |
| DAWN_ASSERT(futureID > 0); |
| |
| // If the future id is greater than what we have assigned, it must be invalid. |
| if (futureID > mNextFutureID) { |
| return WireResult::FatalError; |
| } |
| |
| std::unique_ptr<TrackedEvent> spontaneousEvent; |
| WireResult result = mTrackedEvents.Use([&](auto trackedEvents) { |
| auto it = trackedEvents->find(futureID); |
| if (it == trackedEvents->end()) { |
| // If the future is not found, it must've already been completed. |
| return WireResult::Success; |
| } |
| auto& trackedEvent = it->second; |
| |
| if (trackedEvent->GetType() != Event::kType) { |
| // Assert here for debugging, before returning a fatal error that is handled upwards |
| // in production. |
| DAWN_ASSERT(trackedEvent->GetType() == Event::kType); |
| return WireResult::FatalError; |
| } |
| |
| WireResult result = static_cast<Event*>(trackedEvent.get()) |
| ->ReadyHook(futureID, std::forward<ReadyArgs>(readyArgs)...); |
| trackedEvent->SetReady(); |
| |
| // If the event can be spontaneously completed, prepare to do so now. |
| if (trackedEvent->GetCallbackMode() == WGPUCallbackMode_AllowSpontaneous) { |
| spontaneousEvent = std::move(trackedEvent); |
| trackedEvents->erase(futureID); |
| } |
| return result; |
| }); |
| |
| // Handle spontaneous completions. |
| if (spontaneousEvent) { |
| spontaneousEvent->Complete(futureID, EventCompletionType::Ready); |
| } |
| return result; |
| } |
| |
| void ProcessPollEvents(); |
| WGPUWaitStatus WaitAny(size_t count, WGPUFutureWaitInfo* infos, uint64_t timeoutNS); |
| |
| private: |
| // Different states of the EventManager dictate how new incoming events are handled. |
| // Nominal: Usual state of the manager. All events are tracked and callbacks are fired |
| // depending on the callback modes. |
| // InstanceDropped: Transitioned to this state if the last external reference of the Instance |
| // is dropped. In this mode, any non-spontaneous events are no longer tracked and their |
| // callbacks are immediately called since the user cannot call WaitAny or ProcessEvents |
| // anymore. Any existing non-spontaneous events' callbacks are also called on transition. |
| // ClientDropped: Transitioned to this state once the client is dropped. In this mode, no new |
| // events are tracked and callbacks are all immediately fired. Any existing tracked events' |
| // callbacks are also called on transition. |
| State mState = State::Nominal; |
| |
| // Tracks all kinds of events (for both WaitAny and ProcessEvents). We use an ordered map so |
| // that in most cases, event ordering is already implicit when we iterate the map. (Not true for |
| // WaitAny though because the user could specify the FutureIDs out of order.) |
| MutexProtected<std::map<FutureID, std::unique_ptr<TrackedEvent>>> mTrackedEvents; |
| std::atomic<FutureID> mNextFutureID = 1; |
| }; |
| |
| } // namespace dawn::wire::client |
| |
| #endif // SRC_DAWN_WIRE_CLIENT_EVENTMANAGER_H_ |