// Copyright (c) 2012-2022 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(_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); 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; VALIDATE_DIRECTX_RESULT(_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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(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_RESULT(device->CreateShaderResourceView(_resource, &srDesc, &srView)); } if (useUAV) { uaDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; uaDesc.Texture2D.MipSlice = 0; VALIDATE_DIRECTX_RESULT(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 (_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_RESULT(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_RESULT(device->CreateShaderResourceView(_resource, &srDesc, &srView)); } _handleReadOnlyDepth.Init(this, rtView, srView, dsView, nullptr, format, msaa); } } bool GPUTextureDX11::GetData(int32 arrayOrDepthSliceIndex, 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, arrayOrDepthSliceIndex, 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; } // Check if target row pitch is the same if (mipRowPitch == mapped.RowPitch || mipRowPitch == 0) { // Init mip info data.Lines = mapped.DepthPitch / mapped.RowPitch; data.DepthPitch = mapped.DepthPitch; data.RowPitch = mapped.RowPitch; // Copy data data.Data.Copy((byte*)mapped.pData, mapped.DepthPitch); } else { // Init mip info data.Lines = mapped.DepthPitch / mapped.RowPitch; data.DepthPitch = mipRowPitch * data.Lines; data.RowPitch = mipRowPitch; // Copy data data.Data.Allocate(data.DepthPitch); for (uint32 i = 0; i < data.Lines; i++) { Platform::MemoryCopy(data.Data.Get() + data.RowPitch * i, ((byte*)mapped.pData) + mapped.RowPitch * i, data.RowPitch); } } // Unmap texture _device->GetIM()->Unmap(_resource, subresource); return false; } #endif