// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= D3D12Util.h: D3D RHI utility definitions. =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "Containers/Queue.h" #include "D3D12RHICommon.h" #include "D3D12Queue.h" #include "DXGIUtilities.h" #include "RenderUtils.h" #include "ShaderCore.h" namespace D3D12RHI { /** * Checks that the given result isn't a failure. If it is, the application exits with an appropriate error message. * @param Result - The result code to check * @param Code - The code which yielded the result. * @param Filename - The filename of the source file containing Code. * @param Line - The line number of Code within Filename. */ extern void VerifyD3D12Result(HRESULT Result, const ANSICHAR* Code, const ANSICHAR* Filename, uint32 Line, ID3D12Device* Device, FString Message = FString()); /** * Checks that the given result isn't a failure. If it is, the application exits with an appropriate error message. * @param Result - The result code to check * @param Code - The code which yielded the result. * @param Filename - The filename of the source file containing Code. * @param Line - The line number of Code within Filename. */ extern void VerifyD3D12CreateTextureResult(HRESULT D3DResult, const ANSICHAR* Code, const ANSICHAR* Filename, uint32 Line, const D3D12_RESOURCE_DESC& TextureDesc, ID3D12Device* Device); /** * A macro for using VERIFYD3D12RESULT that automatically passes in the code and filename/line. */ #define VERIFYD3D12RESULT_LAMBDA(x, Device, Lambda) {HRESULT hres = x; if (FAILED(hres)) { VerifyD3D12Result(hres, #x, __FILE__, __LINE__, Device, Lambda()); }} #define VERIFYD3D12RESULT_EX(x, Device) {HRESULT hres = x; if (FAILED(hres)) { VerifyD3D12Result(hres, #x, __FILE__, __LINE__, Device); }} #define VERIFYD3D12RESULT(x) {HRESULT hres = x; if (FAILED(hres)) { VerifyD3D12Result(hres, #x, __FILE__, __LINE__, nullptr); }} #define VERIFYD3D12CREATETEXTURERESULT(x, Desc, Device) {HRESULT hres = x; if (FAILED(hres)) { VerifyD3D12CreateTextureResult(hres, #x, __FILE__, __LINE__, Desc, Device); }} /** Checks if given GPU virtual address corresponds to any known resource allocations and logs results */ void LogPageFaultData(class FD3D12Adapter* InAdapter, FD3D12Device* InDevice, D3D12_GPU_VIRTUAL_ADDRESS InPageFaultAddress); } // namespace D3D12RHI using namespace D3D12RHI; class FD3D12Buffer; class FD3D12Resource; class FD3D12ResourceLocation; class FD3D12Texture; bool ShouldSetD3D12ResourceName(const FD3D12ResourceLocation& ResourceLocation); void SetD3D12ObjectName(ID3D12Object* Object, const TCHAR* Name); void SetD3D12ResourceName(FD3D12ResourceLocation& ResourceLocation, const TCHAR* Name); void SetD3D12ResourceName(FD3D12Resource* Resource, const TCHAR* Name); void SetD3D12ResourceName(FD3D12Buffer* Resource, const TCHAR* Name); void SetD3D12ResourceName(FD3D12Texture* Resource, const TCHAR* Name); FString GetD312ObjectName(ID3D12Object* const Object); enum EShaderVisibility { SV_Vertex, SV_Pixel, SV_Geometry, #if PLATFORM_SUPPORTS_MESH_SHADERS SV_Mesh, SV_Amplification, #endif SV_All, SV_ShaderVisibilityCount }; enum ERootSignatureType { RS_Raster, RS_RayTracingGlobal, RS_RayTracingLocal, RS_WorkGraphGlobal, RS_WorkGraphLocalCompute, RS_WorkGraphLocalRaster, }; struct FShaderRegisterCounts { uint8 SamplerCount; uint8 ConstantBufferCount; uint8 ShaderResourceCount; uint8 UnorderedAccessCount; inline bool operator==(const FShaderRegisterCounts& RHS) const { return SamplerCount == RHS.SamplerCount && ConstantBufferCount == RHS.ConstantBufferCount && ShaderResourceCount == RHS.ShaderResourceCount && UnorderedAccessCount == RHS.UnorderedAccessCount; } friend uint32 GetTypeHash(const FShaderRegisterCounts& Counts) { uint32 Hash = GetTypeHash(Counts.SamplerCount); Hash = HashCombineFast(Hash, GetTypeHash(Counts.ConstantBufferCount)); Hash = HashCombineFast(Hash, GetTypeHash(Counts.ShaderResourceCount)); Hash = HashCombineFast(Hash, GetTypeHash(Counts.UnorderedAccessCount)); return Hash; } }; struct FD3D12QuantizedBoundShaderState { FRHIShaderBindingLayout ShaderBindingLayout; TStaticArray RegisterCounts; ERootSignatureType RootSignatureType = RS_Raster; union { struct { uint8 bAllowIAInputLayout : 1; uint8 bNeedsAgsIntrinsicsSpace : 1; uint8 bUseDiagnosticBuffer : 1; uint8 bUseDirectlyIndexedResourceHeap : 1; uint8 bUseDirectlyIndexedSamplerHeap : 1; uint8 bUseRootConstants : 1; uint8 Padding : 2; }; uint8 Flags; }; inline bool operator==(const FD3D12QuantizedBoundShaderState& RHS) const { return ShaderBindingLayout == RHS.ShaderBindingLayout && RegisterCounts == RHS.RegisterCounts && RootSignatureType == RHS.RootSignatureType && Flags == RHS.Flags; } friend uint32 GetTypeHash(const FD3D12QuantizedBoundShaderState& State) { uint32 Hash = GetTypeHash(State.ShaderBindingLayout); for (int32 Index = 0; Index < SV_ShaderVisibilityCount; ++Index) { Hash = HashCombineFast(Hash, GetTypeHash(State.RegisterCounts[Index])); } Hash = HashCombineFast(Hash, GetTypeHash(State.RootSignatureType)); Hash = HashCombineFast(Hash, GetTypeHash(State.Flags)); return Hash; } static void InitShaderRegisterCounts(D3D12_RESOURCE_BINDING_TIER ResourceBindingTier, const FShaderCodePackedResourceCounts& Counts, FShaderRegisterCounts& Shader, bool bAllowUAVs = false); }; /** * Convert from ECubeFace to D3DCUBEMAP_FACES type * @param Face - ECubeFace type to convert * @return D3D cube face enum value */ FORCEINLINE uint32 GetD3D12CubeFace(ECubeFace Face) { switch (Face) { case CubeFace_PosX: default: return 0;//D3DCUBEMAP_FACE_POSITIVE_X; case CubeFace_NegX: return 1;//D3DCUBEMAP_FACE_NEGATIVE_X; case CubeFace_PosY: return 2;//D3DCUBEMAP_FACE_POSITIVE_Y; case CubeFace_NegY: return 3;//D3DCUBEMAP_FACE_NEGATIVE_Y; case CubeFace_PosZ: return 4;//D3DCUBEMAP_FACE_POSITIVE_Z; case CubeFace_NegZ: return 5;//D3DCUBEMAP_FACE_NEGATIVE_Z; }; } /** * Calculate a subresource index for a texture */ FORCEINLINE uint32 CalcSubresource(uint32 MipSlice, uint32 ArraySlice, uint32 MipLevels) { return MipSlice + ArraySlice * MipLevels; } /** * Keeps track of Locks for D3D12 objects */ class FD3D12LockedKey { public: void* SourceObject; uint32 Subresource; public: FD3D12LockedKey() : SourceObject(NULL) , Subresource(0) {} FD3D12LockedKey(FD3D12Resource* source, uint32 subres = 0) : SourceObject((void*)source) , Subresource(subres) {} FD3D12LockedKey(class FD3D12ResourceLocation* source, uint32 subres = 0) : SourceObject((void*)source) , Subresource(subres) {} template FD3D12LockedKey(ClassType* source, uint32 subres = 0) : SourceObject((void*)source) , Subresource(subres) {} bool operator==(const FD3D12LockedKey& Other) const { return SourceObject == Other.SourceObject && Subresource == Other.Subresource; } bool operator!=(const FD3D12LockedKey& Other) const { return SourceObject != Other.SourceObject || Subresource != Other.Subresource; } uint32 GetHash() const { return PointerHash(SourceObject); } /** Hashing function. */ friend uint32 GetTypeHash(const FD3D12LockedKey& K) { return K.GetHash(); } }; class FD3D12RenderTargetView; class FD3D12DepthStencilView; /** * Class for retrieving render targets currently bound to the device context. */ class FD3D12BoundRenderTargets { public: /** Initialization constructor: requires the state cache. */ explicit FD3D12BoundRenderTargets(FD3D12RenderTargetView** RTArray, uint32 NumActiveRTs, FD3D12DepthStencilView* DSView); /** Destructor. */ ~FD3D12BoundRenderTargets(); /** Accessors. */ FORCEINLINE int32 GetNumActiveTargets() const { return NumActiveTargets; } FORCEINLINE FD3D12RenderTargetView* GetRenderTargetView(int32 TargetIndex) { return RenderTargetViews[TargetIndex]; } FORCEINLINE FD3D12DepthStencilView* GetDepthStencilView() { return DepthStencilView; } private: /** Active render target views. */ FD3D12RenderTargetView* RenderTargetViews[MaxSimultaneousRenderTargets]; /** Active depth stencil view. */ FD3D12DepthStencilView* DepthStencilView; /** The number of active render targets. */ int32 NumActiveTargets; }; void LogExecuteCommandLists(uint32 NumCommandLists, ID3D12CommandList *const *ppCommandLists); FString ConvertToResourceStateString(uint32 ResourceState); void LogResourceBarriers(TConstArrayView Barriers, ID3D12CommandList *const pCommandList, ED3D12QueueType QueueType, const FString& ResourceName); // Custom resource states // To Be Determined (TBD) means we need to fill out a resource barrier before the command list is executed. #define D3D12_RESOURCE_STATE_TBD D3D12_RESOURCE_STATES(-1 ^ (1 << 31)) #define D3D12_RESOURCE_STATE_CORRUPT D3D12_RESOURCE_STATES(-2 ^ (1 << 31)) static bool IsValidD3D12ResourceState(D3D12_RESOURCE_STATES InState) { return (InState != D3D12_RESOURCE_STATE_TBD && InState != D3D12_RESOURCE_STATE_CORRUPT); } static bool IsDirectQueueExclusiveD3D12State(D3D12_RESOURCE_STATES InState) { return EnumHasAnyFlags(InState, D3D12_RESOURCE_STATE_RENDER_TARGET | D3D12_RESOURCE_STATE_DEPTH_WRITE | D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); } class FD3D12Texture; D3D12_RESOURCE_STATES GetD3D12ResourceState(ERHIAccess InRHIAccess, ED3D12QueueType QueueType, FD3D12Texture* InRHID3D12Texture); /** * The base class of threadsafe reference counted objects. */ template struct FThreadsafeQueue { private: mutable FCriticalSection SynchronizationObject; // made this mutable so this class can have const functions and still be thread safe TQueue Items; uint32 Size = 0; public: inline const uint32 GetSize() const { return Size; } void Enqueue(const Type& Item) { FScopeLock ScopeLock(&SynchronizationObject); Items.Enqueue(Item); Size++; } bool Dequeue(Type& Result) { FScopeLock ScopeLock(&SynchronizationObject); Size--; return Items.Dequeue(Result); } template bool Dequeue(Type& Result, const CompareFunc& Func) { FScopeLock ScopeLock(&SynchronizationObject); if (Items.Peek(Result)) { if (Func(Result)) { Items.Dequeue(Result); Size--; return true; } } return false; } template bool BatchDequeue(TArray& Result, const CompareFunc& Func, uint32 MaxItems) { FScopeLock ScopeLock(&SynchronizationObject); uint32 i = 0; Type Item; while (Items.Peek(Item) && i <= MaxItems) { if (Func(Item)) { Items.Dequeue(Item); Size--; Result.Emplace(Item); i++; } else { break; } } return i > 0; } bool Peek(Type& Result) { FScopeLock ScopeLock(&SynchronizationObject); return Items.Peek(Result); } bool IsEmpty() { FScopeLock ScopeLock(&SynchronizationObject); return Items.IsEmpty(); } void Empty() { FScopeLock ScopeLock(&SynchronizationObject); Type Result; while (Items.Dequeue(Result)) {} } }; inline bool IsCPUWritable(D3D12_HEAP_TYPE HeapType, const D3D12_HEAP_PROPERTIES *pCustomHeapProperties = nullptr) { check(HeapType == D3D12_HEAP_TYPE_CUSTOM ? pCustomHeapProperties != nullptr : true); return HeapType == D3D12_HEAP_TYPE_UPLOAD || (HeapType == D3D12_HEAP_TYPE_CUSTOM && (pCustomHeapProperties->CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE || pCustomHeapProperties->CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_BACK)); } inline bool IsGPUOnly(D3D12_HEAP_TYPE HeapType, const D3D12_HEAP_PROPERTIES *pCustomHeapProperties = nullptr) { check(HeapType == D3D12_HEAP_TYPE_CUSTOM ? pCustomHeapProperties != nullptr : true); return HeapType == D3D12_HEAP_TYPE_DEFAULT || (HeapType == D3D12_HEAP_TYPE_CUSTOM && (pCustomHeapProperties->CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE)); } inline bool IsCPUAccessible(D3D12_HEAP_TYPE HeapType, const D3D12_HEAP_PROPERTIES* pCustomHeapProperties = nullptr) { return !IsGPUOnly(HeapType, pCustomHeapProperties); } inline D3D12_RESOURCE_STATES DetermineInitialResourceState(D3D12_HEAP_TYPE HeapType, const D3D12_HEAP_PROPERTIES *pCustomHeapProperties = nullptr) { if (HeapType == D3D12_HEAP_TYPE_DEFAULT || IsCPUWritable(HeapType, pCustomHeapProperties)) { return D3D12_RESOURCE_STATE_GENERIC_READ; } else { check(HeapType == D3D12_HEAP_TYPE_READBACK); return D3D12_RESOURCE_STATE_COPY_DEST; } } static inline uint64 GetTilesNeeded(uint32 Width, uint32 Height, uint32 Depth, const D3D12_TILE_SHAPE& Shape) { return uint64((Width + Shape.WidthInTexels - 1) / Shape.WidthInTexels) * ((Height + Shape.HeightInTexels - 1) / Shape.HeightInTexels) * ((Depth + Shape.DepthInTexels - 1) / Shape.DepthInTexels); } static void Get4KTileShape(D3D12_TILE_SHAPE* pTileShape, DXGI_FORMAT DXGIFormat, EPixelFormat UEFormat, D3D12_RESOURCE_DIMENSION Dimension, uint32 SampleCount) { //Bits per unit uint32 BPU = GPixelFormats[UEFormat].BlockBytes * 8; switch (Dimension) { case D3D12_RESOURCE_DIMENSION_BUFFER: case D3D12_RESOURCE_DIMENSION_TEXTURE1D: { check(!UE::DXGIUtilities::IsBlockCompressedFormat(DXGIFormat)); pTileShape->WidthInTexels = (BPU == 0) ? 4096 : 4096 * 8 / BPU; pTileShape->HeightInTexels = 1; pTileShape->DepthInTexels = 1; } break; case D3D12_RESOURCE_DIMENSION_TEXTURE2D: { pTileShape->DepthInTexels = 1; if (UE::DXGIUtilities::IsBlockCompressedFormat(DXGIFormat)) { // Currently only supported block sizes are 64 and 128. // These equations calculate the size in texels for a tile. It relies on the fact that 16*16*16 blocks fit in a tile if the block size is 128 bits. check(BPU == 64 || BPU == 128); pTileShape->WidthInTexels = 16 * UE::DXGIUtilities::GetWidthAlignment(DXGIFormat); pTileShape->HeightInTexels = 16 * UE::DXGIUtilities::GetHeightAlignment(DXGIFormat); if (BPU == 64) { // If bits per block are 64 we double width so it takes up the full tile size. // This is only true for BC1 and BC4 check((DXGIFormat >= DXGI_FORMAT_BC1_TYPELESS && DXGIFormat <= DXGI_FORMAT_BC1_UNORM_SRGB) || (DXGIFormat >= DXGI_FORMAT_BC4_TYPELESS && DXGIFormat <= DXGI_FORMAT_BC4_SNORM)); pTileShape->WidthInTexels *= 2; } } else { if (BPU <= 8) { pTileShape->WidthInTexels = 64; pTileShape->HeightInTexels = 64; } else if (BPU <= 16) { pTileShape->WidthInTexels = 64; pTileShape->HeightInTexels = 32; } else if (BPU <= 32) { pTileShape->WidthInTexels = 32; pTileShape->HeightInTexels = 32; } else if (BPU <= 64) { pTileShape->WidthInTexels = 32; pTileShape->HeightInTexels = 16; } else if (BPU <= 128) { pTileShape->WidthInTexels = 16; pTileShape->HeightInTexels = 16; } else { check(false); } if (SampleCount <= 1) { /* Do nothing */ } else if (SampleCount <= 2) { pTileShape->WidthInTexels /= 2; pTileShape->HeightInTexels /= 1; } else if (SampleCount <= 4) { pTileShape->WidthInTexels /= 2; pTileShape->HeightInTexels /= 2; } else if (SampleCount <= 8) { pTileShape->WidthInTexels /= 4; pTileShape->HeightInTexels /= 2; } else if (SampleCount <= 16) { pTileShape->WidthInTexels /= 4; pTileShape->HeightInTexels /= 4; } else { check(false); } check(UE::DXGIUtilities::GetWidthAlignment(DXGIFormat) == 1); check(UE::DXGIUtilities::GetHeightAlignment(DXGIFormat) == 1); } break; } case D3D12_RESOURCE_DIMENSION_TEXTURE3D: { if (UE::DXGIUtilities::IsBlockCompressedFormat(DXGIFormat)) { // Currently only supported block sizes are 64 and 128. // These equations calculate the size in texels for a tile. It relies on the fact that 16*16*16 blocks fit in a tile if the block size is 128 bits. check(BPU == 64 || BPU == 128); pTileShape->WidthInTexels = 8 * UE::DXGIUtilities::GetWidthAlignment(DXGIFormat); pTileShape->HeightInTexels = 8 * UE::DXGIUtilities::GetHeightAlignment(DXGIFormat); pTileShape->DepthInTexels = 4; if (BPU == 64) { // If bits per block are 64 we double width so it takes up the full tile size. // This is only true for BC1 and BC4 check((DXGIFormat >= DXGI_FORMAT_BC1_TYPELESS && DXGIFormat <= DXGI_FORMAT_BC1_UNORM_SRGB) || (DXGIFormat >= DXGI_FORMAT_BC4_TYPELESS && DXGIFormat <= DXGI_FORMAT_BC4_SNORM)); pTileShape->DepthInTexels *= 2; } } else { if (BPU <= 8) { pTileShape->WidthInTexels = 16; pTileShape->HeightInTexels = 16; pTileShape->DepthInTexels = 16; } else if (BPU <= 16) { pTileShape->WidthInTexels = 16; pTileShape->HeightInTexels = 16; pTileShape->DepthInTexels = 8; } else if (BPU <= 32) { pTileShape->WidthInTexels = 16; pTileShape->HeightInTexels = 8; pTileShape->DepthInTexels = 8; } else if (BPU <= 64) { pTileShape->WidthInTexels = 8; pTileShape->HeightInTexels = 8; pTileShape->DepthInTexels = 8; } else if (BPU <= 128) { pTileShape->WidthInTexels = 8; pTileShape->HeightInTexels = 8; pTileShape->DepthInTexels = 4; } else { check(false); } check(UE::DXGIUtilities::GetWidthAlignment(DXGIFormat) == 1); check(UE::DXGIUtilities::GetHeightAlignment(DXGIFormat) == 1); } } break; } } #define ASSERT_RESOURCE_STATES 0 // Disabled for now. #if ASSERT_RESOURCE_STATES class FD3D12View; struct FD3D12ViewSubset; // template // bool AssertResourceState(ID3D12CommandList* pCommandList, FD3D12View* pView, const D3D12_RESOURCE_STATES& State); bool AssertResourceState(ID3D12CommandList* pCommandList, FD3D12Resource* pResource, const D3D12_RESOURCE_STATES& State, uint32 Subresource); bool AssertResourceState(ID3D12CommandList* pCommandList, FD3D12Resource* pResource, const D3D12_RESOURCE_STATES& State, const FD3D12ViewSubset& ViewSubset); #endif FORCEINLINE_DEBUGGABLE D3D12_PRIMITIVE_TOPOLOGY_TYPE TranslatePrimitiveTopologyType(EPrimitiveTopologyType TopologyType) { switch (TopologyType) { case EPrimitiveTopologyType::Triangle: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; case EPrimitiveTopologyType::Patch: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH; case EPrimitiveTopologyType::Line: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; case EPrimitiveTopologyType::Point: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; default: ensure(0); return D3D12_PRIMITIVE_TOPOLOGY_TYPE_UNDEFINED; } } FORCEINLINE_DEBUGGABLE D3D_PRIMITIVE_TOPOLOGY TranslatePrimitiveType(EPrimitiveType PrimitiveType) { switch (PrimitiveType) { case PT_TriangleList: return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; case PT_TriangleStrip: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; case PT_LineList: return D3D_PRIMITIVE_TOPOLOGY_LINELIST; case PT_PointList: return D3D_PRIMITIVE_TOPOLOGY_POINTLIST; #if defined(D3D12RHI_PRIMITIVE_TOPOLOGY_RECTLIST) case PT_RectList: return D3D12RHI_PRIMITIVE_TOPOLOGY_RECTLIST; #endif default: ensure(0); return D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; } } #pragma warning(push) #pragma warning(disable: 4063) FORCEINLINE_DEBUGGABLE D3D12_PRIMITIVE_TOPOLOGY_TYPE D3D12PrimitiveTypeToTopologyType(D3D_PRIMITIVE_TOPOLOGY PrimitiveType) { switch (PrimitiveType) { case D3D_PRIMITIVE_TOPOLOGY_POINTLIST: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; case D3D_PRIMITIVE_TOPOLOGY_LINELIST: case D3D_PRIMITIVE_TOPOLOGY_LINESTRIP: case D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ: case D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST: case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP: case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ: case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; #if defined(D3D12RHI_PRIMITIVE_TOPOLOGY_RECTLIST) case D3D12RHI_PRIMITIVE_TOPOLOGY_RECTLIST: return D3D12RHI_PRIMITIVE_TOPOLOGY_TYPE_RECT; #endif case D3D_PRIMITIVE_TOPOLOGY_UNDEFINED: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_UNDEFINED; default: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH; } } #pragma warning(pop) // @return 0xffffffff if not not supported FORCEINLINE_DEBUGGABLE uint32 GetMaxMSAAQuality(uint32 SampleCount) { if (SampleCount <= DX_MAX_MSAA_COUNT) { // 0 has better quality (a more even distribution) // higher quality levels might be useful for non box filtered AA or when using weighted samples return 0; } // not supported return 0xffffffff; } struct FD3D12ScopeLock { public: FD3D12ScopeLock(FCriticalSection* CritSec) : CS(CritSec) { CS->Lock(); } ~FD3D12ScopeLock() { CS->Unlock(); } private: FCriticalSection* CS; }; struct FD3D12ScopeNoLock { public: FD3D12ScopeNoLock(FCriticalSection* CritSec) { /* Do Nothing! */ } ~FD3D12ScopeNoLock() { /* Do Nothing! */ } }; FORCEINLINE_DEBUGGABLE bool CheckResourceStateCompatibility(D3D12_RESOURCE_STATES State, D3D12_RESOURCE_FLAGS Flags, FString& IncompatibilityReason) { // Check for ALLOW_RENDER_TARGET compatibility if (State & D3D12_RESOURCE_STATE_RENDER_TARGET) { if (!(Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { IncompatibilityReason.Append(TEXT("D3D12_RESOURCE_STATE_RENDER_TARGET requires D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET.\n")); return false; } } // Check for ALLOW_DEPTH_STENCIL compatibility if (State & D3D12_RESOURCE_STATE_DEPTH_WRITE || State & D3D12_RESOURCE_STATE_DEPTH_READ) { if (!(Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { IncompatibilityReason.Append(TEXT("D3D12_RESOURCE_STATE_DEPTH_WRITE or D3D12_RESOURCE_STATE_DEPTH_READ requires D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL.\n")); return false; } } // Check for ALLOW_UNORDERED_ACCESS compatibility if (State & D3D12_RESOURCE_STATE_UNORDERED_ACCESS) { if (!(Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) { IncompatibilityReason.Append(TEXT("D3D12_RESOURCE_STATE_UNORDERED_ACCESS requires D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS.\n")); return false; } } // Check for DENY_SHADER_RESOURCE incompatibility if (State & (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)) { if (Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) { IncompatibilityReason.Append(TEXT("D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE or D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE cannot be used with D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE.\n")); return false; } } return true; }