// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= D3D11RHIPrivate.h: Private D3D RHI definitions. =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "ID3D11DynamicRHI.h" #include "D3D11RHI.h" // Dependencies. #include "RHI.h" #include "GPUProfiler.h" #include "ShaderCore.h" #include "Containers/ResourceArray.h" #include "EngineGlobals.h" #include "Engine/Engine.h" #include "HDRHelper.h" #include "BoundShaderStateHistory.h" #include "DXGIUtilities.h" DECLARE_LOG_CATEGORY_EXTERN(LogD3D11RHI, Log, All); #include "Containers/StaticArray.h" // D3D RHI public headers. #include "D3D11Util.h" #include "D3D11State.h" #include "D3D11Resources.h" #include "D3D11Viewport.h" #include "D3D11ConstantBuffer.h" #include "D3D11StateCache.h" #include "D3D11NvidiaAftermath.h" #include "RHIValidationCommon.h" #include "RHICoreShader.h" #ifndef WITH_DX_PERF #define WITH_DX_PERF 1 #endif #if INTEL_EXTENSIONS THIRD_PARTY_INCLUDES_START #define INTC_IGDEXT_D3D11 1 #include "igdext.h" THIRD_PARTY_INCLUDES_END #endif // INTEL_EXTENSIONS // DX11 doesn't support higher MSAA count #define DX_MAX_MSAA_COUNT 8 #ifndef WITH_NV_API #define WITH_NV_API 0 #endif #ifndef WITH_AMD_AGS #define WITH_AMD_AGS 0 #endif /** * The D3D RHI stats. */ DECLARE_CYCLE_STAT_EXTERN(TEXT("Present time"),STAT_D3D11PresentTime,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("CustomPresent time"), STAT_D3D11CustomPresentTime, STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("CreateTexture time"),STAT_D3D11CreateTextureTime,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("LockTexture time"),STAT_D3D11LockTextureTime,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("UnlockTexture time"),STAT_D3D11UnlockTextureTime,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("CopyTexture time"),STAT_D3D11CopyTextureTime,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("CreateBoundShaderState time"),STAT_D3D11CreateBoundShaderStateTime,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("New bound shader state time"),STAT_D3D11NewBoundShaderStateTime,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("Clean uniform buffer pool"),STAT_D3D11CleanUniformBufferTime,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("Clear shader resources"),STAT_D3D11ClearShaderResourceTime,STATGROUP_D3D11RHI, ); DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(TEXT("Uniform buffer pool num free"),STAT_D3D11NumFreeUniformBuffers,STATGROUP_D3D11RHI, ); DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Immutable Uniform buffers"), STAT_D3D11NumImmutableUniformBuffers, STATGROUP_D3D11RHI, ); DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(TEXT("Num Bound Shader State"), STAT_D3D11NumBoundShaderState, STATGROUP_D3D11RHI, ); DECLARE_MEMORY_STAT_EXTERN(TEXT("Uniform buffer pool memory"), STAT_D3D11FreeUniformBufferMemory, STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("Update uniform buffer"),STAT_D3D11UpdateUniformBufferTime,STATGROUP_D3D11RHI, ); DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Textures Allocated"),STAT_D3D11TexturesAllocated,STATGROUP_D3D11RHI, ); DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Textures Released"),STAT_D3D11TexturesReleased,STATGROUP_D3D11RHI, ); DECLARE_MEMORY_STAT_EXTERN(TEXT("Texture object pool memory"),STAT_D3D11TexturePoolMemory,STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("RenderTargetCommit"), STAT_D3D11RenderTargetCommits, STATGROUP_D3D11RHI, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("RenderTargetCommitUAV"), STAT_D3D11RenderTargetCommitsUAV, STATGROUP_D3D11RHI, ); extern TAutoConsoleVariable GCVarUseSharedKeyedMutex; extern TAutoConsoleVariable GD3D11DebugCvar; struct FD3D11GlobalStats { // in bytes, never change after RHI, needed to scale game features static int64 GDedicatedVideoMemory; // in bytes, never change after RHI, needed to scale game features static int64 GDedicatedSystemMemory; // in bytes, never change after RHI, needed to scale game features static int64 GSharedSystemMemory; // In bytes. Never changed after RHI init. Our estimate of the amount of memory that we can use for graphics resources in total. static int64 GTotalGraphicsMemory; }; #if (RHI_NEW_GPU_PROFILER == 0) // This class has multiple inheritance but really FGPUTiming is a static class class FD3D11BufferedGPUTiming : public FRenderResource, public FGPUTiming { public: /** * Constructor. * * @param InD3DRHI RHI interface * @param InBufferSize Number of buffered measurements */ FD3D11BufferedGPUTiming(class FD3D11DynamicRHI* InD3DRHI, int32 BufferSize); /** * Start a GPU timing measurement. */ void StartTiming(); /** * End a GPU timing measurement. * The timing for this particular measurement will be resolved at a later time by the GPU. */ void EndTiming(); /** * Retrieves the most recently resolved timing measurement. * The unit is the same as for FPlatformTime::Cycles(). Returns 0 if there are no resolved measurements. * * @return Value of the most recently resolved timing, or 0 if no measurements have been resolved by the GPU yet. */ uint64 GetTiming(bool bGetCurrentResultsAndBlock = false); /** * Initializes all D3D resources. */ virtual void InitRHI(FRHICommandListBase& RHICmdList) override; /** * Releases all D3D resources. */ virtual void ReleaseRHI() override; static void CalibrateTimers(FD3D11DynamicRHI* InD3DRHI); private: /** * Initializes the static variables, if necessary. */ static void PlatformStaticInitialize(void* UserData); /** RHI interface */ FD3D11DynamicRHI* D3DRHI; /** Number of timestamps created in 'StartTimestamps' and 'EndTimestamps'. */ int32 BufferSize; /** Current timing being measured on the CPU. */ int32 CurrentTimestamp; /** Number of measurements in the buffers (0 - BufferSize). */ int32 NumIssuedTimestamps; /** Timestamps for all StartTimings. */ TRefCountPtr* StartTimestamps; /** Timestamps for all EndTimings. */ TRefCountPtr* EndTimestamps; /** Whether we are currently timing the GPU: between StartTiming() and EndTiming(). */ bool bIsTiming; }; /** Used to track whether a period was disjoint on the GPU, which means GPU timings are invalid. */ class FD3D11DisjointTimeStampQuery : public FRenderResource { public: FD3D11DisjointTimeStampQuery(class FD3D11DynamicRHI* InD3DRHI); void StartTracking(); void EndTracking(); bool IsResultValid(); D3D11_QUERY_DATA_TIMESTAMP_DISJOINT GetResult(); /** * Initializes all D3D resources. */ virtual void InitRHI(FRHICommandListBase& RHICmdList) override; /** * Releases all D3D resources. */ virtual void ReleaseRHI() override; private: TRefCountPtr DisjointQuery; FD3D11DynamicRHI* D3DRHI; }; /** A single perf event node, which tracks information about a appBeginDrawEvent/appEndDrawEvent range. */ class FD3D11EventNode : public FGPUProfilerEventNode { public: FD3D11EventNode(const TCHAR* InName, FGPUProfilerEventNode* InParent, class FD3D11DynamicRHI* InRHI) : FGPUProfilerEventNode(InName, InParent), Timing(InRHI, 1) { // Initialize Buffered timestamp queries Timing.InitRHI(FRHICommandListExecutor::GetImmediateCommandList()); // can't do this from the RHI thread } virtual ~FD3D11EventNode() { Timing.ReleaseRHI(); // can't do this from the RHI thread } /** * Returns the time in ms that the GPU spent in this draw event. * This blocks the CPU if necessary, so can cause hitching. */ virtual float GetTiming() override; virtual void StartTiming() override { Timing.StartTiming(); } virtual void StopTiming() override { Timing.EndTiming(); } FD3D11BufferedGPUTiming Timing; }; /** An entire frame of perf event nodes, including ancillary timers. */ class FD3D11EventNodeFrame : public FGPUProfilerEventNodeFrame { public: FD3D11EventNodeFrame(class FD3D11DynamicRHI* InRHI) : FGPUProfilerEventNodeFrame(), RootEventTiming(InRHI, 1), DisjointQuery(InRHI) { FRHICommandListBase& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList(); RootEventTiming.InitRHI(RHICmdList); DisjointQuery.InitRHI(RHICmdList); } ~FD3D11EventNodeFrame() { RootEventTiming.ReleaseRHI(); DisjointQuery.ReleaseRHI(); } /** Start this frame of per tracking */ virtual void StartFrame() override; /** End this frame of per tracking, but do not block yet */ virtual void EndFrame() override; /** Calculates root timing base frequency (if needed by this RHI) */ virtual float GetRootTimingResults() override; virtual void LogDisjointQuery() override; /** Timer tracking inclusive time spent in the root nodes. */ FD3D11BufferedGPUTiming RootEventTiming; /** Disjoint query tracking whether the times reported by DumpEventTree are reliable. */ FD3D11DisjointTimeStampQuery DisjointQuery; }; /** * Encapsulates GPU profiling logic and data. * There's only one global instance of this struct so it should only contain global data, nothing specific to a frame. */ struct FD3DGPUProfiler : public FGPUProfiler { /** Used to measure GPU time per frame. */ FD3D11BufferedGPUTiming FrameTiming; class FD3D11DynamicRHI* D3D11RHI; /** GPU hitch profile histories */ TIndirectArray GPUHitchEventNodeFrames; FD3DGPUProfiler(class FD3D11DynamicRHI* InD3DRHI); virtual FGPUProfilerEventNode* CreateEventNode(const TCHAR* InName, FGPUProfilerEventNode* InParent) override { FD3D11EventNode* EventNode = new FD3D11EventNode(InName, InParent, D3D11RHI); return EventNode; } virtual void PushEvent(const TCHAR* Name, FColor Color) override; virtual void PopEvent() override; void BeginFrame(class FD3D11DynamicRHI* InRHI); void EndFrame(); }; #endif // (RHI_NEW_GPU_PROFILER == 0) struct FD3D11TransitionData { bool bUAVBarrier; }; /** Forward declare the context for the AMD AGS utility library. */ struct AGSContext; struct FD3D11DeviceBasicInfo { D3D_FEATURE_LEVEL MaxFeatureLevel; bool bUMA; }; struct FD3D11Adapter { /** Null if not supported or FindAdapter() wasn't called. */ TRefCountPtr DXGIAdapter; DXGI_ADAPTER_DESC DXGIAdapterDesc; /** The maximum D3D11 feature level supported. 0 if not supported or FindAdapter() wasn't called */ D3D_FEATURE_LEVEL MaxSupportedFeatureLevel; /** Whether this is a software adapter */ bool bSoftwareAdapter; /** Whether the GPU has unified memory. This can be used to identify integrated GPU as well. */ bool bUMA; // constructors FD3D11Adapter() { } FD3D11Adapter(TRefCountPtr InDXGIAdapter, bool bInSoftwareAdatper, const FD3D11DeviceBasicInfo& DeviceInfo) : DXGIAdapter(InDXGIAdapter) , MaxSupportedFeatureLevel(DeviceInfo.MaxFeatureLevel) , bSoftwareAdapter(bInSoftwareAdatper) , bUMA(DeviceInfo.bUMA) { if (DXGIAdapter.IsValid()) { VERIFYD3D11RESULT(DXGIAdapter->GetDesc(&DXGIAdapterDesc)); } } bool IsValid() const { return DXGIAdapter.IsValid(); } }; PRAGMA_DISABLE_DEPRECATION_WARNINGS /** The interface which is implemented by the dynamically bound RHI. */ class D3D11RHI_API FD3D11DynamicRHI : public ID3D11DynamicRHI, public IRHICommandContextPSOFallback { public: typedef TMap FD3D11LockTracker; friend class FD3D11Viewport; /** Initialization constructor. */ FD3D11DynamicRHI(IDXGIFactory1* InDXGIFactory1, D3D_FEATURE_LEVEL InFeatureLevel, const FD3D11Adapter& InAdapter); /** Destructor */ virtual ~FD3D11DynamicRHI(); /** If it hasn't been initialized yet, initializes the D3D device. */ virtual void InitD3DDevice(); // FDynamicRHI interface. virtual void Init() override; virtual void Shutdown() override; virtual const TCHAR* GetName() override { return TEXT("D3D11"); } virtual void FlushPendingLogs() override; static FD3D11DynamicRHI& Get() { return *GetDynamicRHI(); } template static FORCEINLINE typename TD3D11ResourceTraits::TConcreteType* ResourceCast(TRHIType* Resource) { return static_cast::TConcreteType*>(Resource); } static inline FD3D11Texture* ResourceCast(FRHITexture* Texture) { if (!Texture) { return nullptr; } FD3D11Texture* Result = static_cast(Texture->GetTextureBaseRHI()); check(Result); return Result; } template void BindUniformBuffer(uint32 BufferIndex, FRHIUniformBuffer* BufferRHI); template void ApplyStaticUniformBuffers(TRHIShader* Shader); template void SetShaderParametersCommon(FD3D11ConstantBuffer* StageConstantBuffer, TConstArrayView InParametersData, TConstArrayView InParameters, TConstArrayView InResourceParameters); template void SetShaderUnbindsCommon(TConstArrayView InUnbinds); /** * Reads a D3D query's data into the provided buffer. * @param Query - The D3D query to read data from. * @param Data - The buffer to read the data into. * @param DataSize - The size of the buffer. * @param QueryType e.g. RQT_Occlusion or RQT_AbsoluteTime * @param bWait - If true, it will wait for the query to finish. * @param bStallRHIThread - if true, stall RHIT before accessing immediate context * @return true if the query finished. */ bool GetQueryData(ID3D11Query* Query, void* Data, SIZE_T DataSize, bool bTimestamp, bool bWait, bool bStallRHIThread); virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override; virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override; virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override; virtual FBlendStateRHIRef RHICreateBlendState(const FBlendStateInitializerRHI& Initializer) final override; virtual FVertexDeclarationRHIRef RHICreateVertexDeclaration(const FVertexDeclarationElementList& Elements) final override; virtual FPixelShaderRHIRef RHICreatePixelShader(TArrayView Code, const FSHAHash& Hash) final override; virtual FVertexShaderRHIRef RHICreateVertexShader(TArrayView Code, const FSHAHash& Hash) final override; virtual FGeometryShaderRHIRef RHICreateGeometryShader(TArrayView Code, const FSHAHash& Hash) final override; virtual FComputeShaderRHIRef RHICreateComputeShader(TArrayView Code, const FSHAHash& Hash) final override; virtual FStagingBufferRHIRef RHICreateStagingBuffer() final override; virtual void RHICopyToStagingBuffer(FRHIBuffer* SourceBuffer, FRHIStagingBuffer* DestinationStagingBuffer, uint32 Offset, uint32 NumBytes) final override; virtual FGPUFenceRHIRef RHICreateGPUFence(const FName& Name) final override; virtual void RHIWriteGPUFence_TopOfPipe(FRHICommandListBase& RHICmdList, FRHIGPUFence* FenceRHI) final override; virtual void RHIWriteGPUFence(FRHIGPUFence* Fence) final override; virtual void* RHILockStagingBuffer(FRHIStagingBuffer* StagingBuffer, FRHIGPUFence* Fence, uint32 Offset, uint32 SizeRHI) final override; virtual void RHIUnlockStagingBuffer(FRHIStagingBuffer* StagingBuffer) final override; virtual FBoundShaderStateRHIRef RHICreateBoundShaderState(FRHIVertexDeclaration* VertexDeclaration, FRHIVertexShader* VertexShader, FRHIPixelShader* PixelShader, FRHIGeometryShader* GeometryShader) final override; virtual FUniformBufferRHIRef RHICreateUniformBuffer(const void* Contents, const FRHIUniformBufferLayout* Layout, EUniformBufferUsage Usage, EUniformBufferValidation Validation) final override; virtual void RHIUpdateUniformBuffer(FRHICommandListBase& RHICmdList, FRHIUniformBuffer* UniformBufferRHI, const void* Contents) final override; #if ENABLE_LOW_LEVEL_MEM_TRACKER || UE_MEMORY_TRACE_ENABLED virtual void RHIUpdateAllocationTags(FRHICommandListBase& RHICmdList, FRHIBuffer* Buffer) final override; #endif [[nodiscard]] FD3D11Buffer* BeginCreateBufferInternal(const FRHIBufferCreateDesc& CreateDesc); [[nodiscard]] void FinalizeCreateBufferInternal(FD3D11Buffer* Buffer, TConstArrayView InitialData); [[nodiscard]] FD3D11Buffer* CreateBufferInternal(const FRHIBufferCreateDesc& CreateDesc, TConstArrayView InitialData); [[nodiscard]] virtual FRHIBufferInitializer RHICreateBufferInitializer(FRHICommandListBase& RHICmdList, const FRHIBufferCreateDesc& CreateDesc) final override; virtual void RHIReplaceResources(FRHICommandListBase& RHICmdList, TArray&& ReplaceInfos) final override; virtual void* LockBuffer_BottomOfPipe(FRHICommandListBase& RHICmdList, FRHIBuffer* Buffer, uint32 Offset, uint32 Size, EResourceLockMode LockMode) final override; virtual void UnlockBuffer_BottomOfPipe(FRHICommandListBase& RHICmdList, FRHIBuffer* Buffer) final override; virtual FRHICalcTextureSizeResult RHICalcTexturePlatformSize(FRHITextureDesc const& Desc, uint32 FirstMipIndex) final override; virtual void RHIGetTextureMemoryStats(FTextureMemoryStats& OutStats) final override; virtual bool RHIGetTextureMemoryVisualizeData(FColor* TextureData,int32 SizeX,int32 SizeY,int32 Pitch,int32 PixelSize) final override; [[nodiscard]] FD3D11Texture* BeginCreateTextureInternal(const FRHITextureCreateDesc& CreateDesc); void FinalizeCreateTexture2DInternal(FD3D11Texture* Texture, TConstArrayView InitialData); void FinalizeCreateTexture3DInternal(FD3D11Texture* Texture, TConstArrayView InitialData); FD3D11Texture* FinalizeCreateTextureInternal(FD3D11Texture* Texture, const FRHITextureDesc& InDesc, TConstArrayView InitialData); [[nodiscard]] FD3D11Texture* CreateTextureInternal(const FRHITextureCreateDesc& CreateDesc, TConstArrayView InitialData); [[nodiscard]] virtual FRHITextureInitializer RHICreateTextureInitializer(FRHICommandListBase& RHICmdList, const FRHITextureCreateDesc& CreateDesc) final override; virtual FTextureRHIRef RHIAsyncCreateTexture2D(uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags Flags, ERHIAccess InResourceState, void** InitialMipData, uint32 NumInitialMips, const TCHAR* DebugName, FGraphEventRef& OutCompletionEvent) final override; virtual uint32 RHIComputeMemorySize(FRHITexture* TextureRHI) final override; virtual void RHIAsyncCopyTexture2DCopy(FRHITexture* NewTexture2DRHI, FRHITexture* Texture2DRHI, int32 NewMipCount, int32 NewSizeX, int32 NewSizeY, FThreadSafeCounter* RequestStatus); virtual FTextureRHIRef RHIAsyncReallocateTexture2D(FRHITexture* Texture2D, int32 NewMipCount, int32 NewSizeX, int32 NewSizeY, FThreadSafeCounter* RequestStatus) final override; virtual FRHILockTextureResult RHILockTexture(FRHICommandListImmediate& RHICmdList, const FRHILockTextureArgs& Arguments) final override; virtual void RHIUnlockTexture(FRHICommandListImmediate& RHICmdList, const FRHILockTextureArgs& Arguments) final override; virtual void RHIUpdateTexture2D(FRHICommandListBase& RHICmdList, FRHITexture* Texture, uint32 MipIndex, const struct FUpdateTextureRegion2D& UpdateRegion, uint32 SourcePitch, const uint8* SourceData) final override; virtual void RHIUpdateTexture3D(FRHICommandListBase& RHICmdList, FRHITexture* Texture, uint32 MipIndex, const struct FUpdateTextureRegion3D& UpdateRegion, uint32 SourceRowPitch, uint32 SourceDepthPitch, const uint8* SourceData) final override; virtual void RHIBindDebugLabelName(FRHICommandListBase& RHICmdList, FRHITexture* Texture, const TCHAR* Name) final override; virtual void RHIBindDebugLabelName(FRHICommandListBase& RHICmdList, FRHIBuffer* Buffer, const TCHAR* Name) final override; virtual void RHIBindDebugLabelName(FRHICommandListBase& RHICmdList, FRHIUnorderedAccessView* UnorderedAccessViewRHI, const TCHAR* Name) final override; virtual void RHIReadSurfaceData(FRHITexture* Texture,FIntRect Rect,TArray& OutData,FReadSurfaceDataFlags InFlags) final override; virtual void RHIReadSurfaceData(FRHITexture* TextureRHI, FIntRect InRect, TArray& OutData, FReadSurfaceDataFlags InFlags) final override; virtual void RHIMapStagingSurface(FRHITexture* Texture, FRHIGPUFence* Fence, void*& OutData, int32& OutWidth, int32& OutHeight, uint32 GPUIndex = 0) final override; virtual void RHIUnmapStagingSurface(FRHITexture* Texture, uint32 GPUIndex = 0) final override; virtual void RHIReadSurfaceFloatData(FRHITexture* Texture,FIntRect Rect,TArray& OutData,ECubeFace CubeFace,int32 ArrayIndex,int32 MipIndex) final override; virtual void RHIRead3DSurfaceFloatData(FRHITexture* Texture,FIntRect Rect,FIntPoint ZMinMax,TArray& OutData) final override; virtual FRenderQueryRHIRef RHICreateRenderQuery(ERenderQueryType QueryType) final override; virtual bool RHIGetRenderQueryResult(FRHIRenderQuery* RenderQuery, uint64& OutResult, bool bWait, uint32 GPUIndex = INDEX_NONE) final override; virtual FTextureRHIRef RHIGetViewportBackBuffer(FRHIViewport* Viewport) final override; virtual void RHIAliasTextureResources(FTextureRHIRef& DestTexture, FTextureRHIRef& SrcTexture) final override; virtual FTextureRHIRef RHICreateAliasedTexture(FTextureRHIRef& SourceTexture) final override; virtual void RHIAdvanceFrameForGetViewportBackBuffer(FRHIViewport* Viewport) final override; virtual void RHIFlushResources() final override; virtual FViewportRHIRef RHICreateViewport(void* WindowHandle, uint32 SizeX, uint32 SizeY, bool bIsFullscreen, EPixelFormat PreferredPixelFormat) override; virtual void RHIResizeViewport(FRHIViewport* Viewport, uint32 SizeX, uint32 SizeY, bool bIsFullscreen) final override; virtual void RHIResizeViewport(FRHIViewport* Viewport, uint32 SizeX, uint32 SizeY, bool bIsFullscreen, EPixelFormat PreferredPixelFormat) final override; virtual void RHIHandleDisplayChange() final override; virtual void RHITick(float DeltaTime) final override; virtual void RHIBlockUntilGPUIdle() final override; virtual bool RHIGetAvailableResolutions(FScreenResolutionArray& Resolutions, bool bIgnoreRefreshRate) final override; virtual void RHIGetSupportedResolution(uint32& Width, uint32& Height) final override; virtual void* RHIGetNativeDevice() final override; virtual void* RHIGetNativeInstance() final override; virtual void* RHIGetNativeCommandBuffer() final override; virtual class IRHICommandContext* RHIGetDefaultContext() final override; virtual IRHIComputeContext* RHIGetCommandContext(ERHIPipeline Pipeline, FRHIGPUMask GPUMask) final override; virtual void RHIFinalizeContext(FRHIFinalizeContextArgs&& Args, TRHIPipelineArray& Output) final override; virtual void RHISubmitCommandLists(FRHISubmitCommandListsArgs&& Args) final override; // SRV / UAV creation functions virtual FShaderResourceViewRHIRef RHICreateShaderResourceView (class FRHICommandListBase& RHICmdList, FRHIViewableResource* Resource, FRHIViewDesc const& ViewDesc) final override; virtual FUnorderedAccessViewRHIRef RHICreateUnorderedAccessView(class FRHICommandListBase& RHICmdList, FRHIViewableResource* Resource, FRHIViewDesc const& ViewDesc) final override; virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) final override; virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) final override; virtual void RHIDispatchIndirectComputeShader(FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; virtual void RHIBeginUAVOverlap() final override; virtual void RHIEndUAVOverlap() final override; virtual void RHISetMultipleViewports(uint32 Count, const FViewportBounds* Data) final override; virtual void RHIClearUAVFloat(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FVector4f& Values) final override; virtual void RHIClearUAVUint(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FUintVector4& Values) final override; virtual void RHICopyTexture(FRHITexture* SourceTexture, FRHITexture* DestTexture, const FRHICopyTextureInfo& CopyInfo) final override; virtual void RHICopyBufferRegion(FRHIBuffer* DestBuffer, uint64 DstOffset, FRHIBuffer* SourceBuffer, uint64 SrcOffset, uint64 NumBytes) final override; virtual void RHICreateTransition(FRHITransition* Transition, const FRHITransitionCreateInfo& CreateInfo) final override; virtual void RHIReleaseTransition(FRHITransition* Transition) final override; virtual void RHIBeginTransitions(TArrayView Transitions) override final; virtual void RHIEndTransitions(TArrayView Transitions) override final; virtual void RHIEndRenderQuery_TopOfPipe (FRHICommandListBase& RHICmdList, FRHIRenderQuery* RenderQuery) override final; virtual void RHIBeginRenderQuery(FRHIRenderQuery* RenderQuery) final override; virtual void RHIEndRenderQuery (FRHIRenderQuery* RenderQuery) final override; virtual void RHIBeginDrawingViewport(FRHIViewport* Viewport, FRHITexture* RenderTargetRHI) final override; virtual void RHIEndDrawingViewport(FRHIViewport* Viewport, bool bPresent, bool bLockToVsync) final override; virtual void RHIEndFrame(const FRHIEndFrameArgs& Args) final override; virtual void RHISetStreamSource(uint32 StreamIndex, FRHIBuffer* VertexBuffer, uint32 Offset) final override; virtual void RHISetRasterizerState(FRHIRasterizerState* NewState) final override; virtual void RHISetViewport(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ) final override; virtual void RHISetStereoViewport(float LeftMinX, float RightMinX, float LeftMinY, float RightMinY, float MinZ, float LeftMaxX, float RightMaxX, float LeftMaxY, float RightMaxY, float MaxZ) override; virtual void RHISetScissorRect(bool bEnable, uint32 MinX, uint32 MinY, uint32 MaxX, uint32 MaxY) final override; virtual void RHISetBoundShaderState(FRHIBoundShaderState* BoundShaderState) final override; virtual void RHISetGraphicsPipelineState(FRHIGraphicsPipelineState* GraphicsState, uint32 StencilRef, bool bApplyAdditionalState) final override; virtual void RHISetStaticUniformBuffers(const FUniformBufferStaticBindings& InUniformBuffers) final override; virtual void RHISetStaticUniformBuffer(FUniformBufferStaticSlot Slot, FRHIUniformBuffer* Buffer) final override; virtual void RHISetShaderParameters(FRHIComputeShader* Shader, TConstArrayView InParametersData, TConstArrayView InParameters, TConstArrayView InResourceParameters, TConstArrayView InBindlessParameters) final override; virtual void RHISetShaderParameters(FRHIGraphicsShader* Shader, TConstArrayView InParametersData, TConstArrayView InParameters, TConstArrayView InResourceParameters, TConstArrayView InBindlessParameters) final override; virtual void RHISetShaderUnbinds(FRHIComputeShader* Shader, TConstArrayView InUnbinds) final override; virtual void RHISetShaderUnbinds(FRHIGraphicsShader* Shader, TConstArrayView InUnbinds) final override; virtual void RHISetDepthStencilState(FRHIDepthStencilState* NewState, uint32 StencilRef) final override; virtual void RHISetStencilRef(uint32 StencilRef) final override; virtual void RHISetBlendState(FRHIBlendState* NewState, const FLinearColor& BlendFactor) final override; virtual void RHISetBlendFactor(const FLinearColor& BlendFactor) final override; void SetRenderTargets(uint32 NumSimultaneousRenderTargets, const FRHIRenderTargetView* NewRenderTargets, const FRHIDepthRenderTargetView* NewDepthStencilTarget); void InternalSetUAVCS(uint32 BindIndex, FD3D11UnorderedAccessView* UnorderedAccessViewRHI); void InternalSetUAVVSPS(uint32 BindIndex, FD3D11UnorderedAccessView* UnorderedAccessViewRHI); void SetRenderTargetsAndClear(const FRHISetRenderTargetsInfo& RenderTargetsInfo); virtual void RHIDrawPrimitive(uint32 BaseVertexIndex, uint32 NumPrimitives, uint32 NumInstances) final override; virtual void RHIDrawPrimitiveIndirect(FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; virtual void RHIDrawIndexedIndirect(FRHIBuffer* IndexBufferRHI, FRHIBuffer* ArgumentsBufferRHI, int32 DrawArgumentsIndex, uint32 NumInstances) final override; virtual void RHIDrawIndexedPrimitive(FRHIBuffer* IndexBuffer, int32 BaseVertexIndex, uint32 FirstInstance, uint32 NumVertices, uint32 StartIndex, uint32 NumPrimitives, uint32 NumInstances) final override; virtual void RHIDrawIndexedPrimitiveIndirect(FRHIBuffer* IndexBuffer, FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; virtual void RHIEnableDepthBoundsTest(bool bEnable) final override { if (GSupportsDepthBoundsTest && StateCache.bDepthBoundsEnabled != bEnable) { EnableDepthBoundsTest(bEnable, 0.0f, 1.0f); } } virtual void RHISetDepthBounds(float MinDepth, float MaxDepth) final override { if (GSupportsDepthBoundsTest && (StateCache.DepthBoundsMin != MinDepth || StateCache.DepthBoundsMax != MaxDepth)) { EnableDepthBoundsTest(true, MinDepth, MaxDepth); } } #if WITH_RHI_BREADCRUMBS virtual void RHIBeginBreadcrumbGPU(FRHIBreadcrumbNode* Breadcrumb) final override; virtual void RHIEndBreadcrumbGPU (FRHIBreadcrumbNode* Breadcrumb) final override; #endif void PollQueryResults(); // *_RenderThread functions. Command lists call these functions on RT. You can implement your own behavior inside these functions. // For example, deferring the actual creation to RHI thread by sending an RHI command. // For D3D11, these functions mainly just remove RHIT stalls because ID3D11Device is thread safe. virtual FTextureRHIRef AsyncReallocateTexture2D_RenderThread(class FRHICommandListImmediate& RHICmdList, FRHITexture* Texture2D, int32 NewMipCount, int32 NewSizeX, int32 NewSizeY, FThreadSafeCounter* RequestStatus) final override; virtual void RHIEndUpdateTexture3D(FRHICommandListBase& RHICmdList, FUpdateTexture3DData& UpdateData) final override; virtual void RHIBeginRenderPass(const FRHIRenderPassInfo& InInfo, const TCHAR* InName) final override; virtual void RHIEndRenderPass() final override; void ResolveTexture(UE::RHICore::FResolveTextureInfo Info); // ID3D11DynamicRHI interface virtual ID3D11Device* RHIGetDevice() const final override; virtual ID3D11DeviceContext* RHIGetDeviceContext() const final override; virtual IDXGIAdapter* RHIGetAdapter() const final override; virtual IDXGISwapChain* RHIGetSwapChain(FRHIViewport* InViewport) const final override; virtual DXGI_FORMAT RHIGetSwapChainFormat(EPixelFormat InFormat) const final override; virtual FTextureRHIRef RHICreateTexture2DFromResource(EPixelFormat Format, ETextureCreateFlags TexCreateFlags, const FClearValueBinding& ClearValueBinding, ID3D11Texture2D* Resource) final override; virtual FTextureRHIRef RHICreateTexture2DArrayFromResource(EPixelFormat Format, ETextureCreateFlags TexCreateFlags, const FClearValueBinding&, ID3D11Texture2D* Resource) final override; virtual FTextureRHIRef RHICreateTextureCubeFromResource(EPixelFormat Format, ETextureCreateFlags TexCreateFlags, const FClearValueBinding& ClearValueBinding, ID3D11Texture2D* Resource) final override; virtual ID3D11Buffer* RHIGetResource(FRHIBuffer* InBuffer) const final override; virtual ID3D11Resource* RHIGetResource(FRHITexture* InTexture) const final override; virtual int64 RHIGetResourceMemorySize(FRHITexture* InTexture) const final override; virtual ID3D11RenderTargetView* RHIGetRenderTargetView(FRHITexture* InTexture, int32 InMipIndex = 0, int32 InArraySliceIndex = -1) const final override; virtual ID3D11ShaderResourceView* RHIGetShaderResourceView(FRHITexture* InTexture) const final override; virtual void RHIRegisterWork(uint32 NumPrimitives) final override; virtual void RHIVerifyResult(ID3D11Device* Device, HRESULT Result, const ANSICHAR* Code, const ANSICHAR* Filename, uint32 Line) const final override; virtual void RHIGetDisplaysInformation(FDisplayInformationArray& OutDisplayInformation) final override; // Accessors. ID3D11Device* GetDevice() const { return Direct3DDevice; } FD3D11DeviceContext* GetDeviceContext() const { return Direct3DDeviceIMContext; } IDXGIFactory1* GetFactory() const { return DXGIFactory1; } void AddLockedData(const FD3D11LockedKey& Key, const FD3D11LockedData& LockedData) { FScopeLock Lock(&LockTrackerCS); LockTracker.Add(Key, LockedData); } bool RemoveLockedData(const FD3D11LockedKey& Key, FD3D11LockedData& OutLockedData) { FScopeLock Lock(&LockTrackerCS); return LockTracker.RemoveAndCopyValue(Key, OutLockedData); } bool IsQuadBufferStereoEnabled(); void DisableQuadBufferStereo(); void BeginRecursiveCommand() { // Nothing to do } private: void EnableDepthBoundsTest(bool bEnable, float MinDepth, float MaxDepth); static void ClearUAV(TRHICommandList_RecursiveHazardous& RHICmdList, FD3D11UnorderedAccessView* UAV, const void* ClearValues, bool bFloat); enum class EForceFullScreenClear { EDoNotForce, EForce }; virtual void RHIClearMRTImpl(const bool* bClearColorArray, int32 NumClearColors, const FLinearColor* ColorArray, bool bClearDepth, float Depth, bool bClearStencil, uint32 Stencil); template void ClearShaderResourceViews(FD3D11ViewableResource* Resource); template void ClearAllShaderResourcesForFrequency(); template void InternalSetShaderResourceView(FD3D11ViewableResource* Resource, ID3D11ShaderResourceView* SRV, int32 ResourceIndex); void TrackResourceBoundAsVB(FD3D11ViewableResource* Resource, int32 StreamIndex); void TrackResourceBoundAsIB(FD3D11ViewableResource* Resource); void SetCurrentComputeShader(FRHIComputeShader* ComputeShader) { CurrentComputeShader = ComputeShader; } const FComputeShaderRHIRef& GetCurrentComputeShader() const { return CurrentComputeShader; } public: template void SetShaderResourceView(FD3D11ViewableResource* Resource, ID3D11ShaderResourceView* SRV, int32 ResourceIndex) { InternalSetShaderResourceView(Resource, SRV, ResourceIndex); } void ClearState(); void ConditionalClearShaderResource(FD3D11ViewableResource* Resource, bool bCheckBoundInputAssembler); void ClearAllShaderResources(); EPixelFormat GetDisplayFormat(EPixelFormat InPixelFormat) const; FD3D11StateCache& GetStateCache() { return StateCache; } protected: /** The global D3D interface. */ TRefCountPtr DXGIFactory1; #if PLATFORM_WINDOWS TRefCountPtr DXGIFactoryForDisplayList; #endif // Whether HDR is available from the particular DXGI factories available bool bDXGISupportsHDR; /** The global D3D device's immediate context */ TRefCountPtr Direct3DDeviceIMContext; #if WITH_DX_PERF TRefCountPtr Direct3DDeviceIMAnnotation; #endif #if NV_AFTERMATH UE::RHICore::Nvidia::Aftermath::D3D11::FCommandList AftermathHandle = nullptr; #endif /** The global D3D device's immediate context */ TRefCountPtr Direct3DDevice; FD3D11StateCache StateCache; /** Tracks outstanding locks on each thread */ FD3D11LockTracker LockTracker; FCriticalSection LockTrackerCS; /** A list of all viewport RHIs that have been created. */ TArray Viewports; /** The viewport which is currently being drawn. */ TRefCountPtr DrawingViewport; /** The feature level of the device. */ D3D_FEATURE_LEVEL FeatureLevel; /** * The context for the AMD AGS utility library. * AGSContext does not implement AddRef/Release. * Just use a bare pointer. */ AGSContext* AmdAgsContext; #if INTEL_EXTENSIONS // Context and functions for Intel extension framework utility library INTCExtensionContext* IntelExtensionContext; bool bIntelSupportsUAVOverlap; #endif // INTEL_EXTENSIONS // set by UpdateMSAASettings(), get by GetMSAAQuality() // [SampleCount] = Quality, 0xffffffff if not supported uint32 AvailableMSAAQualities[DX_MAX_MSAA_COUNT + 1]; /** A buffer in system memory containing all zeroes of the specified size. */ void* ZeroBuffer; uint32 ZeroBufferSize; // Tracks the currently set state blocks. bool bCurrentDepthStencilStateIsReadOnly; // Current PSO Primitive Type EPrimitiveType PrimitiveType; TRefCountPtr CurrentRenderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT]; TRefCountPtr CurrentUAVs[D3D11_PS_CS_UAV_REGISTER_COUNT]; ID3D11UnorderedAccessView* UAVBound[D3D11_PS_CS_UAV_REGISTER_COUNT]; uint32 UAVBindFirst; uint32 UAVBindCount; uint32 UAVSChanged; uint32 CurrentRTVOverlapMask; uint32 CurrentUAVMask; TRefCountPtr CurrentDepthStencilTarget; TRefCountPtr CurrentDepthTexture; FD3D11ViewableResource* CurrentResourcesBoundAsSRVs[SF_NumStandardFrequencies][D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT]; FD3D11ViewableResource* CurrentResourcesBoundAsVBs[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; FD3D11ViewableResource* CurrentResourceBoundAsIB; int32 MaxBoundShaderResourcesIndex[SF_NumStandardFrequencies]; int32 MaxBoundVertexBufferIndex; uint32 NumSimultaneousRenderTargets; uint32 NumUAVs; /** Internal frame counter that just counts calls to Present */ uint32 PresentCounter; // Render queries that should be polled by the RHI thread. struct { FD3D11RenderQuery* First = nullptr; FD3D11RenderQuery* Last = nullptr; } ActiveQueries; friend class FD3D11RenderQuery; struct FProfiler { struct FFrame { TRefCountPtr CompletionQuery; #if RHI_NEW_GPU_PROFILER UE::RHI::GPUProfiler::FEventStream EventStream; #endif #if WITH_RHI_BREADCRUMBS FRHIBreadcrumbAllocatorArray BreadcrumbAllocators{}; #endif FFrame() #if RHI_NEW_GPU_PROFILER : EventStream(UE::RHI::GPUProfiler::FQueue(UE::RHI::GPUProfiler::FQueue::EType::Graphics, 0, 0)) #endif {} }; FFrame Current; TQueue> Pending; TArray TimestampPool; TArray> EventPool; } Profiler; #if RHI_NEW_GPU_PROFILER template TEventType& EmplaceProfilerEvent(TArgs&&... Args) { return Profiler.Current.EventStream.Emplace(Forward(Args)...); } void InsertProfilerTimestamp(uint64* Target); void FlushProfilerStats() { // Flush accumulated draw stats if (StatEvent) { EmplaceProfilerEvent() = StatEvent; StatEvent = {}; } } #endif // RHI_NEW_GPU_PROFILER public: struct FTimestampCalibration { uint64 CPUTimestamp = 0; uint64 CPUFrequency = 0; uint64 GPUTimestamp = 0; uint64 GPUFrequency = 0; }; TOptional CalibrateTimers(); protected: TOptional TimestampCalibration; /** D3D11 defines a maximum of 14 constant buffers per shader stage. */ enum { MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE = 14 }; /** Track the currently bound uniform buffers. */ FRHIUniformBuffer* BoundUniformBuffers[SF_NumStandardFrequencies][MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE]; /** Bit array to track which uniform buffers have changed since the last draw call. */ uint16 DirtyUniformBuffers[SF_NumStandardFrequencies]; TArray StaticUniformBuffers; /** Tracks the current depth stencil access type. */ FExclusiveDepthStencil CurrentDSVAccessType; /** When a new shader is set, we discard all old constants set for the previous shader. */ bool bDiscardSharedConstants; /** A list of all D3D constant buffers RHIs that have been created. */ TRefCountPtr VSConstantBuffer; TRefCountPtr PSConstantBuffer; TRefCountPtr GSConstantBuffer; TRefCountPtr CSConstantBuffer; /** A history of the most recently used bound shader states, used to keep transient bound shader states from being recreated for each use. */ TGlobalResource< TBoundShaderStateHistory<10000> > BoundShaderStateHistory; FComputeShaderRHIRef CurrentComputeShader; FDisplayInformationArray DisplayList; HANDLE ExceptionHandlerHandle = INVALID_HANDLE_VALUE; bool bRenderDoc = false; public: #if (RHI_NEW_GPU_PROFILER == 0) void RegisterGPUWork(uint32 NumPrimitives = 0, uint32 NumVertices = 0) { GPUProfilingData.RegisterGPUWork(NumPrimitives, NumVertices); } void RegisterGPUDispatch(FIntVector GroupCount) { GPUProfilingData.RegisterGPUDispatch(GroupCount); } #endif inline const FD3D11Adapter& GetAdapter() const { return Adapter; } protected: #if (RHI_NEW_GPU_PROFILER == 0) FD3DGPUProfiler GPUProfilingData; #endif FD3D11Adapter Adapter; FD3D11Texture* CreateTextureFromResource(bool bTextureArray, bool bCubeTexture, EPixelFormat Format, ETextureCreateFlags TexCreateFlags, const FClearValueBinding& ClearValueBinding, ID3D11Texture2D* TextureResource); /** Initializes the constant buffers. Called once at RHI initialization time. */ void InitConstantBuffers(); /** needs to be called before each draw call */ virtual void CommitNonComputeShaderConstants(); /** needs to be called before each dispatch call */ virtual void CommitComputeShaderConstants(); template void SetResourcesFromTables(const ShaderType* RESTRICT); void CommitGraphicsResourceTables(); void CommitComputeResourceTables(FD3D11ComputeShader* ComputeShader); void ValidateExclusiveDepthStencilAccess(FExclusiveDepthStencil Src) const; /** * Gets the best supported MSAA settings from the provided MSAA count to check against. * * @param PlatformFormat The format of the texture being created * @param MSAACount The MSAA count to check against. * @param OutBestMSAACount The best MSAA count that is suppored. Could be smaller than MSAACount if it is not supported * @param OutMSAAQualityLevels The number MSAA quality levels for the best msaa count supported */ void GetBestSupportedMSAASetting( DXGI_FORMAT PlatformFormat, uint32 MSAACount, uint32& OutBestMSAACount, uint32& OutMSAAQualityLevels ); // shared code for different D3D11 devices (e.g. PC DirectX11 and XboxOne) called // after device creation and GRHISupportsAsyncTextureCreation was set and before resource init void SetupAfterDeviceCreation(); // called by SetupAfterDeviceCreation() when the device gets initialized void UpdateMSAASettings(); // @return 0xffffffff if not not supported uint32 GetMaxMSAAQuality(uint32 SampleCount); void CommitRenderTargetsAndUAVs(); void CommitRenderTargets(bool bClearUAVS); void CommitUAVs(); /** * Cleanup the D3D device. * This function must be called from the main game thread. */ virtual void CleanupD3DDevice(); void ReleasePooledUniformBuffers(); template static void ResolveTextureUsingShader( FD3D11DynamicRHI* const This, FD3D11Texture* const SourceTexture, FD3D11Texture* const DestTexture, ID3D11RenderTargetView* const DestTextureRTV, ID3D11DepthStencilView* const DestTextureDSV, D3D11_TEXTURE2D_DESC const& ResolveTargetDesc, FResolveRect const& SourceRect, FResolveRect const& DestRect, typename TPixelShader::FParameter const PixelShaderParameter ); /** * Returns a pointer to a texture resource that can be used for CPU reads. * Note: the returned resource could be the original texture or a new temporary texture. * @param TextureRHI - Source texture to create a staging texture from. * @param InRect - rectangle to 'stage'. * @param StagingRectOUT - parameter is filled with the rectangle to read from the returned texture. * @return The CPU readable Texture object. */ TRefCountPtr GetStagingTexture(FRHITexture* TextureRHI,FIntRect InRect, FIntRect& OutRect, FReadSurfaceDataFlags InFlags); void ReadSurfaceDataNoMSAARaw(FRHITexture* TextureRHI,FIntRect Rect,TArray& OutData, FReadSurfaceDataFlags InFlags); void ReadSurfaceDataMSAARaw(FRHITexture* TextureRHI, FIntRect Rect, TArray& OutData, FReadSurfaceDataFlags InFlags); #if INTEL_EXTENSIONS void StartIntelExtensions(); void StopIntelExtensions(); #endif // INTEL_EXTENSIONS bool bUAVOverlapEnabled = false; void EnableUAVOverlap(); void DisableUAVOverlap(); bool SetupDisplayHDRMetaData(); void UpdateMemoryStats(); friend struct FD3DGPUProfiler; }; PRAGMA_ENABLE_DEPRECATION_WARNINGS /** Implements the D3D11RHI module as a dynamic RHI providing module. */ class FD3D11DynamicRHIModule : public IDynamicRHIModule { public: // IModuleInterface virtual bool SupportsDynamicReloading() override { return false; } // IDynamicRHIModule virtual bool IsSupported() override; virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override; private: FD3D11Adapter ChosenAdapter; // set MaxSupportedFeatureLevel and ChosenAdapter void FindAdapter(); }; // 1d, 31 bit (uses the sign bit for internal use), O(n) where n is the amount of elements stored // does not enforce any alignment // unoccupied regions get compacted but occupied don't get compacted class FRangeAllocator { public: struct FRange { // not valid FRange() : Start(0) , Size(0) { check(!IsValid()); } void SetOccupied(int32 InStart, int32 InSize) { check(InStart >= 0); check(InSize > 0); Start = InStart; Size = InSize; check(IsOccupied()); } void SetUnOccupied(int32 InStart, int32 InSize) { check(InStart >= 0); check(InSize > 0); Start = InStart; Size = -InSize; check(!IsOccupied()); } bool IsValid() { return Size != 0; } bool IsOccupied() const { return Size > 0; } uint32 ComputeSize() const { return (Size > 0) ? Size : -Size; } // @apram InSize can be <0 to remove from the size void ExtendUnoccupied(int32 InSize) { check(!IsOccupied()); Size -= InSize; } void MakeOccupied(int32 InSize) { check(InSize > 0); check(!IsOccupied()); Size = InSize; } void MakeUnOccupied() { check(IsOccupied()); Size = -Size; } bool operator==(const FRange& rhs) const { return Start == rhs.Start && Size == rhs.Size; } int32 GetStart() { return Start; } int32 GetEnd() { return Start + ComputeSize(); } private: // in bytes int32 Start; // in bytes, 0:not valid, <0:unoccupied, >0:occupied int32 Size; }; public: // constructor FRangeAllocator(uint32 TotalSize) { FRange NewRange; NewRange.SetUnOccupied(0, TotalSize); Entries.Add(NewRange); } // specified range must be non occupied void OccupyRange(FRange InRange) { check(InRange.IsValid()); check(InRange.IsOccupied()); for(uint32 i = 0, Num = Entries.Num(); i < Num; ++i) { FRange& ref = Entries[i]; if(!ref.IsOccupied()) { int32 OverlapSize = ref.GetEnd() - InRange.GetStart(); if(OverlapSize > 0) { int32 FrontCutSize = InRange.GetStart() - ref.GetStart(); // there is some front part we cut off if(FrontCutSize > 0) { FRange NewFrontRange; NewFrontRange.SetUnOccupied(InRange.GetStart(), ref.ComputeSize() - FrontCutSize); ref.SetUnOccupied(ref.GetStart(), FrontCutSize); ++i; // remaining is added behind the found element Entries.Insert(NewFrontRange, i); // don't access ref or Num any more - Entries[] might be reallocated } check(Entries[i].GetStart() == InRange.GetStart()); int32 BackCutSize = Entries[i].ComputeSize() - InRange.ComputeSize(); // otherwise the range was already occupied or not enough space was left (internal error) check(BackCutSize >= 0); // there is some back part we cut off if(BackCutSize > 0) { FRange NewBackRange; NewBackRange.SetUnOccupied(Entries[i].GetStart() + InRange.ComputeSize(), BackCutSize); Entries.Insert(NewBackRange, i + 1); } Entries[i] = InRange; return; } } } } // @param InSize >0 FRange AllocRange(uint32 InSize)//, uint32 Alignment) { check(InSize > 0); for(uint32 i = 0, Num = Entries.Num(); i < Num; ++i) { FRange& ref = Entries[i]; if(!ref.IsOccupied()) { uint32 RefSize = ref.ComputeSize(); // take the first fitting one - later we could optimize for minimal fragmentation if(RefSize >= InSize) { ref.MakeOccupied(InSize); FRange Ret = ref; if(RefSize > InSize) { FRange NewRange; NewRange.SetUnOccupied(ref.GetEnd(), RefSize - InSize); // remaining is added behind the found element Entries.Insert(NewRange, i + 1); } return Ret; } } } // nothing found return FRange(); } // @param In needs to be what was returned by AllocRange() void ReleaseRange(FRange In) { int32 Index = Entries.Find(In); check(Index != INDEX_NONE); FRange& refIndex = Entries[Index]; refIndex.MakeUnOccupied(); Compacten(Index); } // for debugging uint32 GetNumEntries() const { return Entries.Num(); } // for debugging uint32 ComputeUnoccupiedSize() const { uint32 Ret = 0; for(uint32 i = 0, Num = Entries.Num(); i < Num; ++i) { const FRange& ref = Entries[i]; if(!ref.IsOccupied()) { uint32 RefSize = ref.ComputeSize(); Ret += RefSize; } } return Ret; } private: // compact unoccupied ranges void Compacten(uint32 StartIndex) { check(!Entries[StartIndex].IsOccupied()); if(StartIndex && !Entries[StartIndex-1].IsOccupied()) { // Seems we can combine with the element before, // searching further is not needed as we assume the buffer was compact before the last change. --StartIndex; } uint32 ElementsToRemove = 0; uint32 SizeGained = 0; for(uint32 i = StartIndex + 1, Num = Entries.Num(); i < Num; ++i) { FRange& ref = Entries[i]; if(!ref.IsOccupied()) { ++ElementsToRemove; SizeGained += ref.ComputeSize(); } else { break; } } if(ElementsToRemove) { Entries.RemoveAt(StartIndex + 1, ElementsToRemove, EAllowShrinking::No); Entries[StartIndex].ExtendUnoccupied(SizeGained); } } public: static void Test() { // testing code #if !UE_BUILD_SHIPPING { // create FRangeAllocator A(10); check(A.GetNumEntries() == 1); check(A.ComputeUnoccupiedSize() == 10); // successfully alloc FRangeAllocator::FRange a = A.AllocRange(3); check(a.GetStart() == 0); check(a.GetEnd() == 3); check(a.IsOccupied()); check(A.GetNumEntries() == 2); check(A.ComputeUnoccupiedSize() == 7); // successfully alloc FRangeAllocator::FRange b = A.AllocRange(4); check(b.GetStart() == 3); check(b.GetEnd() == 7); check(b.IsOccupied()); check(A.GetNumEntries() == 3); check(A.ComputeUnoccupiedSize() == 3); // failed alloc FRangeAllocator::FRange c = A.AllocRange(4); check(!c.IsValid()); check(!c.IsOccupied()); check(A.GetNumEntries() == 3); check(A.ComputeUnoccupiedSize() == 3); // successfully alloc FRangeAllocator::FRange d = A.AllocRange(3); check(d.GetStart() == 7); check(d.GetEnd() == 10); check(d.IsOccupied()); check(A.GetNumEntries() == 3); check(A.ComputeUnoccupiedSize() == 0); A.ReleaseRange(b); check(A.GetNumEntries() == 3); check(A.ComputeUnoccupiedSize() == 4); A.ReleaseRange(a); check(A.GetNumEntries() == 2); check(A.ComputeUnoccupiedSize() == 7); A.ReleaseRange(d); check(A.GetNumEntries() == 1); check(A.ComputeUnoccupiedSize() == 10); // we are back to a clean start FRangeAllocator::FRange e = A.AllocRange(10); check(e.GetStart() == 0); check(e.GetEnd() == 10); check(e.IsOccupied()); check(A.GetNumEntries() == 1); check(A.ComputeUnoccupiedSize() == 0); A.ReleaseRange(e); check(A.GetNumEntries() == 1); check(A.ComputeUnoccupiedSize() == 10); // we are back to a clean start // create define range we want to block out FRangeAllocator::FRange f; f.SetOccupied(2, 4); A.OccupyRange(f); check(A.GetNumEntries() == 3); check(A.ComputeUnoccupiedSize() == 6); FRangeAllocator::FRange g = A.AllocRange(2); check(g.GetStart() == 0); check(g.GetEnd() == 2); check(g.IsOccupied()); check(A.GetNumEntries() == 3); check(A.ComputeUnoccupiedSize() == 4); FRangeAllocator::FRange h = A.AllocRange(4); check(h.GetStart() == 6); check(h.GetEnd() == 10); check(h.IsOccupied()); check(A.GetNumEntries() == 3); check(A.ComputeUnoccupiedSize() == 0); } #endif // !UE_BUILD_SHIPPING } private: // ordered from small to large (for efficient compactening) TArray Entries; }; extern D3D11RHI_API FD3D11DynamicRHI* GD3D11RHI;