// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Threading/Task.h" #include "Engine/Platform/Platform.h" #include "GPUTasksContext.h" class GPUResource; /// /// Describes GPU work object. /// class GPUTask : public Task { friend GPUTasksContext; public: /// /// Describes GPU work type /// DECLARE_ENUM_EX_4(Type, byte, 0, Custom, CopyResource, UploadTexture, UploadBuffer); /// /// Describes GPU work result value /// DECLARE_ENUM_4(Result, Ok, Failed, MissingResources, MissingData); private: /// /// Task type /// Type _type; byte _syncLatency; /// /// Synchronization point when async task has been done /// GPUSyncPoint _syncPoint; /// /// The context that performed this task, it should synchronize it. /// GPUTasksContext* _context; protected: /// /// Initializes a new instance of the class. /// /// The type. /// Amount of frames until async operation is synced with GPU. GPUTask(const Type type, byte syncLatency = GPU_ASYNC_LATENCY) : _type(type) , _syncLatency(syncLatency) , _syncPoint(0) , _context(nullptr) { } public: /// /// Gets a task type. /// FORCE_INLINE Type GetType() const { return _type; } /// /// Gets work finish synchronization point /// FORCE_INLINE GPUSyncPoint GetSyncPoint() const { return _syncPoint + _syncLatency; } public: /// /// Checks if operation is syncing /// FORCE_INLINE bool IsSyncing() const { return IsRunning() && _syncPoint != 0; } public: /// /// Executes this task. /// /// The context. void Execute(GPUTasksContext* context); /// /// Action fired when asynchronous operation has been synchronized with a GPU /// void Sync() { if (_context != nullptr) { ASSERT(IsSyncing()); // Sync and finish _context = nullptr; OnSync(); OnFinish(); } } /// /// Cancels the task results synchronization. /// void CancelSync() { ASSERT(IsSyncing()); // Rollback state and cancel _context = nullptr; SetState(TaskState::Queued); Cancel(); } protected: virtual Result run(GPUTasksContext* context) = 0; virtual void OnSync() { } public: // [Task] String ToString() const override; protected: // [Task] void Enqueue() override; bool Run() override { return true; } void OnCancel() override { // Check if task is waiting for sync (very likely situation) if (IsSyncing()) { // Task has been performed but is waiting for a CPU/GPU sync so we have to cancel that ASSERT(_context != nullptr); _context->OnCancelSync(this); _context = nullptr; SetState(TaskState::Canceled); } else { // Maybe we could also handle cancel event during running but not yet syncing ASSERT(!IsRunning()); } // Base Task::OnCancel(); } };