// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= VulkanContext.h: Class to generate Vulkan command buffers from RHI CommandLists =============================================================================*/ #pragma once #include "VulkanRHIPrivate.h" #include "VulkanQuery.h" class FVulkanDevice; class FVulkanDynamicRHI; class FVulkanFence; class FVulkanPendingGfxState; class FVulkanPendingComputeState; class FVulkanQueue; class FVulkanSwapChain; class FVulkanSemaphore; class FVulkanTiming; class FVulkanDescriptorPoolSetContainer; enum class EVulkanSyncPointType { // Sync points of this type do not include an FGraphEvent, so cannot // report completion to the CPU (via either IsComplete() or Wait()) // The will resolve into binary or timeline semaphores at submit time. //GPUOnly, :todo-jn: Move GPU syncs to new FVulkanSyncPoint class // Sync points of this type include an FGraphEvent. The IsComplete() and Wait() functions // can be used to poll for completion from the CPU, or block the CPU, respectively. GPUAndCPU, // Same as GPUAndCPU but not time critical and can be pushed back up until the end of the frame. // Generally meant to be used for work that can happen at any point after the command buffers // are done executing on the GPU. Context, }; class FVulkanSyncPoint; using FVulkanSyncPointRef = TRefCountPtr; // // A sync point is a logical point on a GPU queue's timeline that can be awaited by other queues, or the CPU. // These are used throughout the RHI as a way to abstract the underlying Vulkan semaphores and fences. // The submission thread manages the underlying semaphores, fences and signaled values, and reports completion // to the relevant sync points via an FGraphEvent. // // Sync points are one-shot, meaning they represent a single timeline point, and are released after use, via ref-counting. // Use FVulkanSyncPoint::Create() to make a new sync point and hold a reference to it via a FVulkanSyncPointRef object. // class FVulkanSyncPoint final : public FThreadSafeRefCountedObject { friend FVulkanDynamicRHI; friend FVulkanQueue; static TLockFreePointerListUnordered MemoryPool; // No copying or moving FVulkanSyncPoint(FVulkanSyncPoint const&) = delete; FVulkanSyncPoint(FVulkanSyncPoint&&) = delete; FVulkanSyncPoint(EVulkanSyncPointType InType, const TCHAR* InDebugName) : Type(InType) #if RHI_USE_SYNC_POINT_DEBUG_NAME , DebugName(InDebugName) #endif { if ((Type == EVulkanSyncPointType::GPUAndCPU) || (Type == EVulkanSyncPointType::Context)) { GraphEvent = FGraphEvent::CreateGraphEvent(); } } public: static FVulkanSyncPointRef Create(EVulkanSyncPointType Type, const TCHAR* InDebugName) { LLM_SCOPE_BYNAME(TEXT("RHIMisc/CreateSyncPoint")); return new FVulkanSyncPoint(Type, InDebugName); } bool IsComplete() const { checkf(GraphEvent, TEXT("This sync point was not created with a CPU event. Cannot check completion on the CPU.")); return GraphEvent->IsComplete(); } void Wait() const; FGraphEvent* GetGraphEvent() const { checkf(GraphEvent, TEXT("This sync point was not created with a CPU event.")); return GraphEvent; } EVulkanSyncPointType GetType() const { return Type; } void* operator new(size_t Size) { check(Size == sizeof(FVulkanSyncPoint)); void* Memory = MemoryPool.Pop(); if (!Memory) { Memory = FMemory::Malloc(sizeof(FVulkanSyncPoint), alignof(FVulkanSyncPoint)); } return Memory; } void operator delete(void* Pointer) { MemoryPool.Push(Pointer); } inline const TCHAR* GetDebugName() const { #if RHI_USE_SYNC_POINT_DEBUG_NAME return DebugName; #else return TEXT("SyncPointBusyWait"); #endif } private: FGraphEventRef GraphEvent; EVulkanSyncPointType Type; #if RHI_USE_SYNC_POINT_DEBUG_NAME const TCHAR* DebugName = nullptr; #endif }; struct FVulkanCommitReservedResourceDesc { TRefCountPtr Resource = nullptr; uint64 CommitSizeInBytes = 0; }; class FVulkanPayload { friend class FVulkanCommandListContext; friend class FVulkanContextCommon; friend class FVulkanDynamicRHI; friend class FVulkanQueue; public: FVulkanPayload(FVulkanQueue& InQueue) : Queue(InQueue) #if RHI_NEW_GPU_PROFILER , EventStream(InQueue.GetProfilerQueue()) #endif {} ~FVulkanPayload(); protected: // Must only be used to merge Secondary or Parallel command buffers void Merge(FVulkanPayload& Other); void PreExecute(); FVulkanQueue& Queue; TArray WaitFlags; // flags that match 1:1 with WaitSemaphores TArray WaitSemaphores; // wait before command buffers TArray CommandBuffers; TArray SignalSemaphores; // signaled after command buffers // Signaled when the payload has been submitted to the GPU queue TArray SubmissionEvents; // For internal completion tracking of the payload uint64 TimelineSemaphoreValue = 0; FVulkanFence* Fence = nullptr; // Used to sync other CPU work to payload completion TArray SyncPoints; // Queries used in the command lists of this payload TStaticArray, (int32)EVulkanQueryPoolType::Count> QueryPools; // Used DescriptorPoolSetContainers that can be cleaned up on completion TArray DescriptorPoolSetContainers; // Used by RHIRunOnQueue TFunction PreExecuteCallback; #if WITH_RHI_BREADCRUMBS FRHIBreadcrumbRange BreadcrumbRange{}; TSharedPtr BreadcrumbAllocators{}; #endif #if RHI_NEW_GPU_PROFILER TOptional Timing; TOptional ExternalGPUTime; TOptional EndFrameEvent; UE::RHI::GPUProfiler::FEventStream EventStream; #else bool bEndFrame = false; #endif // UpdateReservedResources TArray ReservedResourcesToCommit; }; struct FVulkanPlatformCommandList : public IRHIPlatformCommandList, public TArray { }; template<> struct TVulkanResourceTraits { typedef FVulkanPlatformCommandList TConcreteType; };