// 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();
}
};