Files

580 lines
22 KiB
C++

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if GRAPHICS_API_DIRECTX11
#include "GPUTextureDX11.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
bool GPUTextureDX11::OnInit()
{
// Cache formats
const PixelFormat format = Format();
const PixelFormat typelessFormat = PixelFormatExtensions::MakeTypeless(format);
const DXGI_FORMAT dxgiFormat = RenderToolsDX::ToDxgiFormat(typelessFormat);
_dxgiFormatDSV = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindDepthStencilFormat(format));
_dxgiFormatSRV = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindShaderResourceFormat(format, _sRGB));
_dxgiFormatRTV = _dxgiFormatSRV;
_dxgiFormatUAV = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindUnorderedAccessFormat(format));
// Cache properties
auto device = _device->GetDevice();
bool useSRV = IsShaderResource();
bool useDSV = IsDepthStencil();
bool useRTV = IsRenderTarget();
bool useUAV = IsUnorderedAccess();
// Create resource
HRESULT result;
if (IsVolume())
{
// Create texture description
D3D11_TEXTURE3D_DESC textureDesc;
textureDesc.MipLevels = MipLevels();
textureDesc.Format = dxgiFormat;
textureDesc.Width = Width();
textureDesc.Height = Height();
textureDesc.Depth = Depth();
textureDesc.BindFlags = 0;
textureDesc.CPUAccessFlags = RenderToolsDX::GetDX11CpuAccessFlagsFromUsage(_desc.Usage);
textureDesc.MiscFlags = 0;
textureDesc.Usage = RenderToolsDX::ToD3D11Usage(_desc.Usage);
if (useSRV)
textureDesc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
if (useRTV)
textureDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
if (useDSV)
textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
if (useUAV)
textureDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
// Create texture
ID3D11Texture3D* texture;
result = device->CreateTexture3D(&textureDesc, nullptr, &texture);
_resource = texture;
}
else
{
// Create texture description
D3D11_TEXTURE2D_DESC textureDesc;
textureDesc.MipLevels = MipLevels();
textureDesc.Format = dxgiFormat;
textureDesc.Width = Width();
textureDesc.Height = Height();
textureDesc.Usage = RenderToolsDX::ToD3D11Usage(_desc.Usage);
textureDesc.BindFlags = 0;
textureDesc.CPUAccessFlags = RenderToolsDX::GetDX11CpuAccessFlagsFromUsage(_desc.Usage);
textureDesc.ArraySize = ArraySize();
textureDesc.SampleDesc.Count = static_cast<UINT>(_desc.MultiSampleLevel);
textureDesc.SampleDesc.Quality = 0;
textureDesc.MiscFlags = 0;
if (useSRV)
textureDesc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
if (useRTV)
textureDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
if (useDSV)
textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
if (useUAV)
textureDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
if (_device->GetFeatureLevel() >= FeatureLevel::SM5 && IsMultiSample())
textureDesc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
if (IsCubeMap())
textureDesc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
// Create texture
ID3D11Texture2D* texture;
result = device->CreateTexture2D(&textureDesc, nullptr, &texture);
_resource = texture;
}
LOG_DIRECTX_RESULT_WITH_RETURN(result, true);
ASSERT(_resource != nullptr);
DX_SET_DEBUG_NAME(_resource, GetName());
// Update memory usage
_memoryUsage = calculateMemoryUsage();
// Initialize handles to the resource
if (IsRegularTexture())
{
// 'Regular' texture is using only one handle (texture/cubemap)
_handlesPerSlice.Resize(1, false);
}
else
{
// Create all handles
initHandles();
}
return false;
}
void GPUTextureDX11::OnResidentMipsChanged()
{
const int32 firstMipIndex = MipLevels() - ResidentMipLevels();
const int32 mipLevels = ResidentMipLevels();
D3D11_SHADER_RESOURCE_VIEW_DESC srDesc;
srDesc.Format = _dxgiFormatSRV;
if (IsCubeMap())
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srDesc.TextureCube.MostDetailedMip = firstMipIndex;
srDesc.TextureCube.MipLevels = mipLevels;
}
else if (IsVolume())
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
srDesc.Texture3D.MostDetailedMip = firstMipIndex;
srDesc.Texture3D.MipLevels = mipLevels;
}
else
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srDesc.Texture2D.MostDetailedMip = firstMipIndex;
srDesc.Texture2D.MipLevels = mipLevels;
}
ID3D11ShaderResourceView* srView = nullptr;
if (mipLevels != 0)
VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateShaderResourceView(_resource, &srDesc, &srView));
GPUTextureViewDX11& view = IsVolume() ? _handleVolume : _handlesPerSlice[0];
if (view.GetParent() == nullptr)
view.Init(this, nullptr, srView, nullptr, nullptr, Format(), MultiSampleLevel());
else
view.SetSRV(srView);
}
void GPUTextureDX11::OnReleaseGPU()
{
_handlesPerMip.Resize(0, false);
_handlesPerSlice.Resize(0, false);
_handleArray.Release();
_handleVolume.Release();
_handleReadOnlyDepth.Release();
DX_SAFE_RELEASE_CHECK(_resource, 0);
// Base
GPUTexture::OnReleaseGPU();
}
#define CLEAR_VIEWS() rtView = nullptr; srView = nullptr; dsView = nullptr; uaView = nullptr
void GPUTextureDX11::initHandles()
{
ID3D11RenderTargetView* rtView;
ID3D11ShaderResourceView* srView;
ID3D11DepthStencilView* dsView;
ID3D11UnorderedAccessView* uaView;
D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
D3D11_SHADER_RESOURCE_VIEW_DESC srDesc;
D3D11_DEPTH_STENCIL_VIEW_DESC dsDesc;
D3D11_UNORDERED_ACCESS_VIEW_DESC uaDesc;
CLEAR_VIEWS();
rtDesc.Format = _dxgiFormatRTV;
srDesc.Format = _dxgiFormatSRV;
dsDesc.Format = _dxgiFormatDSV;
dsDesc.Flags = 0;
uaDesc.Format = _dxgiFormatUAV;
// Cache properties
auto device = _device->GetDevice();
bool useSRV = IsShaderResource();
bool useDSV = IsDepthStencil();
bool useRTV = IsRenderTarget();
bool useUAV = IsUnorderedAccess();
int32 arraySize = ArraySize();
int32 mipLevels = MipLevels();
bool isArray = arraySize > 1;
bool isCubeMap = IsCubeMap();
bool isMsaa = IsMultiSample();
bool isVolume = IsVolume();
auto format = Format();
auto msaa = MultiSampleLevel();
// Create resource views
if (isVolume)
{
// Create handle for whole 3d texture
if (useSRV)
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
srDesc.Texture3D.MostDetailedMip = 0;
srDesc.Texture3D.MipLevels = mipLevels;
VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView));
}
if (useRTV)
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
rtDesc.Texture3D.MipSlice = 0;
rtDesc.Texture3D.FirstWSlice = 0;
rtDesc.Texture3D.WSize = Depth();
VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView));
}
if (useUAV)
{
uaDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
uaDesc.Texture3D.MipSlice = 0;
uaDesc.Texture3D.WSize = Depth();
uaDesc.Texture3D.FirstWSlice = 0;
VALIDATE_DIRECTX_CALL(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView));
}
_handleVolume.Init(this, rtView, srView, nullptr, uaView, format, msaa);
// Init per slice views
_handlesPerSlice.Resize(Depth(), false);
if (_desc.HasPerSliceViews() && useRTV)
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
rtDesc.Texture3D.MipSlice = 0;
rtDesc.Texture3D.WSize = 1;
for (int32 sliceIndex = 0; sliceIndex < Depth(); sliceIndex++)
{
rtDesc.Texture3D.FirstWSlice = sliceIndex;
VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView));
_handlesPerSlice[sliceIndex].Init(this, rtView, nullptr, nullptr, nullptr, format, msaa);
}
}
}
else if (isArray)
{
// Resize handles
_handlesPerSlice.Resize(ArraySize(), false);
// Create per array slice handles
for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
{
CLEAR_VIEWS();
if (useDSV)
{
/*if (isCubeMap)
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
dsDesc.Texture2DArray.ArraySize = 6;
dsDesc.Texture2DArray.FirstArraySlice = arrayIndex * 6;
dsDesc.Texture2DArray.MipSlice = 0;
}
else*/
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
dsDesc.Texture2DArray.ArraySize = 1;
dsDesc.Texture2DArray.FirstArraySlice = arrayIndex;
dsDesc.Texture2DArray.MipSlice = 0;
}
VALIDATE_DIRECTX_CALL(device->CreateDepthStencilView(_resource, &dsDesc, &dsView));
}
if (useRTV)
{
/*if (isCubeMap)
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtDesc.Texture2DArray.ArraySize = 6;
rtDesc.Texture2DArray.FirstArraySlice = arrayIndex * 6;
rtDesc.Texture2DArray.MipSlice = 0;
}
else*/
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtDesc.Texture2DArray.ArraySize = 1;
rtDesc.Texture2DArray.FirstArraySlice = arrayIndex;
rtDesc.Texture2DArray.MipSlice = 0;
}
VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView));
}
if (useSRV)
{
// When GetFeatureLevel returns D3D_FEATURE_LEVEL_10_0 or less, Resources created with D3D11_RESOURCE_MISC_TEXTURECUBE may only be treated as cubemap ShaderResourceViews.
// (ViewDimension must be D3D11_SRV_DIMENSION_TEXTURECUBE) [ STATE_CREATION ERROR #126: CREATESHADERRESOURCEVIEW_INVALIDDESC]
if (isCubeMap && _device->GetRendererType() != RendererType::DirectX10)
{
/*if (isCubeMap)
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY;
srDesc.TextureCubeArray.First2DArrayFace = arrayIndex * 6;
srDesc.TextureCubeArray.NumCubes = 1;
srDesc.TextureCubeArray.MipLevels = mipLevels;
srDesc.TextureCubeArray.MostDetailedMip = 0;
}
else*/
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srDesc.Texture2DArray.ArraySize = 1;
srDesc.Texture2DArray.FirstArraySlice = arrayIndex;
srDesc.Texture2DArray.MipLevels = mipLevels;
srDesc.Texture2DArray.MostDetailedMip = 0;
}
VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView));
}
}
_handlesPerSlice[arrayIndex].Init(this, rtView, srView, dsView, nullptr, format, msaa);
}
// Create whole array handle
{
CLEAR_VIEWS();
if (useDSV)
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
dsDesc.Texture2DArray.ArraySize = arraySize;
dsDesc.Texture2DArray.FirstArraySlice = 0;
dsDesc.Texture2DArray.MipSlice = 0;
VALIDATE_DIRECTX_CALL(device->CreateDepthStencilView(_resource, &dsDesc, &dsView));
}
if (useRTV)
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtDesc.Texture2DArray.ArraySize = arraySize;
rtDesc.Texture2DArray.FirstArraySlice = 0;
rtDesc.Texture2DArray.MipSlice = 0;
VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView));
}
if (useSRV)
{
if (isCubeMap)
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srDesc.TextureCube.MostDetailedMip = 0;
srDesc.TextureCube.MipLevels = mipLevels;
}
else
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srDesc.Texture2DArray.ArraySize = arraySize;
srDesc.Texture2DArray.FirstArraySlice = 0;
srDesc.Texture2DArray.MipLevels = mipLevels;
srDesc.Texture2DArray.MostDetailedMip = 0;
}
VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView));
}
if (useUAV)
{
uaDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
uaDesc.Texture2DArray.MipSlice = 0;
uaDesc.Texture2DArray.ArraySize = arraySize;
uaDesc.Texture2DArray.FirstArraySlice = 0;
VALIDATE_DIRECTX_CALL(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView));
}
_handleArray.Init(this, rtView, srView, dsView, uaView, format, msaa);
}
}
else
{
// Resize handles
_handlesPerSlice.Resize(1, false);
CLEAR_VIEWS();
// Create single handle for the whole texture
if (useDSV)
{
if (isCubeMap)
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
dsDesc.Texture2DArray.MipSlice = 0;
dsDesc.Texture2DArray.FirstArraySlice = 0;
dsDesc.Texture2DArray.ArraySize = arraySize * 6;
}
else if (isMsaa)
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
}
else
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsDesc.Texture2D.MipSlice = 0;
}
VALIDATE_DIRECTX_CALL(device->CreateDepthStencilView(_resource, &dsDesc, &dsView));
}
if (useRTV)
{
if (isCubeMap)
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtDesc.Texture2DArray.MipSlice = 0;
rtDesc.Texture2DArray.FirstArraySlice = 0;
rtDesc.Texture2DArray.ArraySize = arraySize * 6;
}
else if (isMsaa)
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
}
else
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtDesc.Texture2D.MipSlice = 0;
}
VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView));
}
if (useSRV)
{
if (isCubeMap)
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srDesc.TextureCube.MostDetailedMip = 0;
srDesc.TextureCube.MipLevels = mipLevels;
}
else if (isMsaa)
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
}
else
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srDesc.Texture2D.MostDetailedMip = 0;
srDesc.Texture2D.MipLevels = mipLevels;
}
VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView));
}
if (useUAV)
{
uaDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
uaDesc.Texture2D.MipSlice = 0;
VALIDATE_DIRECTX_CALL(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView));
}
_handlesPerSlice[0].Init(this, rtView, srView, dsView, uaView, format, msaa);
}
// Init per mip map handles
if (HasPerMipViews())
{
// Init handles
_handlesPerMip.Resize(arraySize, false);
for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
{
auto& slice = _handlesPerMip[arrayIndex];
slice.Resize(mipLevels, false);
for (int32 mipIndex = 0; mipIndex < mipLevels; mipIndex++)
{
dsView = nullptr;
rtView = nullptr;
srView = nullptr;
// DSV
if (useDSV)
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
dsDesc.Texture2DArray.ArraySize = 1;
dsDesc.Texture2DArray.FirstArraySlice = arrayIndex;
dsDesc.Texture2DArray.MipSlice = mipIndex;
_device->GetDevice()->CreateDepthStencilView(_resource, &dsDesc, &dsView);
}
// RTV
if (useRTV)
{
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtDesc.Texture2DArray.ArraySize = 1;
rtDesc.Texture2DArray.FirstArraySlice = arrayIndex;
rtDesc.Texture2DArray.MipSlice = mipIndex;
_device->GetDevice()->CreateRenderTargetView(_resource, &rtDesc, &rtView);
}
// SRV
if (useSRV)
{
// When GetFeatureLevel returns D3D_FEATURE_LEVEL_10_0 or less, Resources created with D3D11_RESOURCE_MISC_TEXTURECUBE may only be treated as cubemap ShaderResourceViews.
// (ViewDimension must be D3D11_SRV_DIMENSION_TEXTURECUBE) [ STATE_CREATION ERROR #126: CREATESHADERRESOURCEVIEW_INVALIDDESC]
if ((isCubeMap && _device->GetRendererType() == RendererType::DirectX10) == false)
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srDesc.Texture2DArray.ArraySize = 1;
srDesc.Texture2DArray.FirstArraySlice = arrayIndex;
srDesc.Texture2DArray.MipLevels = 1;
srDesc.Texture2DArray.MostDetailedMip = mipIndex;
_device->GetDevice()->CreateShaderResourceView(_resource, &srDesc, &srView);
}
}
slice[mipIndex].Init(this, rtView, srView, dsView, nullptr, format, msaa);
}
}
}
// Read-only depth-stencil
if (EnumHasAnyFlags(_desc.Flags, GPUTextureFlags::ReadOnlyDepthView))
{
CLEAR_VIEWS();
// Create single handle for the whole texture
if (useDSV)
{
if (isCubeMap)
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
dsDesc.Texture2DArray.MipSlice = 0;
dsDesc.Texture2DArray.FirstArraySlice = 0;
dsDesc.Texture2DArray.ArraySize = arraySize * 6;
}
else if (isMsaa)
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
}
else
{
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsDesc.Texture2D.MipSlice = 0;
}
dsDesc.Flags = D3D11_DSV_READ_ONLY_DEPTH;
if (PixelFormatExtensions::HasStencil(format))
dsDesc.Flags |= D3D11_DSV_READ_ONLY_STENCIL;
VALIDATE_DIRECTX_CALL(device->CreateDepthStencilView(_resource, &dsDesc, &dsView));
}
ASSERT(!useRTV);
rtView = nullptr;
if (useSRV)
{
if (isCubeMap)
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srDesc.TextureCube.MostDetailedMip = 0;
srDesc.TextureCube.MipLevels = mipLevels;
}
else if (isMsaa)
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
}
else
{
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srDesc.Texture2D.MostDetailedMip = 0;
srDesc.Texture2D.MipLevels = mipLevels;
}
VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView));
}
_handleReadOnlyDepth.Init(this, rtView, srView, dsView, nullptr, format, msaa);
}
}
bool GPUTextureDX11::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch)
{
if (!IsStaging())
{
LOG(Warning, "Texture::GetData is valid only for staging resources.");
return true;
}
GPUDeviceLock lock(_device);
// Map the staging resource mip map for reading
const uint32 subresource = RenderToolsDX::CalcSubresourceIndex(mipMapIndex, arrayIndex, MipLevels());
D3D11_MAPPED_SUBRESOURCE mapped;
const HRESULT mapResult = _device->GetIM()->Map(_resource, subresource, D3D11_MAP_READ, 0, &mapped);
if (FAILED(mapResult))
{
LOG_DIRECTX_RESULT(mapResult);
return true;
}
data.Copy(mapped.pData, mapped.RowPitch, mapped.DepthPitch, Depth(), mipRowPitch);
// Unmap texture
_device->GetIM()->Unmap(_resource, subresource);
return false;
}
#endif