/* * Copyright 2019-2021 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * In no event and under no legal theory, whether in tort (including negligence), * contract, or otherwise, unless required by applicable law (such as deliberate * and grossly negligent acts) or agreed to in writing, shall any Contributor be * liable for any damages, including any direct, indirect, special, incidental, * or consequential damages of any character arising as a result of this License or * out of the use or inability to use the software (including but not limited to damages * for loss of goodwill, work stoppage, computer failure or malfunction, or any and * all other commercial damages or losses), even if such Contributor has been advised * of the possibility of such damages. */ #include "pch.h" #include "TextureD3D12Impl.hpp" #include "RenderDeviceD3D12Impl.hpp" #include "DeviceContextD3D12Impl.hpp" #include "TextureViewD3D12Impl.hpp" #include "D3D12TypeConversions.hpp" #include "DXGITypeConversions.hpp" #include "d3dx12_win.h" #include "EngineMemory.h" #include "StringTools.hpp" namespace Diligent { static DXGI_FORMAT GetClearFormat(DXGI_FORMAT Fmt, D3D12_RESOURCE_FLAGS Flags) { if (Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) { // clang-format off switch (Fmt) { case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_D32_FLOAT; case DXGI_FORMAT_R16_TYPELESS: return DXGI_FORMAT_D16_UNORM; case DXGI_FORMAT_R24G8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; case DXGI_FORMAT_R32G8X24_TYPELESS: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; } // clang-format on } else if (Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) { // clang-format off switch (Fmt) { case DXGI_FORMAT_R32G32B32A32_TYPELESS: return DXGI_FORMAT_R32G32B32A32_FLOAT; case DXGI_FORMAT_R32G32B32_TYPELESS: return DXGI_FORMAT_R32G32B32_FLOAT; case DXGI_FORMAT_R16G16B16A16_TYPELESS: return DXGI_FORMAT_R16G16B16A16_FLOAT; case DXGI_FORMAT_R32G32_TYPELESS: return DXGI_FORMAT_R32G32_FLOAT; case DXGI_FORMAT_R10G10B10A2_TYPELESS: return DXGI_FORMAT_R10G10B10A2_UNORM; case DXGI_FORMAT_R8G8B8A8_TYPELESS: return DXGI_FORMAT_R8G8B8A8_UNORM; case DXGI_FORMAT_R16G16_TYPELESS: return DXGI_FORMAT_R16G16_FLOAT; case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT; case DXGI_FORMAT_R8G8_TYPELESS: return DXGI_FORMAT_R8G8_UNORM; case DXGI_FORMAT_R16_TYPELESS: return DXGI_FORMAT_R16_FLOAT; case DXGI_FORMAT_R8_TYPELESS: return DXGI_FORMAT_R8_UNORM; case DXGI_FORMAT_B8G8R8A8_TYPELESS: return DXGI_FORMAT_B8G8R8A8_UNORM; case DXGI_FORMAT_B8G8R8X8_TYPELESS: return DXGI_FORMAT_B8G8R8X8_UNORM; } // clang-format on } return Fmt; } D3D12_RESOURCE_DESC TextureD3D12Impl::GetD3D12TextureDesc() const { D3D12_RESOURCE_DESC Desc = {}; Desc.Alignment = 0; if (m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY || m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY || m_Desc.Type == RESOURCE_DIM_TEX_CUBE || m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) Desc.DepthOrArraySize = (UINT16)m_Desc.ArraySize; else if (m_Desc.Type == RESOURCE_DIM_TEX_3D) Desc.DepthOrArraySize = (UINT16)m_Desc.Depth; else Desc.DepthOrArraySize = 1; if (m_Desc.Type == RESOURCE_DIM_TEX_1D || m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY) Desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE1D; else if (m_Desc.Type == RESOURCE_DIM_TEX_2D || m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY || m_Desc.Type == RESOURCE_DIM_TEX_CUBE || m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) Desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; else if (m_Desc.Type == RESOURCE_DIM_TEX_3D) Desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D; else { LOG_ERROR_AND_THROW("Unknown texture type"); } Desc.Flags = D3D12_RESOURCE_FLAG_NONE; if (m_Desc.BindFlags & BIND_RENDER_TARGET) Desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; if (m_Desc.BindFlags & BIND_DEPTH_STENCIL) Desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; if ((m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || (m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS)) Desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; if ((m_Desc.BindFlags & BIND_SHADER_RESOURCE) == 0 && (m_Desc.BindFlags & BIND_DEPTH_STENCIL) != 0) Desc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; auto Format = TexFormatToDXGI_Format(m_Desc.Format, m_Desc.BindFlags); if (Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB && (Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) Desc.Format = DXGI_FORMAT_R8G8B8A8_TYPELESS; else Desc.Format = Format; Desc.Height = UINT{m_Desc.Height}; Desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; Desc.MipLevels = static_cast(m_Desc.MipLevels); Desc.SampleDesc.Count = m_Desc.SampleCount; Desc.SampleDesc.Quality = 0; Desc.Width = UINT64{m_Desc.Width}; return Desc; } TextureD3D12Impl::TextureD3D12Impl(IReferenceCounters* pRefCounters, FixedBlockMemoryAllocator& TexViewObjAllocator, RenderDeviceD3D12Impl* pRenderDeviceD3D12, const TextureDesc& TexDesc, const TextureData* pInitData /*= nullptr*/) : TTextureBase{pRefCounters, TexViewObjAllocator, pRenderDeviceD3D12, TexDesc} { if (m_Desc.Usage == USAGE_IMMUTABLE && (pInitData == nullptr || pInitData->pSubResources == nullptr)) LOG_ERROR_AND_THROW("Immutable textures must be initialized with data at creation time: pInitData can't be null"); if ((m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) != 0) { if (m_Desc.Type != RESOURCE_DIM_TEX_2D && m_Desc.Type != RESOURCE_DIM_TEX_2D_ARRAY) { LOG_ERROR_AND_THROW("Mipmap generation is currently only supported for 2D textures and 2D texture arrays in d3d12 backend"); } } D3D12_RESOURCE_DESC Desc = GetD3D12TextureDesc(); auto* pd3d12Device = pRenderDeviceD3D12->GetD3D12Device(); bool bInitializeTexture = (pInitData != nullptr && pInitData->pSubResources != nullptr && pInitData->NumSubresources > 0); if (m_Desc.Usage == USAGE_IMMUTABLE || m_Desc.Usage == USAGE_DEFAULT || m_Desc.Usage == USAGE_DYNAMIC) { D3D12_CLEAR_VALUE ClearValue = {}; D3D12_CLEAR_VALUE* pClearValue = nullptr; if (Desc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { if (m_Desc.ClearValue.Format != TEX_FORMAT_UNKNOWN) ClearValue.Format = TexFormatToDXGI_Format(m_Desc.ClearValue.Format); else { auto Format = TexFormatToDXGI_Format(m_Desc.Format, m_Desc.BindFlags); ClearValue.Format = GetClearFormat(Format, Desc.Flags); } if (Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) { for (int i = 0; i < 4; ++i) ClearValue.Color[i] = m_Desc.ClearValue.Color[i]; } else if (Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) { ClearValue.DepthStencil.Depth = m_Desc.ClearValue.DepthStencil.Depth; ClearValue.DepthStencil.Stencil = m_Desc.ClearValue.DepthStencil.Stencil; } pClearValue = &ClearValue; } D3D12_HEAP_PROPERTIES HeapProps = {}; HeapProps.Type = D3D12_HEAP_TYPE_DEFAULT; HeapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; HeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; HeapProps.CreationNodeMask = 1; HeapProps.VisibleNodeMask = 1; auto InitialState = bInitializeTexture ? RESOURCE_STATE_COPY_DEST : RESOURCE_STATE_UNDEFINED; SetState(InitialState); auto D3D12State = ResourceStateFlagsToD3D12ResourceStates(InitialState); auto hr = pd3d12Device->CreateCommittedResource(&HeapProps, D3D12_HEAP_FLAG_NONE, &Desc, D3D12State, pClearValue, __uuidof(m_pd3d12Resource), reinterpret_cast(static_cast(&m_pd3d12Resource))); if (FAILED(hr)) LOG_ERROR_AND_THROW("Failed to create D3D12 texture"); if (*m_Desc.Name != 0) m_pd3d12Resource->SetName(WidenString(m_Desc.Name).c_str()); if (bInitializeTexture) { Uint32 ExpectedNumSubresources = Uint32{Desc.MipLevels} * (Desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? 1 : Uint32{Desc.DepthOrArraySize}); if (pInitData->NumSubresources != ExpectedNumSubresources) LOG_ERROR_AND_THROW("Incorrect number of subresources in init data. ", ExpectedNumSubresources, " expected, while ", pInitData->NumSubresources, " provided"); UINT64 uploadBufferSize = 0; pd3d12Device->GetCopyableFootprints(&Desc, 0, pInitData->NumSubresources, 0, nullptr, nullptr, nullptr, &uploadBufferSize); D3D12_HEAP_PROPERTIES UploadHeapProps = {}; UploadHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD; UploadHeapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; UploadHeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; UploadHeapProps.CreationNodeMask = 1; UploadHeapProps.VisibleNodeMask = 1; D3D12_RESOURCE_DESC BufferDesc = {}; BufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; BufferDesc.Alignment = 0; BufferDesc.Width = uploadBufferSize; BufferDesc.Height = 1; BufferDesc.DepthOrArraySize = 1; BufferDesc.MipLevels = 1; BufferDesc.Format = DXGI_FORMAT_UNKNOWN; BufferDesc.SampleDesc.Count = 1; BufferDesc.SampleDesc.Quality = 0; BufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; BufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE; CComPtr UploadBuffer; hr = pd3d12Device->CreateCommittedResource(&UploadHeapProps, D3D12_HEAP_FLAG_NONE, &BufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, __uuidof(UploadBuffer), reinterpret_cast(static_cast(&UploadBuffer))); if (FAILED(hr)) LOG_ERROR_AND_THROW("Failed to create committed resource in an upload heap"); auto InitContext = pRenderDeviceD3D12->AllocateCommandContext(); // copy data to the intermediate upload heap and then schedule a copy from the upload heap to the default texture VERIFY_EXPR(CheckState(RESOURCE_STATE_COPY_DEST)); std::vector> D3D12SubResData(pInitData->NumSubresources, D3D12_SUBRESOURCE_DATA(), STD_ALLOCATOR_RAW_MEM(D3D12_SUBRESOURCE_DATA, GetRawAllocator(), "Allocator for vector")); for (size_t subres = 0; subres < D3D12SubResData.size(); ++subres) { D3D12SubResData[subres].pData = pInitData->pSubResources[subres].pData; D3D12SubResData[subres].RowPitch = pInitData->pSubResources[subres].Stride; D3D12SubResData[subres].SlicePitch = pInitData->pSubResources[subres].DepthStride; } auto UploadedSize = UpdateSubresources(InitContext->GetCommandList(), m_pd3d12Resource, UploadBuffer, 0, 0, pInitData->NumSubresources, D3D12SubResData.data()); VERIFY(UploadedSize == uploadBufferSize, "Incorrect uploaded data size (", UploadedSize, "). ", uploadBufferSize, " is expected"); // Command list fence should only be signaled when submitting cmd list // from the immediate context, otherwise the basic requirement will be violated // as in the scenario below // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/ // // Signaled Fence | Immediate Context | InitContext | // | | | // N | Draw(ResourceX) | | // | Release(ResourceX) | | // | - (ResourceX, N) -> Release Queue | | // | | CopyResource() | // N+1 | | CloseAndExecuteCommandContext() | // | | | // N+2 | CloseAndExecuteCommandContext() | | // | - Cmd list is submitted with number | | // | N+1, but resource it references | | // | was added to the delete queue | | // | with value N | | Uint32 QueueIndex = 0; pRenderDeviceD3D12->CloseAndExecuteTransientCommandContext(QueueIndex, std::move(InitContext)); // We MUST NOT call TransitionResource() from here, because // it will call AddRef() and potentially Release(), while // the object is not constructed yet // Add reference to the object to the release queue to keep it alive // until copy operation is complete. This must be done after // submitting command list for execution! pRenderDeviceD3D12->SafeReleaseDeviceObject(std::move(UploadBuffer), Uint64{1} << QueueIndex); } } else if (m_Desc.Usage == USAGE_STAGING) { // Create staging buffer D3D12_HEAP_PROPERTIES StaginHeapProps = {}; DEV_CHECK_ERR((m_Desc.CPUAccessFlags & (CPU_ACCESS_READ | CPU_ACCESS_WRITE)) == CPU_ACCESS_READ || (m_Desc.CPUAccessFlags & (CPU_ACCESS_READ | CPU_ACCESS_WRITE)) == CPU_ACCESS_WRITE, "Exactly one of CPU_ACCESS_READ or CPU_ACCESS_WRITE flags must be specified"); RESOURCE_STATE InitialState = RESOURCE_STATE_UNKNOWN; if (m_Desc.CPUAccessFlags & CPU_ACCESS_READ) { DEV_CHECK_ERR(!bInitializeTexture, "Readback textures should not be initialized with data"); StaginHeapProps.Type = D3D12_HEAP_TYPE_READBACK; InitialState = RESOURCE_STATE_COPY_DEST; } else if (m_Desc.CPUAccessFlags & CPU_ACCESS_WRITE) { StaginHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD; InitialState = RESOURCE_STATE_GENERIC_READ; } else UNEXPECTED("Unexpected CPU access"); SetState(InitialState); auto D3D12State = ResourceStateFlagsToD3D12ResourceStates(InitialState); StaginHeapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; StaginHeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; StaginHeapProps.CreationNodeMask = 1; StaginHeapProps.VisibleNodeMask = 1; UINT64 stagingBufferSize = 0; Uint32 NumSubresources = Uint32{Desc.MipLevels} * (Desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? 1 : Uint32{Desc.DepthOrArraySize}); m_StagingFootprints = ALLOCATE(GetRawAllocator(), "Memory for staging footprints", D3D12_PLACED_SUBRESOURCE_FOOTPRINT, NumSubresources + 1); pd3d12Device->GetCopyableFootprints(&Desc, 0, NumSubresources, 0, m_StagingFootprints, nullptr, nullptr, &stagingBufferSize); m_StagingFootprints[NumSubresources] = D3D12_PLACED_SUBRESOURCE_FOOTPRINT{stagingBufferSize}; D3D12_RESOURCE_DESC BufferDesc = {}; BufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; BufferDesc.Alignment = 0; BufferDesc.Width = stagingBufferSize; BufferDesc.Height = 1; BufferDesc.DepthOrArraySize = 1; BufferDesc.MipLevels = 1; BufferDesc.Format = DXGI_FORMAT_UNKNOWN; BufferDesc.SampleDesc.Count = 1; BufferDesc.SampleDesc.Quality = 0; BufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; BufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE; // Resources on D3D12_HEAP_TYPE_READBACK heaps do not support persistent map. Map() and Unmap() must be // called between CPU and GPU accesses to the same memory address on some system architectures, when the // page caching behavior is write-back. Map() and Unmap() invalidate and flush the last level CPU cache // on some ARM systems, to marshal data between the CPU and GPU through memory addresses with write-back behavior. // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nf-d3d12-id3d12resource-map auto hr = pd3d12Device->CreateCommittedResource(&StaginHeapProps, D3D12_HEAP_FLAG_NONE, &BufferDesc, D3D12State, nullptr, __uuidof(m_pd3d12Resource), reinterpret_cast(static_cast(&m_pd3d12Resource))); if (FAILED(hr)) LOG_ERROR_AND_THROW("Failed to create staging buffer"); if (bInitializeTexture) { const auto FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); void* pStagingData = nullptr; m_pd3d12Resource->Map(0, nullptr, &pStagingData); DEV_CHECK_ERR(pStagingData != nullptr, "Failed to map staging buffer"); if (pStagingData != nullptr) { for (Uint32 Subres = 0; Subres < NumSubresources; ++Subres) { const auto Mip = Subres % m_Desc.MipLevels; const auto MipProps = GetMipLevelProperties(m_Desc, Mip); const auto& SrcSubresData = pInitData->pSubResources[Subres]; const auto& DstFootprint = GetStagingFootprint(Subres); VERIFY_EXPR(MipProps.StorageWidth == DstFootprint.Footprint.Width); VERIFY_EXPR(MipProps.StorageHeight == DstFootprint.Footprint.Height); VERIFY_EXPR(MipProps.Depth == DstFootprint.Footprint.Depth); CopyTextureSubresource(SrcSubresData, MipProps.StorageHeight / FmtAttribs.BlockHeight, // NumRows MipProps.Depth, MipProps.RowSize, reinterpret_cast(pStagingData) + DstFootprint.Offset, DstFootprint.Footprint.RowPitch, DstFootprint.Footprint.RowPitch * DstFootprint.Footprint.Height / FmtAttribs.BlockHeight // DstDepthStride ); } } D3D12_RANGE FlushRange{0, static_cast(stagingBufferSize)}; m_pd3d12Resource->Unmap(0, &FlushRange); } } else { UNEXPECTED("Unexpected usage"); } } static TextureDesc InitTexDescFromD3D12Resource(ID3D12Resource* pTexture, const TextureDesc& SrcTexDesc) { auto ResourceDesc = pTexture->GetDesc(); TextureDesc TexDesc = SrcTexDesc; if (TexDesc.Format == TEX_FORMAT_UNKNOWN) TexDesc.Format = DXGI_FormatToTexFormat(ResourceDesc.Format); else { auto RefFormat = DXGI_FormatToTexFormat(ResourceDesc.Format); DEV_CHECK_ERR(RefFormat == TexDesc.Format, "The format specified by texture description (", GetTextureFormatAttribs(TexDesc.Format).Name, ") does not match the D3D12 resource format (", GetTextureFormatAttribs(RefFormat).Name, ")"); (void)RefFormat; } TexDesc.Width = static_cast(ResourceDesc.Width); TexDesc.Height = Uint32{ResourceDesc.Height}; TexDesc.ArraySize = Uint32{ResourceDesc.DepthOrArraySize}; TexDesc.MipLevels = Uint32{ResourceDesc.MipLevels}; switch (ResourceDesc.Dimension) { case D3D12_RESOURCE_DIMENSION_TEXTURE1D: TexDesc.Type = TexDesc.ArraySize == 1 ? RESOURCE_DIM_TEX_1D : RESOURCE_DIM_TEX_1D_ARRAY; break; case D3D12_RESOURCE_DIMENSION_TEXTURE2D: TexDesc.Type = TexDesc.ArraySize == 1 ? RESOURCE_DIM_TEX_2D : RESOURCE_DIM_TEX_2D_ARRAY; break; case D3D12_RESOURCE_DIMENSION_TEXTURE3D: TexDesc.Type = RESOURCE_DIM_TEX_3D; break; } TexDesc.SampleCount = ResourceDesc.SampleDesc.Count; TexDesc.Usage = USAGE_DEFAULT; TexDesc.BindFlags = BIND_NONE; if ((ResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) != 0) TexDesc.BindFlags |= BIND_RENDER_TARGET; if ((ResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0) TexDesc.BindFlags |= BIND_DEPTH_STENCIL; if ((ResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) != 0) TexDesc.BindFlags |= BIND_UNORDERED_ACCESS; if ((ResourceDesc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) == 0) { const auto& FormatAttribs = GetTextureFormatAttribs(TexDesc.Format); if (FormatAttribs.IsTypeless || (FormatAttribs.ComponentType != COMPONENT_TYPE_DEPTH && FormatAttribs.ComponentType != COMPONENT_TYPE_DEPTH_STENCIL)) { TexDesc.BindFlags |= BIND_SHADER_RESOURCE; } } return TexDesc; } TextureD3D12Impl::TextureD3D12Impl(IReferenceCounters* pRefCounters, FixedBlockMemoryAllocator& TexViewObjAllocator, RenderDeviceD3D12Impl* pDeviceD3D12, const TextureDesc& TexDesc, RESOURCE_STATE InitialState, ID3D12Resource* pTexture) : TTextureBase{pRefCounters, TexViewObjAllocator, pDeviceD3D12, InitTexDescFromD3D12Resource(pTexture, TexDesc)} { m_pd3d12Resource = pTexture; SetState(InitialState); } void TextureD3D12Impl::CreateViewInternal(const struct TextureViewDesc& ViewDesc, ITextureView** ppView, bool bIsDefaultView) { VERIFY(ppView != nullptr, "View pointer address is null"); if (!ppView) return; VERIFY(*ppView == nullptr, "Overwriting reference to existing object may cause memory leaks"); *ppView = nullptr; try { auto* pDeviceD3D12Impl = ValidatedCast(GetDevice()); auto& TexViewAllocator = pDeviceD3D12Impl->GetTexViewObjAllocator(); VERIFY(&TexViewAllocator == &m_dbgTexViewObjAllocator, "Texture view allocator does not match allocator provided during texture initialization"); auto UpdatedViewDesc = ViewDesc; ValidatedAndCorrectTextureViewDesc(m_Desc, UpdatedViewDesc); DescriptorHeapAllocation ViewDescriptor; switch (ViewDesc.ViewType) { case TEXTURE_VIEW_SHADER_RESOURCE: { VERIFY(m_Desc.BindFlags & BIND_SHADER_RESOURCE, "BIND_SHADER_RESOURCE flag is not set"); ViewDescriptor = pDeviceD3D12Impl->AllocateDescriptors(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); CreateSRV(UpdatedViewDesc, ViewDescriptor.GetCpuHandle()); } break; case TEXTURE_VIEW_RENDER_TARGET: { VERIFY(m_Desc.BindFlags & BIND_RENDER_TARGET, "BIND_RENDER_TARGET flag is not set"); ViewDescriptor = pDeviceD3D12Impl->AllocateDescriptors(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); CreateRTV(UpdatedViewDesc, ViewDescriptor.GetCpuHandle()); } break; case TEXTURE_VIEW_DEPTH_STENCIL: { VERIFY(m_Desc.BindFlags & BIND_DEPTH_STENCIL, "BIND_DEPTH_STENCIL is not set"); ViewDescriptor = pDeviceD3D12Impl->AllocateDescriptors(D3D12_DESCRIPTOR_HEAP_TYPE_DSV); CreateDSV(UpdatedViewDesc, ViewDescriptor.GetCpuHandle()); } break; case TEXTURE_VIEW_UNORDERED_ACCESS: { VERIFY(m_Desc.BindFlags & BIND_UNORDERED_ACCESS, "BIND_UNORDERED_ACCESS flag is not set"); ViewDescriptor = pDeviceD3D12Impl->AllocateDescriptors(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); CreateUAV(UpdatedViewDesc, ViewDescriptor.GetCpuHandle()); } break; default: UNEXPECTED("Unknown view type"); break; } DescriptorHeapAllocation TexArraySRVDescriptor, MipUAVDescriptors; if (UpdatedViewDesc.Flags & TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION) { VERIFY_EXPR((m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) != 0 && (m_Desc.Type == RESOURCE_DIM_TEX_2D || m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY)); { TexArraySRVDescriptor = pDeviceD3D12Impl->AllocateDescriptors(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1); TextureViewDesc TexArraySRVDesc = UpdatedViewDesc; // Create texture array SRV TexArraySRVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; TexArraySRVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; CreateSRV(TexArraySRVDesc, TexArraySRVDescriptor.GetCpuHandle()); } MipUAVDescriptors = pDeviceD3D12Impl->AllocateDescriptors(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_Desc.MipLevels); for (Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel) { TextureViewDesc UAVDesc = UpdatedViewDesc; // Always create texture array UAV UAVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; UAVDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS; UAVDesc.MostDetailedMip = MipLevel; UAVDesc.NumMipLevels = 1; if (UAVDesc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB) UAVDesc.Format = TEX_FORMAT_RGBA8_UNORM; else if (UAVDesc.Format == TEX_FORMAT_BGRA8_UNORM_SRGB) UAVDesc.Format = TEX_FORMAT_BGRA8_UNORM; CreateUAV(UAVDesc, MipUAVDescriptors.GetCpuHandle(MipLevel)); } } auto pViewD3D12 = NEW_RC_OBJ(TexViewAllocator, "TextureViewD3D12Impl instance", TextureViewD3D12Impl, bIsDefaultView ? this : nullptr)(GetDevice(), UpdatedViewDesc, this, std::move(ViewDescriptor), std::move(TexArraySRVDescriptor), std::move(MipUAVDescriptors), bIsDefaultView); VERIFY(pViewD3D12->GetDesc().ViewType == ViewDesc.ViewType, "Incorrect view type"); if (bIsDefaultView) *ppView = pViewD3D12; else pViewD3D12->QueryInterface(IID_TextureView, reinterpret_cast(ppView)); } catch (const std::runtime_error&) { const auto* ViewTypeName = GetTexViewTypeLiteralName(ViewDesc.ViewType); LOG_ERROR("Failed to create view \"", ViewDesc.Name ? ViewDesc.Name : "", "\" (", ViewTypeName, ") for texture \"", m_Desc.Name ? m_Desc.Name : "", "\""); } } TextureD3D12Impl::~TextureD3D12Impl() { // D3D12 object can only be destroyed when it is no longer used by the GPU GetDevice()->SafeReleaseDeviceObject(std::move(m_pd3d12Resource), m_Desc.CommandQueueMask); if (m_StagingFootprints != nullptr) { FREE(GetRawAllocator(), m_StagingFootprints); } } void TextureD3D12Impl::CreateSRV(TextureViewDesc& SRVDesc, D3D12_CPU_DESCRIPTOR_HANDLE SRVHandle) { VERIFY(SRVDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, "Incorrect view type: shader resource is expected"); if (SRVDesc.Format == TEX_FORMAT_UNKNOWN) { SRVDesc.Format = m_Desc.Format; } D3D12_SHADER_RESOURCE_VIEW_DESC D3D12_SRVDesc; TextureViewDesc_to_D3D12_SRV_DESC(SRVDesc, D3D12_SRVDesc, m_Desc.SampleCount); auto* pDeviceD3D12 = static_cast(GetDevice())->GetD3D12Device(); pDeviceD3D12->CreateShaderResourceView(m_pd3d12Resource, &D3D12_SRVDesc, SRVHandle); } void TextureD3D12Impl::CreateRTV(TextureViewDesc& RTVDesc, D3D12_CPU_DESCRIPTOR_HANDLE RTVHandle) { VERIFY(RTVDesc.ViewType == TEXTURE_VIEW_RENDER_TARGET, "Incorrect view type: render target is expected"); if (RTVDesc.Format == TEX_FORMAT_UNKNOWN) { RTVDesc.Format = m_Desc.Format; } D3D12_RENDER_TARGET_VIEW_DESC D3D12_RTVDesc; TextureViewDesc_to_D3D12_RTV_DESC(RTVDesc, D3D12_RTVDesc, m_Desc.SampleCount); auto* pDeviceD3D12 = static_cast(GetDevice())->GetD3D12Device(); pDeviceD3D12->CreateRenderTargetView(m_pd3d12Resource, &D3D12_RTVDesc, RTVHandle); } void TextureD3D12Impl::CreateDSV(TextureViewDesc& DSVDesc, D3D12_CPU_DESCRIPTOR_HANDLE DSVHandle) { VERIFY(DSVDesc.ViewType == TEXTURE_VIEW_DEPTH_STENCIL, "Incorrect view type: depth stencil is expected"); if (DSVDesc.Format == TEX_FORMAT_UNKNOWN) { DSVDesc.Format = m_Desc.Format; } D3D12_DEPTH_STENCIL_VIEW_DESC D3D12_DSVDesc; TextureViewDesc_to_D3D12_DSV_DESC(DSVDesc, D3D12_DSVDesc, m_Desc.SampleCount); auto* pDeviceD3D12 = static_cast(GetDevice())->GetD3D12Device(); pDeviceD3D12->CreateDepthStencilView(m_pd3d12Resource, &D3D12_DSVDesc, DSVHandle); } void TextureD3D12Impl::CreateUAV(TextureViewDesc& UAVDesc, D3D12_CPU_DESCRIPTOR_HANDLE UAVHandle) { VERIFY(UAVDesc.ViewType == TEXTURE_VIEW_UNORDERED_ACCESS, "Incorrect view type: unordered access is expected"); if (UAVDesc.Format == TEX_FORMAT_UNKNOWN) { UAVDesc.Format = m_Desc.Format; } D3D12_UNORDERED_ACCESS_VIEW_DESC D3D12_UAVDesc; TextureViewDesc_to_D3D12_UAV_DESC(UAVDesc, D3D12_UAVDesc); auto* pDeviceD3D12 = static_cast(GetDevice())->GetD3D12Device(); pDeviceD3D12->CreateUnorderedAccessView(m_pd3d12Resource, nullptr, &D3D12_UAVDesc, UAVHandle); } void TextureD3D12Impl::SetD3D12ResourceState(D3D12_RESOURCE_STATES state) { SetState(D3D12ResourceStatesToResourceStateFlags(state)); } D3D12_RESOURCE_STATES TextureD3D12Impl::GetD3D12ResourceState() const { return ResourceStateFlagsToD3D12ResourceStates(GetState()); } } // namespace Diligent