Files
UnrealEngine/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Texture.cpp
2025-05-18 13:04:45 +08:00

1776 lines
63 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
D3D11Texture.cpp: D3D texture RHI implementation.
=============================================================================*/
#include "D3D11RHIPrivate.h"
// For Depth Bounds Test interface
#include "Windows/AllowWindowsPlatformTypes.h"
#if WITH_NVAPI
#include "nvapi.h"
#endif
#if WITH_AMD_AGS
#include "amd_ags.h"
#endif
#include "Windows/HideWindowsPlatformTypes.h"
#include "HAL/LowLevelMemStats.h"
#include "HAL/LowLevelMemTracker.h"
#include "ProfilingDebugging/MemoryTrace.h"
#include "ProfilingDebugging/AssetMetadataTrace.h"
#include "RHICoreStats.h"
#include "RHICoreTexture.h"
#include "RHIUtilities.h"
#include "RHICoreTextureInitializer.h"
#include "RHITextureUtils.h"
int64 FD3D11GlobalStats::GDedicatedVideoMemory = 0;
int64 FD3D11GlobalStats::GDedicatedSystemMemory = 0;
int64 FD3D11GlobalStats::GSharedSystemMemory = 0;
int64 FD3D11GlobalStats::GTotalGraphicsMemory = 0;
/*-----------------------------------------------------------------------------
Texture allocator support.
-----------------------------------------------------------------------------*/
// Note: This function can be called from many different threads
// @param TextureSize >0 to allocate, <0 to deallocate
// @param b3D true:3D, false:2D or cube map
void UpdateD3D11TextureStats(FD3D11Texture& Texture, bool bAllocating)
{
const FRHITextureDesc& TextureDesc = Texture.GetDesc();
uint32 BindFlags;
if (TextureDesc.IsTexture3D())
{
D3D11_TEXTURE3D_DESC Desc;
Texture.GetD3D11Texture3D()->GetDesc(&Desc);
BindFlags = Desc.BindFlags;
}
else
{
D3D11_TEXTURE2D_DESC Desc;
Texture.GetD3D11Texture2D()->GetDesc(&Desc);
BindFlags = Desc.BindFlags;
}
const uint64 TextureSize = Texture.GetMemorySize();
const bool bOnlyStreamableTexturesInTexturePool = false;
UE::RHICore::UpdateGlobalTextureStats(TextureDesc, TextureSize, bOnlyStreamableTexturesInTexturePool, bAllocating);
if (bAllocating)
{
INC_DWORD_STAT(STAT_D3D11TexturesAllocated);
}
else
{
INC_DWORD_STAT(STAT_D3D11TexturesReleased);
}
// On Windows there is no way to hook into the low level d3d allocations and frees.
// This means that we must manually add the tracking here.
if (bAllocating)
{
LLM_IF_ENABLED(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Platform, Texture.GetResource(), TextureSize, ELLMTag::GraphicsPlatform));
LLM_IF_ENABLED(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Default , Texture.GetResource(), TextureSize, ELLMTag::Textures));
{
LLM(UE_MEMSCOPE_DEFAULT(ELLMTag::Textures));
MemoryTrace_Alloc((uint64)Texture.GetResource(), TextureSize, 1024, EMemoryTraceRootHeap::VideoMemory);
}
}
else
{
LLM_IF_ENABLED(FLowLevelMemTracker::Get().OnLowLevelFree(ELLMTracker::Platform, Texture.GetResource()));
LLM_IF_ENABLED(FLowLevelMemTracker::Get().OnLowLevelFree(ELLMTracker::Default , Texture.GetResource()));
MemoryTrace_Free((uint64)Texture.GetResource(), EMemoryTraceRootHeap::VideoMemory);
}
}
FDynamicRHI::FRHICalcTextureSizeResult FD3D11DynamicRHI::RHICalcTexturePlatformSize(FRHITextureDesc const& Desc, uint32 FirstMipIndex)
{
// D3D11 does not provide a way to compute the actual driver/GPU specific in-memory size of a texture.
// Fallback to the estimate based on the texture's dimensions / format etc.
FDynamicRHI::FRHICalcTextureSizeResult Result;
Result.Size = Desc.CalcMemorySizeEstimate(FirstMipIndex);
Result.Align = 1;
return Result;
}
void FD3D11DynamicRHI::RHIGetTextureMemoryStats(FTextureMemoryStats& OutStats)
{
UE::RHICore::FillBaselineTextureMemoryStats(OutStats);
OutStats.DedicatedVideoMemory = FD3D11GlobalStats::GDedicatedVideoMemory;
OutStats.DedicatedSystemMemory = FD3D11GlobalStats::GDedicatedSystemMemory;
OutStats.SharedSystemMemory = FD3D11GlobalStats::GSharedSystemMemory;
OutStats.TotalGraphicsMemory = FD3D11GlobalStats::GTotalGraphicsMemory ? FD3D11GlobalStats::GTotalGraphicsMemory : -1;
OutStats.LargestContiguousAllocation = OutStats.StreamingMemorySize;
}
bool FD3D11DynamicRHI::RHIGetTextureMemoryVisualizeData( FColor* /*TextureData*/, int32 /*SizeX*/, int32 /*SizeY*/, int32 /*Pitch*/, int32 /*PixelSize*/ )
{
// currently only implemented for console (Note: Keep this function for further extension. Talk to NiklasS for more info.)
return false;
}
// Work around an issue with the WARP device & BC7
// Creating two views with different formats (DXGI_FORMAT_BC7_UNORM vs DXGI_FORMAT_BC7_UNORM_SRGB)
// will result in a crash inside d3d10warp.dll when creating the second view
void ApplyBC7SoftwareAdapterWorkaround(bool bSoftwareAdapter, D3D11_TEXTURE2D_DESC& Desc)
{
if (bSoftwareAdapter)
{
bool bApplyWorkaround = Desc.Format == DXGI_FORMAT_BC7_TYPELESS
&& Desc.Usage == D3D11_USAGE_DEFAULT
&& Desc.MipLevels == 1
&& Desc.ArraySize == 1
&& Desc.CPUAccessFlags == 0;
if (bApplyWorkaround)
{
Desc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
}
}
}
/** If true, guard texture creates with SEH to log more information about a driver crash we are seeing during texture streaming. */
#define GUARDED_TEXTURE_CREATES (!(UE_BUILD_SHIPPING || UE_BUILD_TEST || PLATFORM_COMPILER_CLANG))
/**
* Creates a 2D texture optionally guarded by a structured exception handler.
*/
static void SafeCreateTexture2D(ID3D11Device* Direct3DDevice, int32 UEFormat, const D3D11_TEXTURE2D_DESC* TextureDesc, const D3D11_SUBRESOURCE_DATA* SubResourceData, ID3D11Texture2D** OutTexture2D, const TCHAR* DebugName)
{
#if GUARDED_TEXTURE_CREATES
bool bDriverCrash = true;
__try
{
#endif // #if GUARDED_TEXTURE_CREATES
VERIFYD3D11CREATETEXTURERESULT(
Direct3DDevice->CreateTexture2D(TextureDesc,SubResourceData,OutTexture2D),
UEFormat,
TextureDesc->Width,
TextureDesc->Height,
TextureDesc->ArraySize,
TextureDesc->Format,
TextureDesc->MipLevels,
TextureDesc->BindFlags,
TextureDesc->Usage,
TextureDesc->CPUAccessFlags,
TextureDesc->MiscFlags,
TextureDesc->SampleDesc.Count,
TextureDesc->SampleDesc.Quality,
SubResourceData ? SubResourceData->pSysMem : nullptr,
SubResourceData ? SubResourceData->SysMemPitch : 0,
SubResourceData ? SubResourceData->SysMemSlicePitch : 0,
Direct3DDevice,
DebugName
);
#if GUARDED_TEXTURE_CREATES
bDriverCrash = false;
}
__finally
{
if (bDriverCrash)
{
UE_LOG(LogD3D11RHI,Error,
TEXT("Driver crashed while creating texture: %ux%ux%u %s(0x%08x) with %u mips, PF_ %d"),
TextureDesc->Width,
TextureDesc->Height,
TextureDesc->ArraySize,
UE::DXGIUtilities::GetFormatString(TextureDesc->Format),
(uint32)TextureDesc->Format,
TextureDesc->MipLevels,
UEFormat
);
}
}
#endif // #if GUARDED_TEXTURE_CREATES
}
static void SafeCreateTexture3D(FD3D11Device* Direct3DDevice, int32 UEFormat, const D3D11_TEXTURE3D_DESC* TextureDesc, const D3D11_SUBRESOURCE_DATA* SubResourceData, ID3D11Texture3D** OutTexture2D, const TCHAR* DebugName)
{
#if GUARDED_TEXTURE_CREATES
bool bDriverCrash = true;
__try
{
#endif // #if GUARDED_TEXTURE_CREATES
VERIFYD3D11CREATETEXTURERESULT(
Direct3DDevice->CreateTexture3D(TextureDesc,SubResourceData,OutTexture2D),
UEFormat,
TextureDesc->Width,
TextureDesc->Height,
TextureDesc->Depth,
TextureDesc->Format,
TextureDesc->MipLevels,
TextureDesc->BindFlags,
TextureDesc->Usage,
TextureDesc->CPUAccessFlags,
TextureDesc->MiscFlags,
0,
0,
SubResourceData ? SubResourceData->pSysMem : nullptr,
SubResourceData ? SubResourceData->SysMemPitch : 0,
SubResourceData ? SubResourceData->SysMemSlicePitch : 0,
Direct3DDevice,
DebugName
);
#if GUARDED_TEXTURE_CREATES
bDriverCrash = false;
}
__finally
{
if (bDriverCrash)
{
UE_LOG(LogD3D11RHI,Error,
TEXT("Driver crashed while creating texture: %ux%ux%u %s(0x%08x) with %u mips, PF_ %d"),
TextureDesc->Width,
TextureDesc->Height,
TextureDesc->Depth,
UE::DXGIUtilities::GetFormatString(TextureDesc->Format),
(uint32)TextureDesc->Format,
TextureDesc->MipLevels,
UEFormat
);
}
}
#endif // #if GUARDED_TEXTURE_CREATES
}
FD3D11Texture* FD3D11DynamicRHI::BeginCreateTextureInternal(const FRHITextureCreateDesc& CreateDesc)
{
FD3D11Texture* Texture = new FD3D11Texture(CreateDesc);
return Texture;
}
inline uint32 GetMaxMSAAQuality(uint32 SampleCount)
{
if (SampleCount <= DX_MAX_MSAA_COUNT)
{
return 0;
}
return 0xffffffff;
}
enum class ED3D11TextureCreateViewFlags
{
None = 0,
SRV = 1 << 0,
RTV = 1 << 1,
DSV = 1 << 2,
};
ENUM_CLASS_FLAGS(ED3D11TextureCreateViewFlags);
// Setup a D3D11_TEXTURE#D_DESC structure and return a set of flags telling the caller which view types should be created.
template<typename TDesc>
static ED3D11TextureCreateViewFlags SetupD3D11TextureCommonDesc(TDesc& D3D11Desc, const FRHITextureDesc& TextureDesc, DXGI_FORMAT PlatformResourceFormat)
{
FMemory::Memzero(D3D11Desc);
const bool bTextureArray = TextureDesc.IsTextureArray();
const bool bCubeTexture = TextureDesc.IsTextureCube();
const uint32 SizeX = TextureDesc.Extent.X;
const uint32 SizeY = TextureDesc.Extent.Y;
const uint32 SizeZ = bCubeTexture ? TextureDesc.ArraySize * 6 : TextureDesc.ArraySize;
const EPixelFormat Format = TextureDesc.Format;
const uint32 NumMips = TextureDesc.NumMips;
const uint32 NumSamples = TextureDesc.NumSamples;
const ETextureCreateFlags Flags = TextureDesc.Flags;
check(SizeX > 0 && SizeY > 0 && NumMips > 0);
const bool bSRGB = EnumHasAnyFlags(Flags, ETextureCreateFlags::SRGB);
uint32 CPUAccessFlags = 0;
D3D11_USAGE TextureUsage = D3D11_USAGE_DEFAULT;
bool bCreateShaderResource = true;
uint32 ActualMSAAQuality = GetMaxMSAAQuality(NumSamples);
check(ActualMSAAQuality != 0xffffffff);
check(NumSamples == 1 || !EnumHasAnyFlags(Flags, ETextureCreateFlags::Shared));
const bool bIsMultisampled = NumSamples > 1;
if (EnumHasAnyFlags(Flags, ETextureCreateFlags::CPUReadback))
{
check(!EnumHasAnyFlags(Flags, ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::DepthStencilTargetable | ETextureCreateFlags::ShaderResource));
CPUAccessFlags = D3D11_CPU_ACCESS_READ;
TextureUsage = D3D11_USAGE_STAGING;
bCreateShaderResource = false;
}
if (EnumHasAnyFlags(Flags, ETextureCreateFlags::CPUWritable))
{
CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
TextureUsage = D3D11_USAGE_STAGING;
bCreateShaderResource = false;
}
// Describe the texture.
D3D11Desc.Width = SizeX;
D3D11Desc.Height = SizeY;
D3D11Desc.MipLevels = NumMips;
D3D11Desc.Format = PlatformResourceFormat;
D3D11Desc.Usage = TextureUsage;
D3D11Desc.BindFlags = bCreateShaderResource ? D3D11_BIND_SHADER_RESOURCE : 0;
D3D11Desc.CPUAccessFlags = CPUAccessFlags;
D3D11Desc.MiscFlags = bCubeTexture ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
// NV12/P010 doesn't support SRV in NV12 format so don't create SRV for it.
// Todo: add support for SRVs of underneath luminance & chrominance textures.
if (Format == PF_NV12 || Format == PF_P010)
{
// This has to be set after the bind flags because it is valid to bind R8 or B8G8 to this
// and creating a SRV afterward would fail because of the missing bind flags
bCreateShaderResource = false;
}
if (EnumHasAnyFlags(Flags, ETextureCreateFlags::DisableSRVCreation))
{
bCreateShaderResource = false;
}
if (EnumHasAnyFlags(Flags, ETextureCreateFlags::Shared))
{
if (GCVarUseSharedKeyedMutex->GetInt() != 0)
{
D3D11Desc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
}
else
{
D3D11Desc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
}
}
// Set up the texture bind flags.
bool bCreateRTV = false;
bool bCreateDSV = false;
if (EnumHasAnyFlags(Flags, ETextureCreateFlags::RenderTargetable))
{
check(!EnumHasAnyFlags(Flags, ETextureCreateFlags::DepthStencilTargetable | ETextureCreateFlags::ResolveTargetable));
D3D11Desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
bCreateRTV = true;
}
else if (EnumHasAnyFlags(Flags, ETextureCreateFlags::DepthStencilTargetable))
{
check(!EnumHasAnyFlags(Flags, ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::ResolveTargetable));
D3D11Desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
bCreateDSV = true;
}
else if (EnumHasAnyFlags(Flags, ETextureCreateFlags::ResolveTargetable))
{
check(!EnumHasAnyFlags(Flags, ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::DepthStencilTargetable));
if (Format == PF_DepthStencil || Format == PF_ShadowDepth || Format == PF_D24)
{
D3D11Desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
bCreateDSV = true;
}
else
{
D3D11Desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
bCreateRTV = true;
}
}
// NV12 doesn't support RTV in NV12 format so don't create RTV for it.
// Todo: add support for RTVs of underneath luminance & chrominance textures.
if (Format == PF_NV12 || Format == PF_P010)
{
bCreateRTV = false;
}
if (EnumHasAnyFlags(Flags, ETextureCreateFlags::UAV))
{
D3D11Desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
}
if (bCreateDSV && !EnumHasAnyFlags(Flags, ETextureCreateFlags::ShaderResource))
{
D3D11Desc.BindFlags &= ~D3D11_BIND_SHADER_RESOURCE;
bCreateShaderResource = false;
}
ED3D11TextureCreateViewFlags CreateViewFlags = ED3D11TextureCreateViewFlags::None;
if (bCreateShaderResource)
{
EnumAddFlags(CreateViewFlags, ED3D11TextureCreateViewFlags::SRV);
}
if (bCreateRTV)
{
EnumAddFlags(CreateViewFlags, ED3D11TextureCreateViewFlags::RTV);
}
if (bCreateDSV)
{
EnumAddFlags(CreateViewFlags, ED3D11TextureCreateViewFlags::DSV);
}
return CreateViewFlags;
}
TRefCountPtr<ID3D11RenderTargetView> CreateRTV(FD3D11Device* Direct3DDevice, ID3D11Resource* Resource, const FRHITextureDesc& TextureDesc, DXGI_FORMAT PlatformResourceFormat, uint32 MipIndex, uint32 SliceIndex, uint32 SliceCount)
{
D3D11_RENDER_TARGET_VIEW_DESC RTVDesc;
FMemory::Memzero(RTVDesc);
RTVDesc.Format = UE::DXGIUtilities::FindShaderResourceFormat(PlatformResourceFormat, EnumHasAnyFlags(TextureDesc.Flags, ETextureCreateFlags::SRGB));
if (TextureDesc.IsTexture3D())
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
RTVDesc.Texture3D.MipSlice = MipIndex;
RTVDesc.Texture3D.FirstWSlice = 0;
RTVDesc.Texture3D.WSize = TextureDesc.Depth;
}
else if (TextureDesc.IsTextureArray() || TextureDesc.IsTextureCube())
{
if (TextureDesc.NumSamples > 1)
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
RTVDesc.Texture2DMSArray.FirstArraySlice = SliceIndex;
RTVDesc.Texture2DMSArray.ArraySize = SliceCount;
}
else
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
RTVDesc.Texture2DArray.FirstArraySlice = SliceIndex;
RTVDesc.Texture2DArray.ArraySize = SliceCount;
RTVDesc.Texture2DArray.MipSlice = MipIndex;
}
}
else
{
if (TextureDesc.NumSamples > 1)
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
// Nothing to set
}
else
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
RTVDesc.Texture2D.MipSlice = MipIndex;
}
}
TRefCountPtr<ID3D11RenderTargetView> RenderTargetView;
VERIFYD3D11RESULT_EX(Direct3DDevice->CreateRenderTargetView(Resource, &RTVDesc, RenderTargetView.GetInitReference()), Direct3DDevice);
return RenderTargetView;
}
TRefCountPtr<ID3D11ShaderResourceView> CreateSRV(FD3D11Device* Direct3DDevice, ID3D11Resource* Resource, const FRHITextureDesc& TextureDesc, DXGI_FORMAT PlatformResourceFormat)
{
const DXGI_FORMAT PlatformShaderResourceFormat = UE::DXGIUtilities::FindShaderResourceFormat(PlatformResourceFormat, EnumHasAnyFlags(TextureDesc.Flags, ETextureCreateFlags::SRGB));
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
FMemory::Memzero(SRVDesc);
SRVDesc.Format = PlatformShaderResourceFormat;
switch (TextureDesc.Dimension)
{
case ETextureDimension::Texture2D:
if (TextureDesc.NumSamples > 1)
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
// Nothing to set
}
else
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MostDetailedMip = 0;
SRVDesc.Texture2D.MipLevels = TextureDesc.NumMips;
}
break;
case ETextureDimension::Texture2DArray:
if (TextureDesc.NumSamples > 1)
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
SRVDesc.Texture2DMSArray.FirstArraySlice = 0;
SRVDesc.Texture2DMSArray.ArraySize = TextureDesc.ArraySize;
}
else
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
SRVDesc.Texture2DArray.MostDetailedMip = 0;
SRVDesc.Texture2DArray.MipLevels = TextureDesc.NumMips;
SRVDesc.Texture2DArray.FirstArraySlice = 0;
SRVDesc.Texture2DArray.ArraySize = TextureDesc.ArraySize;
}
break;
case ETextureDimension::Texture3D:
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
SRVDesc.Texture3D.MipLevels = TextureDesc.NumMips;
SRVDesc.Texture3D.MostDetailedMip = 0;
break;
case ETextureDimension::TextureCube:
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
SRVDesc.TextureCube.MostDetailedMip = 0;
SRVDesc.TextureCube.MipLevels = TextureDesc.NumMips;
break;
case ETextureDimension::TextureCubeArray:
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY;
SRVDesc.TextureCubeArray.MostDetailedMip = 0;
SRVDesc.TextureCubeArray.MipLevels = TextureDesc.NumMips;
SRVDesc.TextureCubeArray.First2DArrayFace = 0;
SRVDesc.TextureCubeArray.NumCubes = TextureDesc.ArraySize;
break;
default:
break;
}
TRefCountPtr<ID3D11ShaderResourceView> SRV;
VERIFYD3D11RESULT_EX(Direct3DDevice->CreateShaderResourceView(Resource, &SRVDesc, SRV.GetInitReference()), Direct3DDevice);
return SRV;
}
void FD3D11DynamicRHI::FinalizeCreateTexture2DInternal(FD3D11Texture* Texture, TConstArrayView<D3D11_SUBRESOURCE_DATA> InitialData)
{
const FString DebugName = Texture->GetName().ToString();
const FRHITextureDesc& TextureDesc = Texture->GetDesc();
check(!TextureDesc.IsTexture3D());
const bool bTextureArray = TextureDesc.IsTextureArray();
const bool bCubeTexture = TextureDesc.IsTextureCube();
const uint32 SizeX = TextureDesc.Extent.X;
const uint32 SizeY = TextureDesc.Extent.Y;
const uint32 SizeZ = bCubeTexture ? TextureDesc.ArraySize * 6 : TextureDesc.ArraySize;
const EPixelFormat Format = TextureDesc.Format;
const uint32 NumMips = TextureDesc.NumMips;
const uint32 NumSamples = TextureDesc.NumSamples;
const ETextureCreateFlags Flags = TextureDesc.Flags;
check(SizeX > 0 && SizeY > 0 && NumMips > 0);
if (bCubeTexture)
{
checkf(SizeX <= GetMaxCubeTextureDimension(), TEXT("Requested cube texture size too large: %i, Max: %i, DebugName: '%s'"), SizeX, GetMaxCubeTextureDimension(), *DebugName);
check(SizeX == SizeY);
}
else
{
checkf(SizeX <= GetMax2DTextureDimension(), TEXT("Requested texture2d x size too large: %i, Max: %i, DebugName: '%s'"), SizeX, GetMax2DTextureDimension(), *DebugName);
checkf(SizeY <= GetMax2DTextureDimension(), TEXT("Requested texture2d y size too large: %i, Max: %i, DebugName: '%s'"), SizeY, GetMax2DTextureDimension(), *DebugName);
}
if (bTextureArray)
{
checkf(SizeZ <= GetMaxTextureArrayLayers(), TEXT("Requested texture array size too large: %i, Max: %i, DebugName: '%s'"), SizeZ, GetMaxTextureArrayLayers(), *DebugName);
}
SCOPE_CYCLE_COUNTER(STAT_D3D11CreateTextureTime);
const DXGI_FORMAT PlatformResourceFormat = UE::DXGIUtilities::GetPlatformTextureResourceFormat(Format, Flags);
const uint32 ActualMSAAQuality = GetMaxMSAAQuality(TextureDesc.NumSamples);
check(ActualMSAAQuality != 0xffffffff);
check(TextureDesc.NumSamples == 1 || !EnumHasAnyFlags(Flags, ETextureCreateFlags::Shared));
// Describe the texture.
D3D11_TEXTURE2D_DESC Desc;
ED3D11TextureCreateViewFlags CreateViewFlags = SetupD3D11TextureCommonDesc(Desc, TextureDesc, PlatformResourceFormat);
// Texture2D specific vars
Desc.ArraySize = SizeZ;
Desc.SampleDesc.Count = NumSamples;
Desc.SampleDesc.Quality = ActualMSAAQuality;
ApplyBC7SoftwareAdapterWorkaround(Adapter.bSoftwareAdapter, Desc);
const D3D11_SUBRESOURCE_DATA* pSubresourceData = nullptr;
if (InitialData.Num())
{
// Caller provided initial data.
check(InitialData.Num() == NumMips * SizeZ);
pSubresourceData = InitialData.GetData();
}
TRefCountPtr<ID3D11Texture2D> TextureResource;
#if INTEL_EXTENSIONS
if (EnumHasAnyFlags(TextureDesc.Flags, ETextureCreateFlags::Atomic64Compatible) && IsRHIDeviceIntel() && GRHIGlobals.SupportsAtomicUInt64)
{
INTC_D3D11_TEXTURE2D_DESC IntelDesc{};
IntelDesc.EmulatedTyped64bitAtomics = true;
IntelDesc.pD3D11Desc = &Desc;
VERIFYD3D11RESULT(INTC_D3D11_CreateTexture2D(IntelExtensionContext, &IntelDesc, pSubresourceData, TextureResource.GetInitReference()));
}
else
#endif
{
SafeCreateTexture2D(Direct3DDevice, Format, &Desc, pSubresourceData, TextureResource.GetInitReference(), *DebugName);
}
#if RHI_USE_RESOURCE_DEBUG_NAME
if (Texture->GetName() != NAME_None)
{
SetD3D11ObjectName(TextureResource, DebugName);
}
#endif
TArray<TRefCountPtr<ID3D11RenderTargetView>> RenderTargetViews;
bool bCreatedRTVPerSlice = false;
if (EnumHasAnyFlags(CreateViewFlags, ED3D11TextureCreateViewFlags::RTV))
{
if (EnumHasAnyFlags(TextureDesc.Flags, ETextureCreateFlags::TargetArraySlicesIndependently) && (TextureDesc.IsTextureArray() || TextureDesc.IsTextureCube()))
{
bCreatedRTVPerSlice = true;
for (uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
{
for (uint32 SliceIndex = 0; SliceIndex < Desc.ArraySize; SliceIndex++)
{
RenderTargetViews.Emplace(CreateRTV(Direct3DDevice, TextureResource, TextureDesc, PlatformResourceFormat, MipIndex, SliceIndex, 1));
}
}
}
else
{
for (uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
{
RenderTargetViews.Emplace(CreateRTV(Direct3DDevice, TextureResource, TextureDesc, PlatformResourceFormat, MipIndex, 0, Desc.ArraySize));
}
}
}
TRefCountPtr<ID3D11DepthStencilView> DepthStencilViews[FExclusiveDepthStencil::MaxIndex];
if (EnumHasAnyFlags(CreateViewFlags, ED3D11TextureCreateViewFlags::DSV))
{
const D3D11_DSV_DIMENSION DSVDimension =
(bTextureArray || bCubeTexture)
? (TextureDesc.NumSamples > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY : D3D11_DSV_DIMENSION_TEXTURE2DARRAY)
: (TextureDesc.NumSamples > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D);
// Create a depth-stencil-view for the texture.
CD3D11_DEPTH_STENCIL_VIEW_DESC DSVDesc(
DSVDimension,
UE::DXGIUtilities::FindDepthStencilFormat((DXGI_FORMAT)GPixelFormats[TextureDesc.Format].PlatformFormat),
0, // MipSlice
0, // FirstArraySlice
Desc.ArraySize,
0 // Flags
);
for (uint32 AccessType = 0; AccessType < FExclusiveDepthStencil::MaxIndex; ++AccessType)
{
// Create a read-only access views for the texture.
// Read-only DSVs are not supported in Feature Level 10 so
// a dummy DSV is created in order reduce logic complexity at a higher-level.
DSVDesc.Flags = (AccessType & FExclusiveDepthStencil::DepthRead_StencilWrite) ? D3D11_DSV_READ_ONLY_DEPTH : 0;
if(UE::DXGIUtilities::HasStencilBits(DSVDesc.Format))
{
DSVDesc.Flags |= (AccessType & FExclusiveDepthStencil::DepthWrite_StencilRead) ? D3D11_DSV_READ_ONLY_STENCIL : 0;
}
VERIFYD3D11RESULT_EX(Direct3DDevice->CreateDepthStencilView(TextureResource,&DSVDesc,DepthStencilViews[AccessType].GetInitReference()), Direct3DDevice);
}
}
check(IsValidRef(TextureResource));
// Create a shader resource view for the texture.
TRefCountPtr<ID3D11ShaderResourceView> ShaderResourceView;
if (EnumHasAnyFlags(CreateViewFlags, ED3D11TextureCreateViewFlags::SRV))
{
ShaderResourceView = CreateSRV(Direct3DDevice, TextureResource, TextureDesc, PlatformResourceFormat);
check(IsValidRef(ShaderResourceView));
}
Texture->FinalizeCreation(
TextureResource,
ShaderResourceView,
Desc.ArraySize,
bCreatedRTVPerSlice,
RenderTargetViews,
DepthStencilViews
);
}
void FD3D11DynamicRHI::FinalizeCreateTexture3DInternal(FD3D11Texture* Texture, TConstArrayView<D3D11_SUBRESOURCE_DATA> InitialData)
{
const FString DebugName = Texture->GetName().ToString();
const FRHITextureDesc& TextureDesc = Texture->GetDesc();
check(TextureDesc.IsTexture3D());
check(TextureDesc.ArraySize == 1);
SCOPE_CYCLE_COUNTER(STAT_D3D11CreateTextureTime);
// Set up the texture bind flags.
check(!EnumHasAnyFlags(TextureDesc.Flags, ETextureCreateFlags::DepthStencilTargetable | ETextureCreateFlags::ResolveTargetable));
const DXGI_FORMAT PlatformResourceFormat = UE::DXGIUtilities::GetPlatformTextureResourceFormat(TextureDesc.Format, TextureDesc.Flags);
// Describe the texture.
D3D11_TEXTURE3D_DESC Desc;
const ED3D11TextureCreateViewFlags CreateViewFlags = SetupD3D11TextureCommonDesc(Desc, TextureDesc, PlatformResourceFormat);
// Texture3D specific vars
Desc.Depth = TextureDesc.Depth;
const D3D11_SUBRESOURCE_DATA* pSubresourceData = nullptr;
if (InitialData.Num())
{
// Caller provided initial data.
check(InitialData.Num() == TextureDesc.NumMips);
pSubresourceData = InitialData.GetData();
}
TRefCountPtr<ID3D11Texture3D> TextureResource;
SafeCreateTexture3D(Direct3DDevice, TextureDesc.Format, &Desc, pSubresourceData, TextureResource.GetInitReference(), *DebugName);
#if RHI_USE_RESOURCE_DEBUG_NAME
if (Texture->GetName() != NAME_None)
{
SetD3D11ObjectName(TextureResource, DebugName);
}
#endif
// Create a shader resource view for the texture.
TRefCountPtr<ID3D11ShaderResourceView> ShaderResourceView;
if (EnumHasAnyFlags(CreateViewFlags, ED3D11TextureCreateViewFlags::SRV))
{
ShaderResourceView = CreateSRV(Direct3DDevice, TextureResource, TextureDesc, PlatformResourceFormat);
}
TRefCountPtr<ID3D11RenderTargetView> RenderTargetView;
if (EnumHasAnyFlags(CreateViewFlags, ED3D11TextureCreateViewFlags::RTV))
{
RenderTargetView = CreateRTV(Direct3DDevice, TextureResource, TextureDesc, PlatformResourceFormat, 0, 0, 1);
}
Texture->FinalizeCreation(
TextureResource,
ShaderResourceView,
1, // InRTVArraySize
false, // bInCreatedRTVsPerSlice
{ RenderTargetView },
{}
);
}
static const D3D11_SUBRESOURCE_DATA* FillSubresourceData(TArray<D3D11_SUBRESOURCE_DATA>& SubresourceData, const FRHITextureDesc& CreateDesc, TConstArrayView<uint8> InitialData)
{
const D3D11_SUBRESOURCE_DATA* pSubresourceData = nullptr;
if (InitialData.Num())
{
const FPixelFormatInfo& PixelFormat = GPixelFormats[CreateDesc.Format];
const uint32 FaceCount = CreateDesc.IsTextureCube() ? 6 : 1;
const uint32 ArrayCount = CreateDesc.ArraySize * FaceCount;
const uint32 MipCount = CreateDesc.NumMips;
// each mip of each array slice counts as a subresource
SubresourceData.AddZeroed(MipCount * ArrayCount);
uint32 SliceOffset = 0;
for (uint32 ArraySliceIndex = 0; ArraySliceIndex < ArrayCount; ++ArraySliceIndex)
{
uint32 MipOffset = 0;
for (uint32 MipIndex = 0; MipIndex < MipCount; ++MipIndex)
{
const uint32 DataOffset = SliceOffset + MipOffset;
const uint32 SubResourceIndex = ArraySliceIndex * MipCount + MipIndex;
const FUintVector3 BlockCounts = UE::RHITextureUtils::CalculateMipBlockCounts(CreateDesc, MipIndex, PixelFormat);
const uint32 RowPitch = BlockCounts.X * PixelFormat.BlockBytes;
const uint32 SlicePitch = BlockCounts.Y * RowPitch;
SubresourceData[SubResourceIndex].pSysMem = &InitialData[DataOffset];
SubresourceData[SubResourceIndex].SysMemPitch = RowPitch;
SubresourceData[SubResourceIndex].SysMemSlicePitch = SlicePitch;
MipOffset += SlicePitch * BlockCounts.Z;
}
SliceOffset += MipOffset;
}
pSubresourceData = SubresourceData.GetData();
}
return pSubresourceData;
}
FD3D11Texture* FD3D11DynamicRHI::FinalizeCreateTextureInternal(FD3D11Texture* Texture, const FRHITextureDesc& InDesc, TConstArrayView<uint8> InitialData)
{
TArray<D3D11_SUBRESOURCE_DATA> SubresourceData;
FillSubresourceData(SubresourceData, InDesc, InitialData);
if (InDesc.IsTexture3D())
{
FinalizeCreateTexture3DInternal(Texture, TConstArrayView<D3D11_SUBRESOURCE_DATA>(SubresourceData));
}
else
{
FinalizeCreateTexture2DInternal(Texture, TConstArrayView<D3D11_SUBRESOURCE_DATA>(SubresourceData));
}
return Texture;
}
FD3D11Texture* FD3D11DynamicRHI::CreateTextureInternal(const FRHITextureCreateDesc& CreateDesc, TConstArrayView<uint8> InitialData)
{
LLM_SCOPE_DYNAMIC_STAT_OBJECTPATH_FNAME(CreateDesc.OwnerName, ELLMTagSet::Assets);
LLM_SCOPE_DYNAMIC_STAT_OBJECTPATH_FNAME(CreateDesc.GetTraceClassName(), ELLMTagSet::AssetClasses);
UE_TRACE_METADATA_SCOPE_ASSET_FNAME(CreateDesc.DebugName, CreateDesc.GetTraceClassName(), CreateDesc.OwnerName);
FD3D11Texture* Texture = BeginCreateTextureInternal(CreateDesc);
return FinalizeCreateTextureInternal(Texture, CreateDesc, InitialData);
}
FRHITextureInitializer FD3D11DynamicRHI::RHICreateTextureInitializer(FRHICommandListBase& RHICmdList, const FRHITextureCreateDesc& CreateDesc)
{
LLM_SCOPE_DYNAMIC_STAT_OBJECTPATH_FNAME(CreateDesc.OwnerName, ELLMTagSet::Assets);
LLM_SCOPE_DYNAMIC_STAT_OBJECTPATH_FNAME(CreateDesc.GetTraceClassName(), ELLMTagSet::AssetClasses);
UE_TRACE_METADATA_SCOPE_ASSET_FNAME(CreateDesc.DebugName, CreateDesc.GetTraceClassName(), CreateDesc.OwnerName);
if (CreateDesc.InitAction == ERHITextureInitAction::Default)
{
return UE::RHICore::FDefaultTextureInitializer(RHICmdList, CreateTextureInternal(CreateDesc, {}));
}
if (CreateDesc.InitAction == ERHITextureInitAction::BulkData)
{
check(CreateDesc.BulkData);
FD3D11Texture* Texture = CreateTextureInternal(CreateDesc, CreateDesc.BulkData->GetBulkDataView<uint8>());
// Discard the bulk data's contents.
CreateDesc.BulkData->Discard();
return UE::RHICore::FDefaultTextureInitializer(RHICmdList, Texture);
}
if (CreateDesc.InitAction == ERHITextureInitAction::Initializer)
{
FD3D11Texture* Texture = BeginCreateTextureInternal(CreateDesc);
const uint64 UploadMemorySize = UE::RHITextureUtils::CalculateTextureSize(CreateDesc);
void* UploadMemory = FMemory::Malloc(UploadMemorySize, 16);
return UE::RHICore::FDefaultLayoutTextureInitializer(RHICmdList, Texture, UploadMemory, UploadMemorySize,
[this, Texture = TRefCountPtr<FD3D11Texture>(Texture), UploadMemory = UE::RHICore::FInitializerScopedMemory(UploadMemory), UploadMemorySize](FRHICommandListBase& RHICmdList) mutable
{
TConstArrayView<uint8> InitialData(reinterpret_cast<const uint8*>(UploadMemory.Pointer), UploadMemorySize);
return TRefCountPtr<FRHITexture>(FinalizeCreateTextureInternal(Texture, Texture->GetDesc(), InitialData));
});
}
return UE::RHICore::HandleUnknownTextureInitializerInitAction(RHICmdList, CreateDesc);
}
FTextureRHIRef FD3D11DynamicRHI::RHIAsyncCreateTexture2D(uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags Flags, ERHIAccess InResourceState, void** InitialMipData, uint32 NumInitialMips, const TCHAR* DebugName, FGraphEventRef& OutCompletionEvent)
{
const FPixelFormatInfo& PixelFormat = GPixelFormats[Format];
TArray<D3D11_SUBRESOURCE_DATA, TInlineAllocator<12>> SubresourceData;
SubresourceData.SetNumUninitialized(NumMips);
for (uint32 MipIndex = 0; MipIndex < NumInitialMips; ++MipIndex)
{
const uint32 NumBlocksX = UE::RHITextureUtils::CalculateMipBlockCount(SizeX, MipIndex, PixelFormat.BlockSizeX);
const uint32 NumBlocksY = UE::RHITextureUtils::CalculateMipBlockCount(SizeY, MipIndex, PixelFormat.BlockSizeY);
SubresourceData[MipIndex].pSysMem = InitialMipData[MipIndex];
SubresourceData[MipIndex].SysMemPitch = NumBlocksX * PixelFormat.BlockBytes;
SubresourceData[MipIndex].SysMemSlicePitch = NumBlocksX * NumBlocksY * PixelFormat.BlockBytes;
}
void* TempBuffer = ZeroBuffer;
uint32 TempBufferSize = ZeroBufferSize;
for (uint32 MipIndex = NumInitialMips; MipIndex < NumMips; ++MipIndex)
{
const uint32 NumBlocksX = UE::RHITextureUtils::CalculateMipBlockCount(SizeX, MipIndex, PixelFormat.BlockSizeX);
const uint32 NumBlocksY = UE::RHITextureUtils::CalculateMipBlockCount(SizeY, MipIndex, PixelFormat.BlockSizeY);
uint32 MipSize = NumBlocksX * NumBlocksY * PixelFormat.BlockBytes;
if (MipSize > TempBufferSize)
{
UE_LOG(LogD3D11RHI, Verbose, TEXT("Temp texture streaming buffer not large enough, needed %d bytes"), MipSize);
check(TempBufferSize == ZeroBufferSize);
TempBufferSize = MipSize;
TempBuffer = FMemory::Malloc(TempBufferSize);
FMemory::Memzero(TempBuffer, TempBufferSize);
}
SubresourceData[MipIndex].pSysMem = TempBuffer;
SubresourceData[MipIndex].SysMemPitch = NumBlocksX * PixelFormat.BlockBytes;
SubresourceData[MipIndex].SysMemSlicePitch = MipSize;
}
const FRHITextureCreateDesc CreateDesc =
FRHITextureCreateDesc::Create2D(DebugName, SizeX, SizeY, (EPixelFormat)Format)
.SetClearValue(FClearValueBinding::None)
.SetFlags(Flags)
.SetNumMips(NumMips)
.DetermineInititialState();
FD3D11Texture* Texture = BeginCreateTextureInternal(CreateDesc);
FinalizeCreateTexture2DInternal(Texture, SubresourceData);
if (TempBufferSize != ZeroBufferSize)
{
FMemory::Free(TempBuffer);
}
OutCompletionEvent = nullptr;
return Texture;
}
uint32 FD3D11DynamicRHI::RHIComputeMemorySize(FRHITexture* TextureRHI)
{
if(!TextureRHI)
{
return 0;
}
FD3D11Texture* Texture = ResourceCast(TextureRHI);
return Texture->GetMemorySize();
}
void FD3D11DynamicRHI::RHIAsyncCopyTexture2DCopy(FRHITexture* NewTexture2DRHI, FRHITexture* Texture2DRHI, int32 NewMipCount, int32 NewSizeX, int32 NewSizeY, FThreadSafeCounter* RequestStatus)
{
FD3D11Texture* Texture2D = ResourceCast(Texture2DRHI);
FD3D11Texture* NewTexture2D = ResourceCast(NewTexture2DRHI);
// Use the GPU to asynchronously copy the old mip-maps into the new texture.
const uint32 NumSharedMips = FMath::Min(Texture2D->GetNumMips(), NewTexture2D->GetNumMips());
const uint32 SourceMipOffset = Texture2D->GetNumMips() - NumSharedMips;
const uint32 DestMipOffset = NewTexture2D->GetNumMips() - NumSharedMips;
for (uint32 MipIndex = 0; MipIndex < NumSharedMips; ++MipIndex)
{
// Use the GPU to copy between mip-maps.
// This is serialized with other D3D commands, so it isn't necessary to increment Counter to signal a pending asynchronous copy.
Direct3DDeviceIMContext->CopySubresourceRegion(
NewTexture2D->GetResource(),
D3D11CalcSubresource(MipIndex + DestMipOffset, 0, NewTexture2D->GetNumMips()),
0,
0,
0,
Texture2D->GetResource(),
D3D11CalcSubresource(MipIndex + SourceMipOffset, 0, Texture2D->GetNumMips()),
NULL
);
}
// Decrement the thread-safe counter used to track the completion of the reallocation, since D3D handles sequencing the
// async mip copies with other D3D calls.
RequestStatus->Decrement();
}
FTextureRHIRef FD3D11DynamicRHI::RHIAsyncReallocateTexture2D(FRHITexture* Texture2DRHI, int32 NewMipCount, int32 NewSizeX, int32 NewSizeY, FThreadSafeCounter* RequestStatus)
{
FD3D11Texture* Texture2D = ResourceCast(Texture2DRHI);
FRHITextureCreateDesc CreateDesc(
Texture2D->GetDesc(),
RHIGetDefaultResourceState(Texture2D->GetDesc().Flags, false),
TEXT("RHIAsyncReallocateTexture2D")
);
CreateDesc.Extent = FIntPoint(NewSizeX, NewSizeY);
CreateDesc.NumMips = NewMipCount;
CreateDesc.SetOwnerName(Texture2D->GetOwnerName());
// Allocate a new texture.
FD3D11Texture* NewTexture2D = CreateTextureInternal(CreateDesc, {});
RHIAsyncCopyTexture2DCopy(NewTexture2D, Texture2D, NewMipCount, NewSizeX, NewSizeY, RequestStatus);
return NewTexture2D;
}
FTextureRHIRef FD3D11DynamicRHI::AsyncReallocateTexture2D_RenderThread(
class FRHICommandListImmediate& RHICmdList,
FRHITexture* Texture2D,
int32 NewMipCount,
int32 NewSizeX,
int32 NewSizeY,
FThreadSafeCounter* RequestStatus)
{
FTextureRHIRef NewTexture2D;
if (ShouldNotEnqueueRHICommand())
{
NewTexture2D = RHIAsyncReallocateTexture2D(Texture2D, NewMipCount, NewSizeX, NewSizeY, RequestStatus);
}
else
{
// Allocate a new texture.
FRHITextureCreateDesc CreateDesc(
Texture2D->GetDesc(),
RHIGetDefaultResourceState(Texture2D->GetDesc().Flags, false),
TEXT("AsyncReallocateTexture2D_RenderThread")
);
CreateDesc.Extent = FIntPoint(NewSizeX, NewSizeY);
CreateDesc.NumMips = NewMipCount;
CreateDesc.SetOwnerName(Texture2D->GetOwnerName());
NewTexture2D = CreateTextureInternal(CreateDesc, {});
RunOnRHIThread([this, NewTexture2D, Texture2D, NewMipCount, NewSizeX, NewSizeY, RequestStatus]()
{
RHIAsyncCopyTexture2DCopy(NewTexture2D, Texture2D, NewMipCount, NewSizeX, NewSizeY, RequestStatus);
});
}
return NewTexture2D;
}
FRHILockTextureResult FD3D11Texture::Lock(FD3D11DynamicRHI* D3DRHI, const FRHILockTextureArgs& Arguments, bool bForceLockDeferred)
{
check(!IsTexture3D()); // Only 2D texture locks are implemented
SCOPE_CYCLE_COUNTER(STAT_D3D11LockTextureTime);
const FRHITextureDesc& Desc = this->GetDesc();
const uint32 ArrayIndex = UE::RHICore::GetLockArrayIndex(Desc, Arguments);
// Calculate the subresource index corresponding to the specified mip-map.
const uint32 Subresource = D3D11CalcSubresource(Arguments.MipIndex, ArrayIndex, Desc.NumMips);
// Calculate the dimensions of the mip-map.
const uint32 BlockSizeX = GPixelFormats[Desc.Format].BlockSizeX;
const uint32 BlockSizeY = GPixelFormats[Desc.Format].BlockSizeY;
const uint32 BlockBytes = GPixelFormats[Desc.Format].BlockBytes;
const uint32 MipSizeX = FMath::Max(uint32(Desc.Extent.X) >> Arguments.MipIndex, BlockSizeX);
const uint32 MipSizeY = FMath::Max(uint32(Desc.Extent.Y) >> Arguments.MipIndex, BlockSizeY);
const uint32 NumBlocksX = (MipSizeX + BlockSizeX - 1) / BlockSizeX;
const uint32 NumBlocksY = (MipSizeY + BlockSizeY - 1) / BlockSizeY;
const uint32 MipBytes = NumBlocksX * NumBlocksY * BlockBytes;
FRHILockTextureResult Result;
FD3D11LockedData LockedData;
if (Arguments.LockMode == RLM_WriteOnly)
{
if (!bForceLockDeferred && EnumHasAnyFlags(Desc.Flags, TexCreate_CPUWritable))
{
D3D11_MAPPED_SUBRESOURCE MappedTexture{};
VERIFYD3D11RESULT_EX(D3DRHI->GetDeviceContext()->Map(GetResource(), Subresource, D3D11_MAP_WRITE, 0, &MappedTexture), D3DRHI->GetDevice());
LockedData.SetData(MappedTexture.pData);
LockedData.Pitch = MappedTexture.RowPitch;
}
else
{
// If we're writing to the texture, allocate a system memory buffer to receive the new contents.
LockedData.AllocData(MipBytes);
LockedData.Pitch = NumBlocksX * BlockBytes;
LockedData.bLockDeferred = true;
}
}
else
{
check(!bForceLockDeferred);
// If we're reading from the texture, we create a staging resource, copy the texture contents to it, and map it.
// Create the staging texture.
D3D11_TEXTURE2D_DESC StagingTextureDesc{};
GetD3D11Texture2D()->GetDesc(&StagingTextureDesc);
StagingTextureDesc.Width = MipSizeX;
StagingTextureDesc.Height = MipSizeY;
StagingTextureDesc.MipLevels = 1;
StagingTextureDesc.ArraySize = 1;
StagingTextureDesc.Usage = D3D11_USAGE_STAGING;
StagingTextureDesc.BindFlags = 0;
StagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
StagingTextureDesc.MiscFlags = 0;
const FString StagingTextureName = FString::Printf(TEXT("%s_Staging"), *GetName().ToString());
TRefCountPtr<ID3D11Texture2D> StagingTexture;
VERIFYD3D11CREATETEXTURERESULT(
D3DRHI->GetDevice()->CreateTexture2D(&StagingTextureDesc, NULL, StagingTexture.GetInitReference()),
Desc.Format,
Desc.Extent.X,
Desc.Extent.Y,
this->GetSizeZ(),
StagingTextureDesc.Format,
1,
0,
StagingTextureDesc.Usage,
StagingTextureDesc.CPUAccessFlags,
StagingTextureDesc.MiscFlags,
StagingTextureDesc.SampleDesc.Count,
StagingTextureDesc.SampleDesc.Quality,
nullptr,
0,
0,
D3DRHI->GetDevice(),
*StagingTextureName
);
LockedData.StagingResource = StagingTexture;
// Copy the mip-map data from the real resource into the staging resource
D3DRHI->GetDeviceContext()->CopySubresourceRegion(StagingTexture, 0, 0, 0, 0, GetResource(), Subresource, NULL);
// Map the staging resource, and return the mapped address.
D3D11_MAPPED_SUBRESOURCE MappedTexture{};
VERIFYD3D11RESULT_EX(D3DRHI->GetDeviceContext()->Map(StagingTexture, 0, D3D11_MAP_READ, 0, &MappedTexture), D3DRHI->GetDevice());
LockedData.SetData(MappedTexture.pData);
LockedData.Pitch = MappedTexture.RowPitch;
}
// Add the lock to the outstanding lock list.
if (!bForceLockDeferred)
{
D3DRHI->AddLockedData(FD3D11LockedKey(GetResource(), Subresource), LockedData);
}
else
{
RunOnRHIThread([this, Subresource, LockedData, D3DRHI]()
{
D3DRHI->AddLockedData(FD3D11LockedKey(GetResource(), Subresource), LockedData);
});
}
Result.Data = LockedData.GetData();
Result.ByteCount = MipBytes;
Result.Stride = LockedData.Pitch;
return Result;
}
void FD3D11Texture::Unlock(FD3D11DynamicRHI* D3DRHI, const FRHILockTextureArgs& Arguments)
{
check(!IsTexture3D()); // Only 2D texture locks are implemented
SCOPE_CYCLE_COUNTER(STAT_D3D11UnlockTextureTime);
const FRHITextureDesc& Desc = this->GetDesc();
const uint32 ArrayIndex = UE::RHICore::GetLockArrayIndex(Desc, Arguments);
// Calculate the subresource index corresponding to the specified mip-map.
const uint32 Subresource = D3D11CalcSubresource(Arguments.MipIndex, ArrayIndex, Desc.NumMips);
// Find the object that is tracking this lock and remove it from outstanding list
FD3D11LockedData LockedData;
verifyf(D3DRHI->RemoveLockedData(FD3D11LockedKey(GetResource(), Subresource), LockedData), TEXT("Texture is not locked"));
if (!LockedData.bLockDeferred && EnumHasAnyFlags(Desc.Flags, TexCreate_CPUWritable))
{
D3DRHI->GetDeviceContext()->Unmap(GetResource(), 0);
}
else if(!LockedData.StagingResource)
{
// If we're writing, we need to update the subresource
D3DRHI->GetDeviceContext()->UpdateSubresource(GetResource(), Subresource, NULL, LockedData.GetData(), LockedData.Pitch, 0);
LockedData.FreeData();
}
else
{
D3DRHI->GetDeviceContext()->Unmap(LockedData.StagingResource, 0);
}
}
FRHILockTextureResult FD3D11DynamicRHI::RHILockTexture(FRHICommandListImmediate& RHICmdList, const FRHILockTextureArgs& Arguments)
{
FD3D11Texture* Texture = ResourceCast(Arguments.Texture);
if (ShouldNotEnqueueRHICommand())
{
ConditionalClearShaderResource(Texture, false);
return Texture->Lock(this, Arguments, false /* bForceLockDeferred */);
}
if (Arguments.LockMode == RLM_ReadOnly)
{
RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
ConditionalClearShaderResource(Texture, false);
return Texture->Lock(this, Arguments, false /* bForceLockDeferred */);
}
return Texture->Lock(this, Arguments, true /* bForceLockDeferred */);
}
void FD3D11DynamicRHI::RHIUnlockTexture(FRHICommandListImmediate& RHICmdList, const FRHILockTextureArgs& Arguments)
{
FD3D11Texture* Texture = ResourceCast(Arguments.Texture);
if (ShouldNotEnqueueRHICommand())
{
Texture->Unlock(this, Arguments);
}
else
{
RunOnRHIThread([this, Texture, Arguments]()
{
Texture->Unlock(this, Arguments);
});
}
}
void FD3D11DynamicRHI::RHIUpdateTexture2D(FRHICommandListBase& RHICmdList, FRHITexture* TextureRHI, uint32 MipIndex, const FUpdateTextureRegion2D& UpdateRegion, uint32 SourcePitch, const uint8* SourceData)
{
const FPixelFormatInfo& FormatInfo = GPixelFormats[TextureRHI->GetFormat()];
check(UpdateRegion.Width % FormatInfo.BlockSizeX == 0);
check(UpdateRegion.Height % FormatInfo.BlockSizeY == 0);
check(UpdateRegion.DestX % FormatInfo.BlockSizeX == 0);
check(UpdateRegion.DestY % FormatInfo.BlockSizeY == 0);
check(UpdateRegion.SrcX % FormatInfo.BlockSizeX == 0);
check(UpdateRegion.SrcY % FormatInfo.BlockSizeY == 0);
const uint32 SrcXInBlocks = FMath::DivideAndRoundUp<uint32>(UpdateRegion.SrcX, FormatInfo.BlockSizeX);
const uint32 SrcYInBlocks = FMath::DivideAndRoundUp<uint32>(UpdateRegion.SrcY, FormatInfo.BlockSizeY);
const uint32 WidthInBlocks = FMath::DivideAndRoundUp<uint32>(UpdateRegion.Width, FormatInfo.BlockSizeX);
const uint32 HeightInBlocks = FMath::DivideAndRoundUp<uint32>(UpdateRegion.Height, FormatInfo.BlockSizeY);
const void* UpdateMemory = SourceData + FormatInfo.BlockBytes * SrcXInBlocks + SourcePitch * SrcYInBlocks;
uint32 UpdatePitch = SourcePitch;
const bool bNeedStagingMemory = RHICmdList.IsTopOfPipe();
if (bNeedStagingMemory)
{
const size_t SourceDataSizeInBlocks = static_cast<size_t>(WidthInBlocks) * static_cast<size_t>(HeightInBlocks);
const size_t SourceDataSize = SourceDataSizeInBlocks * FormatInfo.BlockBytes;
uint8* const StagingMemory = (uint8*)FMemory::Malloc(SourceDataSize);
const size_t StagingPitch = static_cast<size_t>(WidthInBlocks) * FormatInfo.BlockBytes;
const uint8* CopySrc = (const uint8*)UpdateMemory;
uint8* CopyDst = (uint8*)StagingMemory;
for (uint32 BlockRow = 0; BlockRow < HeightInBlocks; BlockRow++)
{
FMemory::Memcpy(CopyDst, CopySrc, WidthInBlocks * FormatInfo.BlockBytes);
CopySrc += SourcePitch;
CopyDst += StagingPitch;
}
UpdateMemory = StagingMemory;
UpdatePitch = StagingPitch;
}
RHICmdList.EnqueueLambda([this, TextureRHI, MipIndex, UpdateRegion, UpdatePitch, UpdateMemory, bNeedStagingMemory] (FRHICommandListBase&)
{
FD3D11Texture* Texture = ResourceCast(TextureRHI);
D3D11_BOX DestBox =
{
UpdateRegion.DestX, UpdateRegion.DestY, 0,
UpdateRegion.DestX + UpdateRegion.Width, UpdateRegion.DestY + UpdateRegion.Height, 1
};
Direct3DDeviceIMContext->UpdateSubresource(Texture->GetResource(), MipIndex, &DestBox, UpdateMemory, UpdatePitch, 0);
if (bNeedStagingMemory)
{
FMemory::Free(const_cast<void*>(UpdateMemory));
}
});
}
void FD3D11DynamicRHI::RHIUpdateTexture3D(FRHICommandListBase& RHICmdList, FRHITexture* TextureRHI,uint32 MipIndex,const FUpdateTextureRegion3D& UpdateRegion,uint32 SourceRowPitch,uint32 SourceDepthPitch,const uint8* SourceData)
{
const SIZE_T SourceDataSize = static_cast<SIZE_T>(SourceDepthPitch) * UpdateRegion.Depth;
uint8* SourceDataCopy = (uint8*)FMemory::Malloc(SourceDataSize);
FMemory::Memcpy(SourceDataCopy, SourceData, SourceDataSize);
SourceData = SourceDataCopy;
RHICmdList.EnqueueLambda([this, TextureRHI, MipIndex, UpdateRegion, SourceRowPitch, SourceDepthPitch, SourceData] (FRHICommandListBase&)
{
FD3D11Texture* Texture = ResourceCast(TextureRHI);
// The engine calls this with the texture size in the region.
// Some platforms like D3D11 needs that to be rounded up to the block size.
const FPixelFormatInfo& Format = GPixelFormats[Texture->GetFormat()];
const int32 NumBlockX = FMath::DivideAndRoundUp<int32>(UpdateRegion.Width, Format.BlockSizeX);
const int32 NumBlockY = FMath::DivideAndRoundUp<int32>(UpdateRegion.Height, Format.BlockSizeY);
D3D11_BOX DestBox =
{
UpdateRegion.DestX, UpdateRegion.DestY, UpdateRegion.DestZ,
UpdateRegion.DestX + NumBlockX * Format.BlockSizeX, UpdateRegion.DestY + NumBlockY * Format.BlockSizeY, UpdateRegion.DestZ + UpdateRegion.Depth
};
Direct3DDeviceIMContext->UpdateSubresource(Texture->GetResource(), MipIndex, &DestBox, SourceData, SourceRowPitch, SourceDepthPitch);
FMemory::Free((void*)SourceData);
});
}
void FD3D11DynamicRHI::RHIEndUpdateTexture3D(FRHICommandListBase& RHICmdList, FUpdateTexture3DData& UpdateData)
{
RHIUpdateTexture3D(RHICmdList, UpdateData.Texture, UpdateData.MipIndex, UpdateData.UpdateRegion, UpdateData.RowPitch, UpdateData.DepthPitch, UpdateData.Data);
FMemory::Free(UpdateData.Data);
UpdateData.Data = nullptr;
}
void FD3D11DynamicRHI::RHIBindDebugLabelName(FRHICommandListBase& RHICmdList, FRHITexture* TextureRHI, const TCHAR* Name)
{
#if RHI_USE_RESOURCE_DEBUG_NAME
TextureRHI->SetName(Name);
SetD3D11ResourceName(ResourceCast(TextureRHI), Name);
#endif
}
FD3D11Texture* FD3D11DynamicRHI::CreateTextureFromResource(bool bTextureArray, bool bCubeTexture, EPixelFormat Format, ETextureCreateFlags TexCreateFlags, const FClearValueBinding& ClearValueBinding, ID3D11Texture2D* TextureResource)
{
D3D11_TEXTURE2D_DESC TextureDesc;
TextureResource->GetDesc(&TextureDesc);
const bool bSRGB = EnumHasAnyFlags(TexCreateFlags, TexCreate_SRGB);
const DXGI_FORMAT PlatformResourceFormat = UE::DXGIUtilities::GetPlatformTextureResourceFormat(Format, TexCreateFlags);
const DXGI_FORMAT PlatformShaderResourceFormat = UE::DXGIUtilities::FindShaderResourceFormat(PlatformResourceFormat, bSRGB);
const DXGI_FORMAT PlatformRenderTargetFormat = UE::DXGIUtilities::FindShaderResourceFormat(PlatformResourceFormat, bSRGB);
const bool bIsMultisampled = TextureDesc.SampleDesc.Count > 1;
TRefCountPtr<ID3D11ShaderResourceView> ShaderResourceView;
TArray<TRefCountPtr<ID3D11RenderTargetView> > RenderTargetViews;
TRefCountPtr<ID3D11DepthStencilView> DepthStencilViews[FExclusiveDepthStencil::MaxIndex];
bool bCreateRTV = (TextureDesc.BindFlags & D3D11_BIND_RENDER_TARGET) != 0;
bool bCreateDSV = (TextureDesc.BindFlags & D3D11_BIND_DEPTH_STENCIL) != 0;
bool bCreateShaderResource = (TextureDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE) != 0;
// DXGI_FORMAT_NV12 allows us to create RTV and SRV but only with other formats, so we should block creation here.
// @todo: Should this be a check? Seems wrong to just silently change what the caller asked for.
if (Format == PF_NV12 || Format == PF_P010)
{
bCreateRTV = false;
bCreateShaderResource = false;
}
bool bCreatedRTVPerSlice = false;
if(bCreateRTV)
{
// Create a render target view for each mip
for (uint32 MipIndex = 0; MipIndex < TextureDesc.MipLevels; MipIndex++)
{
if (EnumHasAnyFlags(TexCreateFlags, TexCreate_TargetArraySlicesIndependently) && (bTextureArray || bCubeTexture))
{
bCreatedRTVPerSlice = true;
for (uint32 SliceIndex = 0; SliceIndex < TextureDesc.ArraySize; SliceIndex++)
{
D3D11_RENDER_TARGET_VIEW_DESC RTVDesc;
FMemory::Memzero(RTVDesc);
RTVDesc.Format = PlatformRenderTargetFormat;
if (bIsMultisampled)
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
RTVDesc.Texture2DMSArray.FirstArraySlice = SliceIndex;
RTVDesc.Texture2DMSArray.ArraySize = 1;
}
else
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
RTVDesc.Texture2DArray.FirstArraySlice = SliceIndex;
RTVDesc.Texture2DArray.ArraySize = 1;
RTVDesc.Texture2DArray.MipSlice = MipIndex;
}
TRefCountPtr<ID3D11RenderTargetView> RenderTargetView;
VERIFYD3D11RESULT_EX(Direct3DDevice->CreateRenderTargetView(TextureResource,&RTVDesc,RenderTargetView.GetInitReference()), Direct3DDevice);
RenderTargetViews.Add(RenderTargetView);
}
}
else
{
D3D11_RENDER_TARGET_VIEW_DESC RTVDesc;
FMemory::Memzero(RTVDesc);
RTVDesc.Format = PlatformRenderTargetFormat;
if (bTextureArray || bCubeTexture)
{
if (bIsMultisampled)
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
RTVDesc.Texture2DMSArray.FirstArraySlice = 0;
RTVDesc.Texture2DMSArray.ArraySize = TextureDesc.ArraySize;
}
else
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
RTVDesc.Texture2DArray.FirstArraySlice = 0;
RTVDesc.Texture2DArray.ArraySize = TextureDesc.ArraySize;
RTVDesc.Texture2DArray.MipSlice = MipIndex;
}
}
else
{
if (bIsMultisampled)
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
// Nothing to set
}
else
{
RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
RTVDesc.Texture2D.MipSlice = MipIndex;
}
}
TRefCountPtr<ID3D11RenderTargetView> RenderTargetView;
VERIFYD3D11RESULT_EX(Direct3DDevice->CreateRenderTargetView(TextureResource,&RTVDesc,RenderTargetView.GetInitReference()), Direct3DDevice);
RenderTargetViews.Add(RenderTargetView);
}
}
}
if(bCreateDSV)
{
// Create a depth-stencil-view for the texture.
D3D11_DEPTH_STENCIL_VIEW_DESC DSVDesc;
FMemory::Memzero(DSVDesc);
DSVDesc.Format = UE::DXGIUtilities::FindDepthStencilFormat(PlatformResourceFormat);
if (bTextureArray || bCubeTexture)
{
if (bIsMultisampled)
{
DSVDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY;
DSVDesc.Texture2DMSArray.FirstArraySlice = 0;
DSVDesc.Texture2DMSArray.ArraySize = TextureDesc.ArraySize;
}
else
{
DSVDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
DSVDesc.Texture2DArray.FirstArraySlice = 0;
DSVDesc.Texture2DArray.ArraySize = TextureDesc.ArraySize;
DSVDesc.Texture2DArray.MipSlice = 0;
}
}
else
{
if (bIsMultisampled)
{
DSVDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
// Nothing to set
}
else
{
DSVDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
DSVDesc.Texture2D.MipSlice = 0;
}
}
for (uint32 AccessType = 0; AccessType < FExclusiveDepthStencil::MaxIndex; ++AccessType)
{
// Create a read-only access views for the texture.
// Read-only DSVs are not supported in Feature Level 10 so
// a dummy DSV is created in order reduce logic complexity at a higher-level.
DSVDesc.Flags = (AccessType & FExclusiveDepthStencil::DepthRead_StencilWrite) ? D3D11_DSV_READ_ONLY_DEPTH : 0;
if (UE::DXGIUtilities::HasStencilBits(DSVDesc.Format))
{
DSVDesc.Flags |= (AccessType & FExclusiveDepthStencil::DepthWrite_StencilRead) ? D3D11_DSV_READ_ONLY_STENCIL : 0;
}
VERIFYD3D11RESULT_EX(Direct3DDevice->CreateDepthStencilView(TextureResource,&DSVDesc,DepthStencilViews[AccessType].GetInitReference()), Direct3DDevice);
}
}
// Create a shader resource view for the texture.
if (bCreateShaderResource)
{
{
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
FMemory::Memzero(SRVDesc);
SRVDesc.Format = PlatformShaderResourceFormat;
if (bCubeTexture && bTextureArray)
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY;
SRVDesc.TextureCubeArray.MostDetailedMip = 0;
SRVDesc.TextureCubeArray.MipLevels = TextureDesc.MipLevels;
SRVDesc.TextureCubeArray.First2DArrayFace = 0;
SRVDesc.TextureCubeArray.NumCubes = TextureDesc.ArraySize / 6;
}
else if (bCubeTexture)
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
SRVDesc.TextureCube.MostDetailedMip = 0;
SRVDesc.TextureCube.MipLevels = TextureDesc.MipLevels;
}
else if (bTextureArray)
{
if (bIsMultisampled)
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
SRVDesc.Texture2DMSArray.FirstArraySlice = 0;
SRVDesc.Texture2DMSArray.ArraySize = TextureDesc.ArraySize;
}
else
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
SRVDesc.Texture2DArray.MostDetailedMip = 0;
SRVDesc.Texture2DArray.MipLevels = TextureDesc.MipLevels;
SRVDesc.Texture2DArray.FirstArraySlice = 0;
SRVDesc.Texture2DArray.ArraySize = TextureDesc.ArraySize;
}
}
else
{
if (bIsMultisampled)
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
// Nothing to set
}
else
{
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MostDetailedMip = 0;
SRVDesc.Texture2D.MipLevels = TextureDesc.MipLevels;
}
}
VERIFYD3D11RESULT_EX(Direct3DDevice->CreateShaderResourceView(TextureResource,&SRVDesc,ShaderResourceView.GetInitReference()), Direct3DDevice);
}
check(IsValidRef(ShaderResourceView));
}
const ETextureDimension Dimension = bTextureArray
? (bCubeTexture ? ETextureDimension::TextureCubeArray : ETextureDimension::Texture2DArray)
: (bCubeTexture ? ETextureDimension::TextureCube : ETextureDimension::Texture2D );
const FRHITextureCreateDesc RHITextureDesc =
FRHITextureCreateDesc::Create(TEXT("FD3D11DynamicRHI::CreateTextureFromResource"), Dimension)
.SetExtent(TextureDesc.Width, TextureDesc.Height)
.SetFormat((EPixelFormat)Format)
.SetClearValue(ClearValueBinding)
.SetArraySize(TextureDesc.ArraySize)
.SetFlags(TexCreateFlags)
.SetNumMips(TextureDesc.MipLevels)
.SetNumSamples(TextureDesc.SampleDesc.Count)
.DetermineInititialState();
FD3D11Texture* Texture2D = new FD3D11Texture(
RHITextureDesc,
TextureResource,
ShaderResourceView,
TextureDesc.ArraySize,
bCreatedRTVPerSlice,
RenderTargetViews,
DepthStencilViews
);
return Texture2D;
}
FTextureRHIRef FD3D11DynamicRHI::RHICreateTexture2DFromResource(EPixelFormat Format, ETextureCreateFlags TexCreateFlags, const FClearValueBinding& ClearValueBinding, ID3D11Texture2D* TextureResource)
{
return CreateTextureFromResource(false, false, Format, TexCreateFlags, ClearValueBinding, TextureResource);
}
FTextureRHIRef FD3D11DynamicRHI::RHICreateTexture2DArrayFromResource(EPixelFormat Format, ETextureCreateFlags TexCreateFlags, const FClearValueBinding& ClearValueBinding, ID3D11Texture2D* TextureResource)
{
return CreateTextureFromResource(true, false, Format, TexCreateFlags, ClearValueBinding, TextureResource);
}
FTextureRHIRef FD3D11DynamicRHI::RHICreateTextureCubeFromResource(EPixelFormat Format, ETextureCreateFlags TexCreateFlags, const FClearValueBinding& ClearValueBinding, ID3D11Texture2D* TextureResource)
{
return CreateTextureFromResource(false, true, Format, TexCreateFlags, ClearValueBinding, TextureResource);
}
FD3D11Texture::FD3D11Texture(const FRHITextureCreateDesc& InDesc)
: FRHITexture(InDesc)
, bCreatedRTVsPerSlice(false)
, bAlias(false)
{
}
void FD3D11Texture::FinalizeCreation(
ID3D11Resource* InResource,
ID3D11ShaderResourceView* InShaderResourceView,
int32 InRTVArraySize,
bool bInCreatedRTVsPerSlice,
TConstArrayView<TRefCountPtr<ID3D11RenderTargetView>> InRenderTargetViews,
TConstArrayView<TRefCountPtr<ID3D11DepthStencilView>> InDepthStencilViews)
{
Resource = InResource;
ShaderResourceView = InShaderResourceView;
RenderTargetViews = InRenderTargetViews;
RTVArraySize = InRTVArraySize;
bCreatedRTVsPerSlice = bInCreatedRTVsPerSlice;
// Set the DSVs for all the access type combinations
if (InDepthStencilViews.Num())
{
check(InDepthStencilViews.Num() == FExclusiveDepthStencil::MaxIndex);
for (uint32 Index = 0; Index < FExclusiveDepthStencil::MaxIndex; Index++)
{
DepthStencilViews[Index] = InDepthStencilViews[Index];
}
}
UpdateD3D11TextureStats(*this, true);
}
FD3D11Texture::FD3D11Texture(FD3D11Texture const& Other, const FString& Name, EAliasResourceParam)
: FRHITexture(FRHITextureCreateDesc(Other.GetDesc(), ERHIAccess::SRVMask, *Name))
, bAlias(true)
{
AliasResource(Other);
}
FD3D11Texture::~FD3D11Texture()
{
if (!bAlias)
{
UpdateD3D11TextureStats(*this, false);
}
}
void FD3D11Texture::AliasResource(FD3D11Texture const& Other)
{
check(bAlias);
IHVResourceHandle = Other.IHVResourceHandle;
Resource = Other.Resource;
ShaderResourceView = Other.ShaderResourceView;
RenderTargetViews = Other.RenderTargetViews;
bCreatedRTVsPerSlice = Other.bCreatedRTVsPerSlice;
RTVArraySize = Other.RTVArraySize;
for (int32 Index = 0; Index < UE_ARRAY_COUNT(DepthStencilViews); ++Index)
{
DepthStencilViews[Index] = Other.DepthStencilViews[Index];
}
}
void FD3D11DynamicRHI::RHIAliasTextureResources(FTextureRHIRef& DstTextureRHI, FTextureRHIRef& SrcTextureRHI)
{
FD3D11Texture* DstTexture = ResourceCast(DstTextureRHI);
FD3D11Texture* SrcTexture = ResourceCast(SrcTextureRHI);
check(DstTexture && SrcTexture);
DstTexture->AliasResource(*SrcTexture);
}
FTextureRHIRef FD3D11DynamicRHI::RHICreateAliasedTexture(FTextureRHIRef& SrcTextureRHI)
{
FD3D11Texture* SrcTexture = ResourceCast(SrcTextureRHI);
check(SrcTexture);
const FString Name = SrcTextureRHI->GetName().ToString() + TEXT("Alias");
return new FD3D11Texture(*SrcTexture, *Name, FD3D11Texture::CreateAlias);
}
void FD3D11DynamicRHI::RHICopyTexture(FRHITexture* SourceTextureRHI, FRHITexture* DestTextureRHI, const FRHICopyTextureInfo& CopyInfo)
{
FRHICommandList_RecursiveHazardous RHICmdList(this);
FD3D11Texture* SourceTexture = ResourceCast(SourceTextureRHI);
FD3D11Texture* DestTexture = ResourceCast(DestTextureRHI);
check(SourceTexture && DestTexture);
#if (RHI_NEW_GPU_PROFILER == 0)
RegisterGPUWork();
#endif
const FRHITextureDesc& SourceDesc = SourceTextureRHI->GetDesc();
const FRHITextureDesc& DestDesc = DestTextureRHI->GetDesc();
const uint16 SourceArraySize = SourceDesc.ArraySize * (SourceDesc.IsTextureCube() ? 6 : 1);
const uint16 DestArraySize = DestDesc.ArraySize * (DestDesc.IsTextureCube() ? 6 : 1);
const bool bAllPixels =
SourceDesc.GetSize() == DestDesc.GetSize() && (CopyInfo.Size == FIntVector::ZeroValue || CopyInfo.Size == SourceDesc.GetSize());
const bool bAllSubresources =
SourceDesc.NumMips == DestDesc.NumMips && SourceDesc.NumMips == CopyInfo.NumMips &&
SourceArraySize == DestArraySize && SourceArraySize == CopyInfo.NumSlices;
if (!bAllPixels || !bAllSubresources)
{
const FPixelFormatInfo& PixelFormatInfo = GPixelFormats[SourceTextureRHI->GetFormat()];
const FIntVector SourceSize = SourceDesc.GetSize();
const FIntVector CopySize = CopyInfo.Size == FIntVector::ZeroValue ? SourceSize >> CopyInfo.SourceMipIndex : CopyInfo.Size;
for (uint32 SliceIndex = 0; SliceIndex < CopyInfo.NumSlices; ++SliceIndex)
{
uint32 SourceSliceIndex = CopyInfo.SourceSliceIndex + SliceIndex;
uint32 DestSliceIndex = CopyInfo.DestSliceIndex + SliceIndex;
for (uint32 MipIndex = 0; MipIndex < CopyInfo.NumMips; ++MipIndex)
{
uint32 SourceMipIndex = CopyInfo.SourceMipIndex + MipIndex;
uint32 DestMipIndex = CopyInfo.DestMipIndex + MipIndex;
const uint32 SourceSubresource = D3D11CalcSubresource(SourceMipIndex, SourceSliceIndex, SourceTextureRHI->GetNumMips());
const uint32 DestSubresource = D3D11CalcSubresource(DestMipIndex, DestSliceIndex, DestTextureRHI->GetNumMips());
D3D11_BOX SrcBox;
SrcBox.left = CopyInfo.SourcePosition.X >> MipIndex;
SrcBox.top = CopyInfo.SourcePosition.Y >> MipIndex;
SrcBox.front = CopyInfo.SourcePosition.Z >> MipIndex;
SrcBox.right = AlignArbitrary<uint32>(FMath::Max<uint32>((CopyInfo.SourcePosition.X + CopySize.X) >> MipIndex, 1), PixelFormatInfo.BlockSizeX);
SrcBox.bottom = AlignArbitrary<uint32>(FMath::Max<uint32>((CopyInfo.SourcePosition.Y + CopySize.Y) >> MipIndex, 1), PixelFormatInfo.BlockSizeY);
SrcBox.back = AlignArbitrary<uint32>(FMath::Max<uint32>((CopyInfo.SourcePosition.Z + CopySize.Z) >> MipIndex, 1), PixelFormatInfo.BlockSizeZ);
const uint32 DestX = CopyInfo.DestPosition.X >> MipIndex;
const uint32 DestY = CopyInfo.DestPosition.Y >> MipIndex;
const uint32 DestZ = CopyInfo.DestPosition.Z >> MipIndex;
Direct3DDeviceIMContext->CopySubresourceRegion(DestTexture->GetResource(), DestSubresource, DestX, DestY, DestZ, SourceTexture->GetResource(), SourceSubresource, &SrcBox);
}
}
}
else
{
// Make sure the params are all by default when using this case
ensure(CopyInfo.SourceSliceIndex == 0 && CopyInfo.DestSliceIndex == 0 && CopyInfo.SourcePosition == FIntVector::ZeroValue && CopyInfo.DestPosition == FIntVector::ZeroValue && CopyInfo.SourceMipIndex == 0 && CopyInfo.DestMipIndex == 0);
Direct3DDeviceIMContext->CopyResource(DestTexture->GetResource(), SourceTexture->GetResource());
}
}
void FD3D11DynamicRHI::RHICopyBufferRegion(FRHIBuffer* DstBuffer, uint64 DstOffset, FRHIBuffer* SrcBuffer, uint64 SrcOffset, uint64 NumBytes)
{
if (!DstBuffer || !SrcBuffer || DstBuffer == SrcBuffer || !NumBytes)
{
return;
}
FD3D11Buffer* DstBufferD3D11 = ResourceCast(DstBuffer);
FD3D11Buffer* SrcBufferD3D11 = ResourceCast(SrcBuffer);
check(DstBufferD3D11 && SrcBufferD3D11);
check(DstOffset + NumBytes <= DstBuffer->GetSize() && SrcOffset + NumBytes <= SrcBuffer->GetSize());
#if (RHI_NEW_GPU_PROFILER == 0)
RegisterGPUWork();
#endif
D3D11_BOX SrcBox;
SrcBox.left = SrcOffset;
SrcBox.right = SrcOffset + NumBytes;
SrcBox.top = 0;
SrcBox.bottom = 1;
SrcBox.front = 0;
SrcBox.back = 1;
ID3D11Resource* DstResource = DstBufferD3D11->Resource.GetReference();
ID3D11Resource* SrcResource = SrcBufferD3D11->Resource.GetReference();
Direct3DDeviceIMContext->CopySubresourceRegion(DstResource, 0, DstOffset, 0, 0, SrcResource, 0, &SrcBox);
}