diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2019-04-02 01:53:10 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2019-04-02 01:53:10 +0000 |
| commit | e03a728e5fd120c285e2d7321bb9a77abedb6759 (patch) | |
| tree | 16a3784b80924f9d79453d49e44b0b86b85d0838 /Graphics/GraphicsEngineD3D12 | |
| parent | Added ScreenCapture class (diff) | |
| download | DiligentCore-e03a728e5fd120c285e2d7321bb9a77abedb6759.tar.gz DiligentCore-e03a728e5fd120c285e2d7321bb9a77abedb6759.zip | |
Implemented staging textures in D3D12 back-end
Diffstat (limited to 'Graphics/GraphicsEngineD3D12')
3 files changed, 431 insertions, 212 deletions
diff --git a/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h index 92e3333e..a31ce319 100644 --- a/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h @@ -80,6 +80,8 @@ public: return m_TexArraySRV.GetCpuHandle(); } + D3D12_RESOURCE_DESC GetD3D12TextureDesc()const; + protected: void CreateViewInternal( const struct TextureViewDesc &ViewDesc, ITextureView **ppView, bool bIsDefaultView )override final; //void PrepareD3D12InitData(const TextureData &InitData, Uint32 NumSubresources, std::vector<D3D12_SUBRESOURCE_DATA> &D3D12InitData); diff --git a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp index eba7ac5d..2a54ae49 100644 --- a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp @@ -1116,19 +1116,66 @@ namespace Diligent RESOURCE_STATE_TRANSITION_MODE DstTextureTransitionMode) { auto& CmdCtx = GetCmdContext(); + if (pSrcTexture->GetDesc().Usage == USAGE_STAGING) + { + DEV_CHECK_ERR((pSrcTexture->GetDesc().CPUAccessFlags & CPU_ACCESS_WRITE) != 0, "Source staging texture must be created with CPU_ACCESS_WRITE flag"); + DEV_CHECK_ERR(pSrcTexture->GetState() == RESOURCE_STATE_GENERIC_READ || !pSrcTexture->IsInKnownState(), "Staging texture must always be in RESOURCE_STATE_GENERIC_READ state"); + } TransitionOrVerifyTextureState(CmdCtx, *pSrcTexture, SrcTextureTransitionMode, RESOURCE_STATE_COPY_SOURCE, "Using resource as copy source (DeviceContextD3D12Impl::CopyTextureRegion)"); - TransitionOrVerifyTextureState(CmdCtx, *pDstTexture, DstTextureTransitionMode, RESOURCE_STATE_COPY_DEST, "Using resource as copy destination (DeviceContextD3D12Impl::CopyTextureRegion)"); - D3D12_TEXTURE_COPY_LOCATION DstLocation = {}, SrcLocation = {}; + if (pDstTexture->GetDesc().Usage == USAGE_STAGING) + { + DEV_CHECK_ERR((pDstTexture->GetDesc().CPUAccessFlags & CPU_ACCESS_READ) != 0, "Destination staging texture must be created with CPU_ACCESS_READ flag"); + DEV_CHECK_ERR(pDstTexture->GetState() == RESOURCE_STATE_COPY_DEST || !pDstTexture->IsInKnownState(), "Staging texture must always be in RESOURCE_STATE_COPY_DEST state"); + } + TransitionOrVerifyTextureState(CmdCtx, *pDstTexture, DstTextureTransitionMode, RESOURCE_STATE_COPY_DEST, "Using resource as copy destination (DeviceContextD3D12Impl::CopyTextureRegion)"); - DstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - DstLocation.pResource = pDstTexture->GetD3D12Resource(); - DstLocation.SubresourceIndex = DstSubResIndex; + D3D12_TEXTURE_COPY_LOCATION DstLocation = {}, SrcLocation = {}; - SrcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; SrcLocation.pResource = pSrcTexture->GetD3D12Resource(); - SrcLocation.SubresourceIndex = SrcSubResIndex; - + if (pSrcTexture->GetDesc().Usage == USAGE_STAGING) + { + SrcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + auto d3d12TexDesc = pSrcTexture->GetD3D12TextureDesc(); + auto* pd3d12Device = m_pDevice.RawPtr<RenderDeviceD3D12Impl>()->GetD3D12Device(); + pd3d12Device->GetCopyableFootprints(&d3d12TexDesc, + SrcSubResIndex, + 1, // Num subresources + 0, // The offset, in bytes, to the resource. + &DstLocation.PlacedFootprint, + nullptr, + nullptr, + nullptr + ); + } + else + { + SrcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + SrcLocation.SubresourceIndex = SrcSubResIndex; + } + + DstLocation.pResource = pDstTexture->GetD3D12Resource(); + if (pDstTexture->GetDesc().Usage == USAGE_STAGING) + { + DstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + auto d3d12TexDesc = pDstTexture->GetD3D12TextureDesc(); + auto* pd3d12Device = m_pDevice.RawPtr<RenderDeviceD3D12Impl>()->GetD3D12Device(); + pd3d12Device->GetCopyableFootprints(&d3d12TexDesc, + DstSubResIndex, + 1, // Num subresources + 0, // The offset, in bytes, to the resource. + &DstLocation.PlacedFootprint, + nullptr, + nullptr, + nullptr + ); + } + else + { + DstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + DstLocation.SubresourceIndex = DstSubResIndex; + } + CmdCtx.FlushResourceBarriers(); CmdCtx.GetCommandList()->CopyTextureRegion( &DstLocation, DstX, DstY, DstZ, &SrcLocation, pD3D12SrcBox); ++m_State.NumCommands; @@ -1343,38 +1390,89 @@ namespace Diligent { TDeviceContextBase::MapTextureSubresource(pTexture, MipLevel, ArraySlice, MapType, MapFlags, pMapRegion, MappedData); - if (MapType != MAP_WRITE) + auto& TextureD3D12 = *ValidatedCast<TextureD3D12Impl>(pTexture); + const auto& TexDesc = TextureD3D12.GetDesc(); + auto Subres = D3D12CalcSubresource(MipLevel, ArraySlice, 0, TexDesc.MipLevels, TexDesc.ArraySize); + if (TexDesc.Usage == USAGE_DEFAULT) { - LOG_ERROR("Textures can currently only be mapped for writing in D3D12 backend"); - MappedData = MappedTextureSubresource{}; - return; - } + if (MapType != MAP_WRITE) + { + LOG_ERROR("USAGE_DEFAULT textures can only be mapped for writing"); + MappedData = MappedTextureSubresource{}; + return; + } - if( (MapFlags & (MAP_FLAG_DISCARD | MAP_FLAG_DO_NOT_SYNCHRONIZE)) != 0 ) - LOG_WARNING_MESSAGE_ONCE("Mapping textures with flags MAP_FLAG_DISCARD or MAP_FLAG_DO_NOT_SYNCHRONIZE has no effect in D3D12 backend"); + if( (MapFlags & (MAP_FLAG_DISCARD | MAP_FLAG_DO_NOT_SYNCHRONIZE)) != 0 ) + LOG_WARNING_MESSAGE_ONCE("Mapping textures with flags MAP_FLAG_DISCARD or MAP_FLAG_DO_NOT_SYNCHRONIZE has no effect in D3D12 backend"); - auto& TextureD3D12 = *ValidatedCast<TextureD3D12Impl>(pTexture); - const auto& TexDesc = TextureD3D12.GetDesc(); + Box FullExtentBox; + if (pMapRegion == nullptr) + { + FullExtentBox.MaxX = std::max(TexDesc.Width >> MipLevel, 1u); + FullExtentBox.MaxY = std::max(TexDesc.Height >> MipLevel, 1u); + if (TexDesc.Type == RESOURCE_DIM_TEX_3D) + FullExtentBox.MaxZ = std::max(TexDesc.Depth >> MipLevel, 1u); + pMapRegion = &FullExtentBox; + } - Box FullExtentBox; - if (pMapRegion == nullptr) - { - FullExtentBox.MaxX = std::max(TexDesc.Width >> MipLevel, 1u); - FullExtentBox.MaxY = std::max(TexDesc.Height >> MipLevel, 1u); - if (TexDesc.Type == RESOURCE_DIM_TEX_3D) - FullExtentBox.MaxZ = std::max(TexDesc.Depth >> MipLevel, 1u); - pMapRegion = &FullExtentBox; + auto UploadSpace = AllocateTextureUploadSpace(TexDesc.Format, *pMapRegion); + MappedData.pData = reinterpret_cast<Uint8*>(UploadSpace.Allocation.CPUAddress) + (UploadSpace.AlignedOffset - UploadSpace.Allocation.Offset); + MappedData.Stride = UploadSpace.Stride; + MappedData.DepthStride = UploadSpace.DepthStride; + + auto it = m_MappedTextures.emplace(MappedTextureKey{&TextureD3D12, Subres}, std::move(UploadSpace)); + if(!it.second) + LOG_ERROR_MESSAGE("Mip level ", MipLevel, ", slice ", ArraySlice, " of texture '", TexDesc.Name, "' has already been mapped"); } + else if (TexDesc.Usage == USAGE_STAGING) + { + if( (MapFlags & MAP_FLAG_DO_NOT_SYNCHRONIZE) == 0 ) + { + LOG_WARNING_MESSAGE_ONCE("Mapping staging textures is never synchronized in D3D12 backend. " + "Application must use fences or other synchronization methods to explicitly synchronize " + "access and map texture with MAP_FLAG_DO_NOT_SYNCHRONIZE flag."); + } - auto UploadSpace = AllocateTextureUploadSpace(TexDesc.Format, *pMapRegion); - MappedData.pData = reinterpret_cast<Uint8*>(UploadSpace.Allocation.CPUAddress) + (UploadSpace.AlignedOffset - UploadSpace.Allocation.Offset); - MappedData.Stride = UploadSpace.Stride; - MappedData.DepthStride = UploadSpace.DepthStride; + auto d3d12TexDesc = TextureD3D12.GetD3D12TextureDesc(); + auto* pd3d12Device = m_pDevice.RawPtr<RenderDeviceD3D12Impl>()->GetD3D12Device(); + D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint = {}; + UINT64 TotalBytes = 0; + UINT NumRows = 0; + UINT64 RowStride = 0; + pd3d12Device->GetCopyableFootprints(&d3d12TexDesc, + Subres, + 1, // Num subresources + 0, // The offset, in bytes, to the resource. + &Footprint, + &NumRows, + &RowStride, + &TotalBytes + ); + + // It is valid to specify the CPU won't read any data by passing a range where + // End is less than or equal to Begin. + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nf-d3d12-id3d12resource-map + D3D12_RANGE InvalidateRange = {1,0}; + if (MapType == MAP_READ) + { + // Resources on D3D12_HEAP_TYPE_READBACK heaps do not support persistent map. + InvalidateRange = D3D12_RANGE{Footprint.Offset, Footprint.Offset + TotalBytes}; + } - auto Subres = D3D12CalcSubresource(MipLevel, ArraySlice, 0, TexDesc.MipLevels, TexDesc.ArraySize); - auto it = m_MappedTextures.emplace(MappedTextureKey{&TextureD3D12, Subres}, std::move(UploadSpace)); - if(!it.second) - LOG_ERROR_MESSAGE("Mip level ", MipLevel, ", slice ", ArraySlice, " of texture '", TexDesc.Name, "' has already been mapped"); + // Nested Map() calls are supported and are ref-counted. The first call to Map() allocates + // a CPU virtual address range for the resource. The last call to Unmap deallocates the CPU + // virtual address range. + + // Map() invalidates the CPU cache, when necessary, so that CPU reads to this address + // reflect any modifications made by the GPU. + TextureD3D12.GetD3D12Resource()->Map(0, &InvalidateRange, &MappedData.pData); + MappedData.Stride = static_cast<Uint32>(RowStride); + MappedData.DepthStride = static_cast<Uint32>(RowStride * NumRows); + } + else + { + UNSUPPORTED(GetUsageString(TexDesc.Usage), " textures cannot currently be mapped in D3D12 back-end"); + } } void DeviceContextD3D12Impl::UnmapTextureSubresource(ITexture* pTexture, Uint32 MipLevel, Uint32 ArraySlice) @@ -1384,24 +1482,64 @@ namespace Diligent TextureD3D12Impl& TextureD3D12 = *ValidatedCast<TextureD3D12Impl>(pTexture); const auto& TexDesc = TextureD3D12.GetDesc(); auto Subres = D3D12CalcSubresource(MipLevel, ArraySlice, 0, TexDesc.MipLevels, TexDesc.ArraySize); - auto UploadSpaceIt = m_MappedTextures.find(MappedTextureKey{&TextureD3D12, Subres}); - if(UploadSpaceIt != m_MappedTextures.end()) - { - auto& UploadSpace = UploadSpaceIt->second; - CopyTextureRegion(UploadSpace.Allocation.pBuffer, - UploadSpace.AlignedOffset, - UploadSpace.Stride, - UploadSpace.DepthStride, - static_cast<Uint32>(UploadSpace.Allocation.Size - (UploadSpace.AlignedOffset - UploadSpace.Allocation.Offset)), - TextureD3D12, - Subres, - UploadSpace.Region, - RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - m_MappedTextures.erase(UploadSpaceIt); + if (TexDesc.Usage == USAGE_DEFAULT) + { + auto UploadSpaceIt = m_MappedTextures.find(MappedTextureKey{&TextureD3D12, Subres}); + if(UploadSpaceIt != m_MappedTextures.end()) + { + auto& UploadSpace = UploadSpaceIt->second; + CopyTextureRegion(UploadSpace.Allocation.pBuffer, + UploadSpace.AlignedOffset, + UploadSpace.Stride, + UploadSpace.DepthStride, + static_cast<Uint32>(UploadSpace.Allocation.Size - (UploadSpace.AlignedOffset - UploadSpace.Allocation.Offset)), + TextureD3D12, + Subres, + UploadSpace.Region, + RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + m_MappedTextures.erase(UploadSpaceIt); + } + else + { + LOG_ERROR_MESSAGE("Failed to unmap mip level ", MipLevel, ", slice ", ArraySlice, " of texture '", TexDesc.Name, "'. The texture has either been unmapped already or has not been mapped"); + } + } + else if (TexDesc.Usage == USAGE_STAGING) + { + // It is valid to specify the CPU didn't write any data by passing a range where + // End is less than or equal to Begin. + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nf-d3d12-id3d12resource-unmap + D3D12_RANGE FlushRange = {1, 0}; + + if (TexDesc.CPUAccessFlags == CPU_ACCESS_WRITE) + { + auto d3d12TexDesc = TextureD3D12.GetD3D12TextureDesc(); + auto* pd3d12Device = m_pDevice.RawPtr<RenderDeviceD3D12Impl>()->GetD3D12Device(); + D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint = {}; + UINT64 TotalBytes = 0; + pd3d12Device->GetCopyableFootprints(&d3d12TexDesc, + Subres, + 1, // Num subresources + 0, // The offset, in bytes, to the resource. + &Footprint, + nullptr, + nullptr, + &TotalBytes + ); + FlushRange = D3D12_RANGE{Footprint.Offset, Footprint.Offset + TotalBytes}; + } + + // Map and Unmap can be called by multiple threads safely. Nested Map calls are supported + // and are ref-counted. The first call to Map allocates a CPU virtual address range for the + // resource. The last call to Unmap deallocates the CPU virtual address range. + + // Unmap() flushes the CPU cache, when necessary, so that GPU reads to this address reflect + // any modifications made by the CPU. + TextureD3D12.GetD3D12Resource()->Unmap(0, &FlushRange); } else { - LOG_ERROR_MESSAGE("Failed to unmap mip level ", MipLevel, ", slice ", ArraySlice, " of texture '", TexDesc.Name, "'. The texture has either been unmapped already or has not been mapped"); + UNSUPPORTED(GetUsageString(TexDesc.Usage), " textures cannot currently be mapped in D3D12 back-end"); } } diff --git a/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp index 84fccc67..0310ee06 100644 --- a/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp @@ -71,49 +71,37 @@ DXGI_FORMAT GetClearFormat(DXGI_FORMAT Fmt, D3D12_RESOURCE_FLAGS Flags) return Fmt; } -TextureD3D12Impl :: TextureD3D12Impl(IReferenceCounters* pRefCounters, - FixedBlockMemoryAllocator& TexViewObjAllocator, - RenderDeviceD3D12Impl* pRenderDeviceD3D12, - const TextureDesc& TexDesc, - const TextureData* pInitData /*= nullptr*/) : - TTextureBase(pRefCounters, TexViewObjAllocator, pRenderDeviceD3D12, TexDesc) +D3D12_RESOURCE_DESC TextureD3D12Impl :: GetD3D12TextureDesc()const { - if (m_Desc.Usage == USAGE_STATIC && (pInitData == nullptr || pInitData->pSubResources == nullptr)) - LOG_ERROR_AND_THROW("Static textures must be initialized with data at creation time: pInitData can't be null"); + D3D12_RESOURCE_DESC Desc = {}; - const auto& FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); - if ((m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) != 0 && FmtAttribs.IsTypeless) - LOG_ERROR_AND_THROW("Textures created with MISC_TEXTURE_FLAG_GENERATE_MIPS flag can't use typeless formats. The following format was provided: ", FmtAttribs.Name, " when attempting to create texture '", m_Desc.Name, "'"); - - 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) + 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 ) + 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 ) + 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) + 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 ) + 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 ) + if (m_Desc.BindFlags & BIND_RENDER_TARGET) Desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; - if( m_Desc.BindFlags & BIND_DEPTH_STENCIL ) + 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) ) + 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 ) + 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); @@ -121,168 +109,259 @@ TextureD3D12Impl :: TextureD3D12Impl(IReferenceCounters* pRefCounters, 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<Uint16>(m_Desc.MipLevels); - Desc.SampleDesc.Count = m_Desc.SampleCount; + + Desc.Height = UINT{m_Desc.Height}; + Desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + Desc.MipLevels = static_cast<UINT16>(m_Desc.MipLevels); + Desc.SampleDesc.Count = m_Desc.SampleCount; Desc.SampleDesc.Quality = 0; - Desc.Width = (UINT64)m_Desc.Width; + Desc.Width = UINT64{m_Desc.Width}; + return Desc; +} - 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; +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_STATIC && (pInitData == nullptr || pInitData->pSubResources == nullptr)) + LOG_ERROR_AND_THROW("Static textures must be initialized with data at creation time: pInitData can't be null"); - auto *pd3d12Device = pRenderDeviceD3D12->GetD3D12Device(); - 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 - ClearValue.Format = GetClearFormat(Format, Desc.Flags); + const auto& FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); + if ((m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) != 0 && FmtAttribs.IsTypeless) + LOG_ERROR_AND_THROW("Textures created with MISC_TEXTURE_FLAG_GENERATE_MIPS flag can't use typeless formats. The following format was provided: ", FmtAttribs.Name, " when attempting to create texture '", m_Desc.Name, "'"); + + D3D12_RESOURCE_DESC Desc = GetD3D12TextureDesc(); - if (Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) + auto* pd3d12Device = pRenderDeviceD3D12->GetD3D12Device(); + if (m_Desc.Usage == USAGE_STATIC || m_Desc.Usage == USAGE_DEFAULT) + { + D3D12_CLEAR_VALUE ClearValue; + D3D12_CLEAR_VALUE* pClearValue = nullptr; + if (Desc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { - for(int i=0; i < 4; ++i) - ClearValue.Color[i] = m_Desc.ClearValue.Color[i]; + 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; } - else if(Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) + + 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; + + bool bInitializeTexture = (pInitData != nullptr && pInitData->pSubResources != nullptr && pInitData->NumSubresources > 0); + 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<void**>(static_cast<ID3D12Resource**>(&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) { - ClearValue.DepthStencil.Depth = m_Desc.ClearValue.DepthStencil.Depth; - ClearValue.DepthStencil.Stencil = m_Desc.ClearValue.DepthStencil.Stencil; + 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<ID3D12Resource> UploadBuffer; + hr = pd3d12Device->CreateCommittedResource( &UploadHeapProps, D3D12_HEAP_FLAG_NONE, + &BufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, __uuidof(UploadBuffer), reinterpret_cast<void**>(static_cast<ID3D12Resource**>(&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<D3D12_SUBRESOURCE_DATA, STDAllocatorRawMem<D3D12_SUBRESOURCE_DATA> > D3D12SubResData(pInitData->NumSubresources, D3D12_SUBRESOURCE_DATA(), STD_ALLOCATOR_RAW_MEM(D3D12_SUBRESOURCE_DATA, GetRawAllocator(), "Allocator for vector<D3D12_SUBRESOURCE_DATA>") ); + 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); } - pClearValue = &ClearValue; - } - bool bInitializeTexture = (pInitData != nullptr && pInitData->pSubResources != nullptr && pInitData->NumSubresources > 0); - 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<void**>(static_cast<ID3D12Resource**>(&m_pd3d12Resource)) ); - if(FAILED(hr)) - LOG_ERROR_AND_THROW("Failed to create D3D12 texture"); + if(m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) + { + if (m_Desc.Type != RESOURCE_DIM_TEX_2D && m_Desc.Type != RESOURCE_DIM_TEX_2D_ARRAY) + { + LOG_ERROR_AND_THROW("Mipmap generation is only supported for 2D textures and texture arrays"); + } - if( *m_Desc.Name != 0) - m_pd3d12Resource->SetName(WidenString(m_Desc.Name).c_str()); + m_MipUAVs = pRenderDeviceD3D12->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_Desc.MipLevels); + for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel) + { + TextureViewDesc UAVDesc; + // Always create texture array UAV + UAVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; + UAVDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS; + UAVDesc.FirstArraySlice = 0; + UAVDesc.NumArraySlices = m_Desc.ArraySize; + UAVDesc.MostDetailedMip = MipLevel; + if (m_Desc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB) + UAVDesc.Format = TEX_FORMAT_RGBA8_UNORM; + CreateUAV( UAVDesc, m_MipUAVs.GetCpuHandle(MipLevel) ); + } - if(bInitializeTexture) + { + m_TexArraySRV = pRenderDeviceD3D12->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1); + TextureViewDesc TexArraySRVDesc; + // Create texture array SRV + TexArraySRVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; + TexArraySRVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; + TexArraySRVDesc.FirstArraySlice = 0; + TexArraySRVDesc.NumArraySlices = m_Desc.ArraySize; + TexArraySRVDesc.MostDetailedMip = 0; + TexArraySRVDesc.NumMipLevels = m_Desc.MipLevels; + CreateSRV( TexArraySRVDesc, m_TexArraySRV.GetCpuHandle() ); + } + } + } + else if (m_Desc.Usage == USAGE_STAGING) { - Uint32 ExpectedNumSubresources = static_cast<Uint32>(Desc.MipLevels * (Desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? 1 : Desc.DepthOrArraySize) ); - if( pInitData->NumSubresources != ExpectedNumSubresources ) - LOG_ERROR_AND_THROW("Incorrect number of subresources in init data. ", ExpectedNumSubresources, " expected, while ", pInitData->NumSubresources, " provided"); + // 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) + { + 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); - UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_pd3d12Resource, 0, pInitData->NumSubresources); + StaginHeapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + StaginHeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + StaginHeapProps.CreationNodeMask = 1; + StaginHeapProps.VisibleNodeMask = 1; - 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; + UINT64 stagingBufferSize = 0; + Uint32 NumSubresources = Uint32{Desc.MipLevels} * (Desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? 1 : Uint32{Desc.DepthOrArraySize}); + pd3d12Device->GetCopyableFootprints(&Desc, 0, NumSubresources, 0, nullptr, nullptr, nullptr, &stagingBufferSize); 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.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; - - CComPtr<ID3D12Resource> UploadBuffer; - hr = pd3d12Device->CreateCommittedResource( &UploadHeapProps, D3D12_HEAP_FLAG_NONE, - &BufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, - nullptr, __uuidof(UploadBuffer), reinterpret_cast<void**>(static_cast<ID3D12Resource**>(&UploadBuffer))); + 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<void**>(static_cast<ID3D12Resource**>(&m_pd3d12Resource))); 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<D3D12_SUBRESOURCE_DATA, STDAllocatorRawMem<D3D12_SUBRESOURCE_DATA> > D3D12SubResData(pInitData->NumSubresources, D3D12_SUBRESOURCE_DATA(), STD_ALLOCATOR_RAW_MEM(D3D12_SUBRESOURCE_DATA, GetRawAllocator(), "Allocator for vector<D3D12_SUBRESOURCE_DATA>") ); - 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); + LOG_ERROR_AND_THROW("Failed to create staging buffer"); } - - if(m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) + else if (m_Desc.Usage == USAGE_DYNAMIC) { - if (m_Desc.Type != RESOURCE_DIM_TEX_2D && m_Desc.Type != RESOURCE_DIM_TEX_2D_ARRAY) - { - LOG_ERROR_AND_THROW("Mipmap generation is only supported for 2D textures and texture arrays"); - } - - m_MipUAVs = pRenderDeviceD3D12->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_Desc.MipLevels); - for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel) - { - TextureViewDesc UAVDesc; - // Always create texture array UAV - UAVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; - UAVDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS; - UAVDesc.FirstArraySlice = 0; - UAVDesc.NumArraySlices = m_Desc.ArraySize; - UAVDesc.MostDetailedMip = MipLevel; - if (m_Desc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB) - UAVDesc.Format = TEX_FORMAT_RGBA8_UNORM; - CreateUAV( UAVDesc, m_MipUAVs.GetCpuHandle(MipLevel) ); - } - - { - m_TexArraySRV = pRenderDeviceD3D12->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1); - TextureViewDesc TexArraySRVDesc; - // Create texture array SRV - TexArraySRVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; - TexArraySRVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; - TexArraySRVDesc.FirstArraySlice = 0; - TexArraySRVDesc.NumArraySlices = m_Desc.ArraySize; - TexArraySRVDesc.MostDetailedMip = 0; - TexArraySRVDesc.NumMipLevels = m_Desc.MipLevels; - CreateSRV( TexArraySRVDesc, m_TexArraySRV.GetCpuHandle() ); - } + LOG_ERROR_AND_THROW("Dynamic textures are not currently implemented in D3D12 back-end"); + } + else + { + UNEXPECTED("Unexpected usage"); } } |
