From 56c2453b20c77282a8f4789603ad954e113185c6 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sat, 19 Aug 2023 20:38:29 -0700 Subject: [PATCH 01/23] Updated RenderDeviceX * made all members const * RenderDeviceX_E and RenderDeviceX_N typedefs --- .../interface/GraphicsTypesX.hpp | 79 +++++++++++-------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/Graphics/GraphicsEngine/interface/GraphicsTypesX.hpp b/Graphics/GraphicsEngine/interface/GraphicsTypesX.hpp index 1f61696c3..9df0b75a1 100644 --- a/Graphics/GraphicsEngine/interface/GraphicsTypesX.hpp +++ b/Graphics/GraphicsEngine/interface/GraphicsTypesX.hpp @@ -1854,7 +1854,13 @@ class RenderDeviceX public: RenderDeviceX() noexcept {} - explicit RenderDeviceX(IRenderDevice* pDevice) noexcept : + RenderDeviceX(IRenderDevice* pDevice) noexcept : + m_pDevice{pDevice} + { + DEV_CHECK_ERR(pDevice, "Device must not be null"); + } + + RenderDeviceX(const RefCntAutoPtr& pDevice) noexcept : m_pDevice{pDevice} { DEV_CHECK_ERR(pDevice, "Device must not be null"); @@ -1868,7 +1874,7 @@ class RenderDeviceX // clang-format on RefCntAutoPtr CreateBuffer(const BufferDesc& BuffDesc, - const BufferData* pBuffData = nullptr) noexcept(!ThrowOnError) + const BufferData* pBuffData = nullptr) const noexcept(!ThrowOnError) { return CreateDeviceObject("buffer", BuffDesc.Name, &IRenderDevice::CreateBuffer, BuffDesc, pBuffData); } @@ -1878,7 +1884,7 @@ class RenderDeviceX USAGE Usage = USAGE_DYNAMIC, BIND_FLAGS BindFlags = BIND_UNIFORM_BUFFER, CPU_ACCESS_FLAGS CPUAccessFlags = CPU_ACCESS_NONE, - const void* pData = nullptr) noexcept(!ThrowOnError) + const void* pData = nullptr) const noexcept(!ThrowOnError) { BufferDesc Desc; Desc.Name = Name; @@ -1901,12 +1907,12 @@ class RenderDeviceX } RefCntAutoPtr CreateTexture(const TextureDesc& TexDesc, - const TextureData* pData = nullptr) noexcept(!ThrowOnError) + const TextureData* pData = nullptr) const noexcept(!ThrowOnError) { return CreateDeviceObject("texture", TexDesc.Name, &IRenderDevice::CreateTexture, TexDesc, pData); } - RefCntAutoPtr CreateShader(const ShaderCreateInfo& ShaderCI) noexcept(!ThrowOnError) + RefCntAutoPtr CreateShader(const ShaderCreateInfo& ShaderCI) const noexcept(!ThrowOnError) { RefCntAutoPtr pShader; m_pDevice->CreateShader(ShaderCI, &pShader, nullptr); @@ -1916,18 +1922,18 @@ class RenderDeviceX } template - RefCntAutoPtr CreateShader(ArgsType&&... args) noexcept(!ThrowOnError) + RefCntAutoPtr CreateShader(ArgsType&&... args) const noexcept(!ThrowOnError) { const ShaderCreateInfo ShaderCI{std::forward(args)...}; return CreateShader(ShaderCI); } - RefCntAutoPtr CreateSampler(const SamplerDesc& SamDesc) noexcept(!ThrowOnError) + RefCntAutoPtr CreateSampler(const SamplerDesc& SamDesc) const noexcept(!ThrowOnError) { return CreateDeviceObject("sampler", SamDesc.Name, &IRenderDevice::CreateSampler, SamDesc); } - RefCntAutoPtr CreateResourceMapping(const ResourceMappingCreateInfo& ResMappingCI) noexcept(!ThrowOnError) + RefCntAutoPtr CreateResourceMapping(const ResourceMappingCreateInfo& ResMappingCI) const noexcept(!ThrowOnError) { RefCntAutoPtr pResMapping; m_pDevice->CreateResourceMapping(ResMappingCI, &pResMapping); @@ -1936,89 +1942,89 @@ class RenderDeviceX return pResMapping; } - RefCntAutoPtr CreateGraphicsPipelineState(const GraphicsPipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateGraphicsPipelineState(const GraphicsPipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateDeviceObject("graphics pipeline", CreateInfo.PSODesc.Name, &IRenderDevice::CreateGraphicsPipelineState, CreateInfo); } - RefCntAutoPtr CreateComputePipelineState(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateComputePipelineState(const ComputePipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateDeviceObject("compute pipeline", CreateInfo.PSODesc.Name, &IRenderDevice::CreateComputePipelineState, CreateInfo); } - RefCntAutoPtr CreateRayTracingPipelineState(const RayTracingPipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateRayTracingPipelineState(const RayTracingPipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateDeviceObject("ray-tracing pipeline", CreateInfo.PSODesc.Name, &IRenderDevice::CreateRayTracingPipelineState, CreateInfo); } - RefCntAutoPtr CreateTilePipelineState(const TilePipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateTilePipelineState(const TilePipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateDeviceObject("tile pipeline", CreateInfo.PSODesc.Name, &IRenderDevice::CreateTilePipelineState, CreateInfo); } - RefCntAutoPtr CreatePipelineState(const GraphicsPipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineState(const GraphicsPipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateGraphicsPipelineState(CreateInfo); } - RefCntAutoPtr CreatePipelineState(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineState(const ComputePipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateComputePipelineState(CreateInfo); } - RefCntAutoPtr CreatePipelineState(const RayTracingPipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineState(const RayTracingPipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateRayTracingPipelineState(CreateInfo); } - RefCntAutoPtr CreatePipelineState(const TilePipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineState(const TilePipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateTilePipelineState(CreateInfo); } - RefCntAutoPtr CreateFence(const FenceDesc& Desc) noexcept(!ThrowOnError) + RefCntAutoPtr CreateFence(const FenceDesc& Desc) const noexcept(!ThrowOnError) { return CreateDeviceObject("fence", Desc.Name, &IRenderDevice::CreateFence, Desc); } - RefCntAutoPtr CreateQuery(const QueryDesc& Desc) noexcept(!ThrowOnError) + RefCntAutoPtr CreateQuery(const QueryDesc& Desc) const noexcept(!ThrowOnError) { return CreateDeviceObject("query", Desc.Name, &IRenderDevice::CreateQuery, Desc); } - RefCntAutoPtr CreateRenderPass(const RenderPassDesc& Desc) noexcept(!ThrowOnError) + RefCntAutoPtr CreateRenderPass(const RenderPassDesc& Desc) const noexcept(!ThrowOnError) { return CreateDeviceObject("render pass", Desc.Name, &IRenderDevice::CreateRenderPass, Desc); } - RefCntAutoPtr CreateFramebuffer(const FramebufferDesc& Desc) noexcept(!ThrowOnError) + RefCntAutoPtr CreateFramebuffer(const FramebufferDesc& Desc) const noexcept(!ThrowOnError) { return CreateDeviceObject("framebuffer", Desc.Name, &IRenderDevice::CreateFramebuffer, Desc); } - RefCntAutoPtr CreateBLAS(const BottomLevelASDesc& Desc) noexcept(!ThrowOnError) + RefCntAutoPtr CreateBLAS(const BottomLevelASDesc& Desc) const noexcept(!ThrowOnError) { return CreateDeviceObject("bottom-level AS", Desc.Name, &IRenderDevice::CreateBLAS, Desc); } - RefCntAutoPtr CreateTLAS(const TopLevelASDesc& Desc) noexcept(!ThrowOnError) + RefCntAutoPtr CreateTLAS(const TopLevelASDesc& Desc) const noexcept(!ThrowOnError) { return CreateDeviceObject("top-level AS", Desc.Name, &IRenderDevice::CreateTLAS, Desc); } - RefCntAutoPtr CreateSBT(const ShaderBindingTableDesc& Desc) noexcept(!ThrowOnError) + RefCntAutoPtr CreateSBT(const ShaderBindingTableDesc& Desc) const noexcept(!ThrowOnError) { return CreateDeviceObject("shader binding table", Desc.Name, &IRenderDevice::CreateSBT, Desc); } - RefCntAutoPtr CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc) const noexcept(!ThrowOnError) { return CreateDeviceObject("pipeline resource signature", Desc.Name, &IRenderDevice::CreatePipelineResourceSignature, Desc); } - RefCntAutoPtr CreateDeviceMemory(const DeviceMemoryCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateDeviceMemory(const DeviceMemoryCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateDeviceObject("device memory", CreateInfo.Desc.Name, &IRenderDevice::CreateDeviceMemory, CreateInfo); } - RefCntAutoPtr CreatePipelineStateCache(const PipelineStateCacheCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineStateCache(const PipelineStateCacheCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateDeviceObject("PSO cache", CreateInfo.Desc.Name, &IRenderDevice::CreatePipelineStateCache, CreateInfo); } @@ -2033,29 +2039,29 @@ class RenderDeviceX return m_pDevice->GetAdapterInfo(); } - const TextureFormatInfo& GetTextureFormatInfo(TEXTURE_FORMAT TexFormat) noexcept + const TextureFormatInfo& GetTextureFormatInfo(TEXTURE_FORMAT TexFormat) const noexcept { return m_pDevice->GetTextureFormatInfo(TexFormat); } - const TextureFormatInfoExt& GetTextureFormatInfoExt(TEXTURE_FORMAT TexFormat) noexcept + const TextureFormatInfoExt& GetTextureFormatInfoExt(TEXTURE_FORMAT TexFormat) const noexcept { return m_pDevice->GetTextureFormatInfoExt(TexFormat); } SparseTextureFormatInfo GetSparseTextureFormatInfo(TEXTURE_FORMAT TexFormat, RESOURCE_DIMENSION Dimension, - Uint32 SampleCount) noexcept + Uint32 SampleCount) const noexcept { return m_pDevice->GetSparseTextureFormatInfo(TexFormat, Dimension, SampleCount); } - void ReleaseStaleResources(bool ForceRelease = false) noexcept + void ReleaseStaleResources(bool ForceRelease = false) const noexcept { return m_pDevice->ReleaseStaleResources(ForceRelease); } - void IdleGPU() noexcept + void IdleGPU() const noexcept { return m_pDevice->IdleGPU(); } @@ -2082,7 +2088,10 @@ class RenderDeviceX private: template - RefCntAutoPtr CreateDeviceObject(const char* ObjectTypeName, const char* ObjectName, CreateMethodType Create, CreateArgsType&&... Args) noexcept(!ThrowOnError) + RefCntAutoPtr CreateDeviceObject(const char* ObjectTypeName, + const char* ObjectName, + CreateMethodType Create, + CreateArgsType&&... Args) const noexcept(!ThrowOnError) { RefCntAutoPtr pObject; (m_pDevice->*Create)(std::forward(Args)..., &pObject); @@ -2098,4 +2107,10 @@ class RenderDeviceX RefCntAutoPtr m_pDevice; }; +// Throws an Exception if object creation failed +using RenderDeviceX_E = RenderDeviceX; + +// Returns Null pointer if object creation failed +using RenderDeviceX_N = RenderDeviceX; + } // namespace Diligent From f1dfe9e43b20ecade9ca1c06b52d7e4c8d961e06 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sat, 19 Aug 2023 20:53:23 -0700 Subject: [PATCH 02/23] Updated RenderDeviceWithCache class --- .../interface/RenderStateCache.hpp | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/Graphics/GraphicsTools/interface/RenderStateCache.hpp b/Graphics/GraphicsTools/interface/RenderStateCache.hpp index 37f53d783..4d0f6cd69 100644 --- a/Graphics/GraphicsTools/interface/RenderStateCache.hpp +++ b/Graphics/GraphicsTools/interface/RenderStateCache.hpp @@ -46,8 +46,8 @@ class RenderDeviceWithCache : public RenderDeviceX RenderDeviceWithCache() noexcept {} - explicit RenderDeviceWithCache(IRenderDevice* pDevice, - IRenderStateCache* pCache = nullptr) noexcept : + RenderDeviceWithCache(IRenderDevice* pDevice, + IRenderStateCache* pCache = nullptr) noexcept : TBase{pDevice}, m_pCache{pCache} { @@ -89,54 +89,54 @@ class RenderDeviceWithCache : public RenderDeviceX } } - RefCntAutoPtr CreateShader(const ShaderCreateInfo& ShaderCI) noexcept(!ThrowOnError) + RefCntAutoPtr CreateShader(const ShaderCreateInfo& ShaderCI) const noexcept(!ThrowOnError) { return m_pCache ? UnpackCachedObject("shader", ShaderCI.Desc.Name, &IRenderStateCache::CreateShader, ShaderCI) : TBase::CreateShader(ShaderCI); } - RefCntAutoPtr CreateGraphicsPipelineState(const GraphicsPipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateGraphicsPipelineState(const GraphicsPipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return m_pCache ? UnpackCachedObject("graphics pipeline", CreateInfo.PSODesc.Name, &IRenderStateCache::CreateGraphicsPipelineState, CreateInfo) : TBase::CreateGraphicsPipelineState(CreateInfo); } - RefCntAutoPtr CreateComputePipelineState(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateComputePipelineState(const ComputePipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return m_pCache ? UnpackCachedObject("compute pipeline", CreateInfo.PSODesc.Name, &IRenderStateCache::CreateComputePipelineState, CreateInfo) : TBase::CreateComputePipelineState(CreateInfo); } - RefCntAutoPtr CreateRayTracingPipelineState(const RayTracingPipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateRayTracingPipelineState(const RayTracingPipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return m_pCache ? UnpackCachedObject("ray-tracing pipeline", CreateInfo.PSODesc.Name, &IRenderStateCache::CreateRayTracingPipelineState, CreateInfo) : TBase::CreateRayTracingPipelineState(CreateInfo); } - RefCntAutoPtr CreateTilePipelineState(const TilePipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreateTilePipelineState(const TilePipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return m_pCache ? UnpackCachedObject("tile pipeline", CreateInfo.PSODesc.Name, &IRenderStateCache::CreateTilePipelineState, CreateInfo) : TBase::CreateTilePipelineState(CreateInfo); } - RefCntAutoPtr CreatePipelineState(const GraphicsPipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineState(const GraphicsPipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateGraphicsPipelineState(CreateInfo); } - RefCntAutoPtr CreatePipelineState(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineState(const ComputePipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateComputePipelineState(CreateInfo); } - RefCntAutoPtr CreatePipelineState(const RayTracingPipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineState(const RayTracingPipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateRayTracingPipelineState(CreateInfo); } - RefCntAutoPtr CreatePipelineState(const TilePipelineStateCreateInfo& CreateInfo) noexcept(!ThrowOnError) + RefCntAutoPtr CreatePipelineState(const TilePipelineStateCreateInfo& CreateInfo) const noexcept(!ThrowOnError) { return CreateTilePipelineState(CreateInfo); } @@ -224,7 +224,10 @@ class RenderDeviceWithCache : public RenderDeviceX private: template - RefCntAutoPtr UnpackCachedObject(const char* ObjectTypeName, const char* ObjectName, CreateMethodType Create, CreateArgsType&&... Args) noexcept(!ThrowOnError) + RefCntAutoPtr UnpackCachedObject(const char* ObjectTypeName, + const char* ObjectName, + CreateMethodType Create, + CreateArgsType&&... Args) const noexcept(!ThrowOnError) { RefCntAutoPtr pObject; (m_pCache->*Create)(std::forward(Args)..., &pObject); @@ -242,6 +245,13 @@ class RenderDeviceWithCache : public RenderDeviceX Uint32 m_CacheContentVersion = 0; }; +// Throws an Exception if object creation failed +using RenderDeviceWithCache_E = RenderDeviceWithCache; + +// Returns Null pointer if object creation failed +using RenderDeviceWithCache_N = RenderDeviceWithCache; + + /// Special string to indicate that the render state cache file should be stored in the application data folder. static constexpr char RenderStateCacheLocationAppData[] = ""; From 518062d47a74e858dacf18b5db67ba1cc5c039b6 Mon Sep 17 00:00:00 2001 From: assiduous Date: Mon, 21 Aug 2023 18:18:39 -0700 Subject: [PATCH 03/23] D3D11 and D3D12 backends: allow depth texture to be in DEPTH_READ state for shader input --- .../src/ShaderResourceCacheD3D11.cpp | 21 +++++++++++++------ .../src/ShaderResourceCacheD3D12.cpp | 12 +++++++++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp index f62222eb2..3d9609b0f 100755 --- a/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -209,7 +209,7 @@ void ShaderResourceCacheD3D11::TransitionResources(DeviceContextD3D11Impl& Ctx, else { LOG_ERROR_MESSAGE("Buffer '", pBuffer->GetDesc().Name, - "' has not been transitioned to Constant Buffer state. Call TransitionShaderResources(), use " + "' has not been transitioned to CONSTANT_BUFFER state. Call TransitionShaderResources(), use " "RESOURCE_STATE_TRANSITION_MODE_TRANSITION mode or explicitly transition the buffer to required state."); } } @@ -233,7 +233,15 @@ void ShaderResourceCacheD3D11::TransitionResources(DeviceContextD3D11Impl& Ctx, auto& SRVRes = SRVArrays.first[i]; if (auto* pTexture = SRVRes.pTexture) { - if (pTexture->IsInKnownState() && !pTexture->CheckAnyState(RESOURCE_STATE_SHADER_RESOURCE | RESOURCE_STATE_INPUT_ATTACHMENT)) + auto RequiredStates = RESOURCE_STATE_SHADER_RESOURCE | RESOURCE_STATE_INPUT_ATTACHMENT; + + const auto& TexDesc = pTexture->GetDesc(); + const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); + if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH || FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL) + { + RequiredStates |= RESOURCE_STATE_DEPTH_READ; + } + if (pTexture->IsInKnownState() && !pTexture->CheckAnyState(RequiredStates)) { if (Mode == StateTransitionMode::Transition) { @@ -242,8 +250,9 @@ void ShaderResourceCacheD3D11::TransitionResources(DeviceContextD3D11Impl& Ctx, else { LOG_ERROR_MESSAGE("Texture '", pTexture->GetDesc().Name, - "' has not been transitioned to Shader Resource state. Call TransitionShaderResources(), use " - "RESOURCE_STATE_TRANSITION_MODE_TRANSITION mode or explicitly transition the texture to required state."); + "' has not been transitioned to one of ", GetResourceStateString(RequiredStates), + ", states. Call TransitionShaderResources(), use RESOURCE_STATE_TRANSITION_MODE_TRANSITION mode " + "or explicitly transition the texture to required state."); } } } @@ -258,7 +267,7 @@ void ShaderResourceCacheD3D11::TransitionResources(DeviceContextD3D11Impl& Ctx, else { LOG_ERROR_MESSAGE("Buffer '", pBuffer->GetDesc().Name, - "' has not been transitioned to Shader Resource state. Call TransitionShaderResources(), use " + "' has not been transitioned to SHADER_RESOURCE state. Call TransitionShaderResources(), use " "RESOURCE_STATE_TRANSITION_MODE_TRANSITION mode or explicitly transition the buffer to required state."); } } diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index 1c9421de5..c7c6313af 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -621,9 +621,17 @@ void ShaderResourceCacheD3D12::Resource::DvpVerifyResourceState() { const auto* pTexViewD3D12 = pObject.ConstPtr(); const auto* pTexD3D12 = pTexViewD3D12->GetTexture(); - if (pTexD3D12->IsInKnownState() && !pTexD3D12->CheckAnyState(RESOURCE_STATE_SHADER_RESOURCE | RESOURCE_STATE_INPUT_ATTACHMENT)) + const auto& TexDesc = pTexD3D12->GetDesc(); + const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); + + auto RequiredStates = RESOURCE_STATE_SHADER_RESOURCE | RESOURCE_STATE_INPUT_ATTACHMENT; + if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH || FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL) + { + RequiredStates |= RESOURCE_STATE_DEPTH_READ; + } + if (pTexD3D12->IsInKnownState() && !pTexD3D12->CheckAnyState(RequiredStates)) { - LOG_ERROR_MESSAGE("Texture '", pTexD3D12->GetDesc().Name, "' must be in RESOURCE_STATE_SHADER_RESOURCE state. Actual state: ", + LOG_ERROR_MESSAGE("Texture '", pTexD3D12->GetDesc().Name, "' must be in one of ", GetResourceStateString(RequiredStates), " states. Actual state: ", GetResourceStateString(pTexD3D12->GetState()), ". Call IDeviceContext::TransitionShaderResources(), use RESOURCE_STATE_TRANSITION_MODE_TRANSITION " "when calling IDeviceContext::CommitShaderResources() or explicitly transition the texture state " From ab7f6e945ee2c36d7102a1737b072a24144b638c Mon Sep 17 00:00:00 2001 From: assiduous Date: Mon, 21 Aug 2023 21:34:09 -0700 Subject: [PATCH 04/23] Vertex pool, dynamic texture atlas and dynamic buffer: added methods to return internal buffer or texture without resize --- .../GraphicsTools/interface/DynamicBuffer.hpp | 16 +++++++++++++--- .../interface/DynamicTextureArray.hpp | 18 ++++++++++++++---- .../interface/DynamicTextureAtlas.h | 16 ++++++++++++---- Graphics/GraphicsTools/interface/VertexPool.h | 13 +++++++++++++ .../GraphicsTools/src/DynamicTextureAtlas.cpp | 5 +++++ Graphics/GraphicsTools/src/VertexPool.cpp | 18 ++++++++++++++++++ 6 files changed, 75 insertions(+), 11 deletions(-) diff --git a/Graphics/GraphicsTools/interface/DynamicBuffer.hpp b/Graphics/GraphicsTools/interface/DynamicBuffer.hpp index c0a98bc20..09f14f5a2 100644 --- a/Graphics/GraphicsTools/interface/DynamicBuffer.hpp +++ b/Graphics/GraphicsTools/interface/DynamicBuffer.hpp @@ -122,14 +122,14 @@ class DynamicBuffer bool DiscardContent = false); - /// Returns the pointer to the buffer object, initializing it if necessary. + /// Returns a pointer to the buffer object, initializing it if necessary. - /// \param[in] pDevice - Render device that will be used to create the new buffer, + /// \param[in] pDevice - Render device that will be used to create a new buffer, /// if necessary (see remarks). /// \param[in] pContext - Device context that will be used to copy existing /// buffer contents or commit memory pages, if necessary /// (see remarks). - /// \return The pointer to the buffer object. + /// \return A pointer to the buffer object. /// /// \remarks If the buffer has been resized, but internal buffer object has not been /// initialized, pDevice and pContext must not be null. @@ -140,6 +140,16 @@ class DynamicBuffer IDeviceContext* pContext); + /// Returns a pointer to the buffer object. + /// + /// \remarks If the buffer has not been initialized, the method returns null. + /// If the buffer may need to be updated (resized or initialized), use the overload + /// that takes pDevice and pContext parameters. + IBuffer* GetBuffer() const + { + return m_pBuffer; + } + /// Returns true if the buffer must be updated before use (e.g. it has been resized, /// but internal buffer has not been initialized or updated). /// When update is not pending, GetBuffer() may be called with null device and context. diff --git a/Graphics/GraphicsTools/interface/DynamicTextureArray.hpp b/Graphics/GraphicsTools/interface/DynamicTextureArray.hpp index dd649f3af..64c00c95e 100644 --- a/Graphics/GraphicsTools/interface/DynamicTextureArray.hpp +++ b/Graphics/GraphicsTools/interface/DynamicTextureArray.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -116,15 +116,15 @@ class DynamicTextureArray bool DiscardContent = false); - /// Returns the pointer to the texture object, initializing it if necessary. + /// Returns a pointer to the texture object, initializing it if necessary. - /// \param[in] pDevice - Render device that will be used to create the new texture, + /// \param[in] pDevice - Render device that will be used to create a new texture, /// if necessary (see remarks). /// \param[in] pContext - Device context that will be used to copy existing /// texture contents (when using non-sparse texture), or bind /// memory tiles (when using sparse textures), if necessary /// (see remarks). - /// \return The pointer to the texture object. + /// \return A pointer to the texture object. /// /// \remarks If the texture has been resized, but internal texture object has not been /// initialized, pDevice and pContext must not be null. @@ -135,6 +135,16 @@ class DynamicTextureArray IDeviceContext* pContext); + /// Returns a pointer to the texture object. + + /// \remarks If the texture has not be initialized, the method returns null. + /// If the texture may need to be updated (initialized or resized), + /// use the overload that takes pDevice and pContext parameters. + ITexture* GetTexture() const + { + return m_pTexture; + } + /// Returns true if the texture must be updated before use (e.g. it has been resized, /// but internal texture has not been initialized or updated). /// When update is not pending, GetTexture() may be called with null device and context. diff --git a/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h b/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h index 53319fca2..defcaa226 100644 --- a/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h +++ b/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -117,11 +117,11 @@ struct DynamicTextureAtlasUsageStats /// Dynamic texture atlas. struct IDynamicTextureAtlas : public IObject { - /// Returns the pointer to the internal texture object. + /// Returns a pointer to the internal texture object. - /// \param[in] pDevice - Pointer to the render device that will be used to + /// \param[in] pDevice - A pointer to the render device that will be used to /// create a new internal texture array, if necessary. - /// \param[in] pContext - Pointer to the device context that will be used to + /// \param[in] pContext - A pointer to the device context that will be used to /// copy existing contents to the new texture array, if /// necessary. /// @@ -133,6 +133,14 @@ struct IDynamicTextureAtlas : public IObject virtual ITexture* GetTexture(IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + /// Returns a pointer to the internal texture object. + + /// \remarks If the texture has not been created yet, the method returns null. + /// If the texture may need to be updated (initialzed or resized), use + /// the overload that takes pDevice and pContext parameters. + virtual ITexture* GetTexture() const = 0; + + /// Performs suballocation from the atlas. /// \param[in] Width - Suballocation width. diff --git a/Graphics/GraphicsTools/interface/VertexPool.h b/Graphics/GraphicsTools/interface/VertexPool.h index 00a6251e8..d388f4e66 100644 --- a/Graphics/GraphicsTools/interface/VertexPool.h +++ b/Graphics/GraphicsTools/interface/VertexPool.h @@ -64,6 +64,11 @@ struct IVertexPoolAllocation : public IObject /// \remarks This method is a shortcut for GetPool()->GetBuffer(Index, pDevice, pContext). virtual IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + /// Returns a pointer to the internal buffer at given index. + + /// \remarks This method is a shortcut for GetPool()->GetBuffer(Index). + virtual IBuffer* GetBuffer(Uint32 Index) const = 0; + /// Stores a pointer to the user-provided data object, which /// may later be retrieved through GetUserData(). /// @@ -188,6 +193,14 @@ struct IVertexPool : public IObject virtual IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + /// Returns a pointer to the internal buffer at given index. + /// + /// \remarks If the internal buffer has not been initialized yet, the method will return null. + /// If the buffer may need to be updated (resized or initialized), use the overload + /// that takes pDevice and pContext parameters. + virtual IBuffer* GetBuffer(Uint32 Index) const = 0; + + /// Allocates vertices from the pool. /// \param[in] NumVertices - The number of vertices to allocate. diff --git a/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp b/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp index 40bea4f56..1b1aca670 100644 --- a/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp +++ b/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp @@ -475,6 +475,11 @@ class DynamicTextureAtlasImpl final : public ObjectBase } } + virtual ITexture* GetTexture() const override final + { + return m_pTexture; + } + virtual Uint32 GetAllocationAlignment(Uint32 Width, Uint32 Height) const override final { return ComputeTextureAtlasSuballocationAlignment(Width, Height, m_MinAlignment); diff --git a/Graphics/GraphicsTools/src/VertexPool.cpp b/Graphics/GraphicsTools/src/VertexPool.cpp index e01b26610..6a3831f0a 100644 --- a/Graphics/GraphicsTools/src/VertexPool.cpp +++ b/Graphics/GraphicsTools/src/VertexPool.cpp @@ -95,6 +95,8 @@ class VertexPoolAllocationImpl final : public ObjectBase virtual IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) override final; + virtual IBuffer* GetBuffer(Uint32 Index) const override final; + virtual void SetUserData(IObject* pUserData) override final { m_pUserData = pUserData; @@ -218,6 +220,17 @@ class VertexPoolImpl final : public ObjectBase return Buffer.GetBuffer(pDevice, pContext); } + virtual IBuffer* GetBuffer(Uint32 Index) const override final + { + if (Index >= m_Buffers.size()) + { + UNEXPECTED("Index (", Index, ") is out of range: there are only ", m_Buffers.size(), " buffers."); + return nullptr; + } + + return m_Buffers[Index]->GetBuffer(); + } + virtual void Allocate(Uint32 NumVertices, IVertexPoolAllocation** ppAllocation) override final { @@ -380,6 +393,11 @@ IBuffer* VertexPoolAllocationImpl::GetBuffer(Uint32 Index, IRenderDevice* pDevic return m_pParentPool->GetBuffer(Index, pDevice, pContext); } +IBuffer* VertexPoolAllocationImpl::GetBuffer(Uint32 Index) const +{ + return m_pParentPool->GetBuffer(Index); +} + void CreateVertexPool(IRenderDevice* pDevice, const VertexPoolCreateInfo& CreateInfo, IVertexPool** ppVertexPool) From 03bb1fe500a569229dff0a1e3249ad2147df0ce0 Mon Sep 17 00:00:00 2001 From: assiduous Date: Mon, 21 Aug 2023 22:10:56 -0700 Subject: [PATCH 05/23] Vertex pool, dynamic buffer and buffer suballocator: separated Update and GetBuffer methods --- .../interface/BufferSuballocator.h | 21 +++++++--- .../GraphicsTools/interface/DynamicBuffer.hpp | 22 +++++----- .../interface/DynamicTextureAtlas.h | 2 +- Graphics/GraphicsTools/interface/VertexPool.h | 22 +++++----- .../GraphicsTools/src/BufferSuballocator.cpp | 22 +++++++--- Graphics/GraphicsTools/src/DynamicBuffer.cpp | 8 ++-- Graphics/GraphicsTools/src/VertexPool.cpp | 10 ++--- .../src/BufferSuballocatorTest.cpp | 8 ++-- .../src/DynamicBufferTest.cpp | 42 ++++++++++++------- .../src/VertexPoolTest.cpp | 13 ++++-- 10 files changed, 107 insertions(+), 63 deletions(-) diff --git a/Graphics/GraphicsTools/interface/BufferSuballocator.h b/Graphics/GraphicsTools/interface/BufferSuballocator.h index 52effa08a..7c9c56ecf 100644 --- a/Graphics/GraphicsTools/interface/BufferSuballocator.h +++ b/Graphics/GraphicsTools/interface/BufferSuballocator.h @@ -61,10 +61,15 @@ struct IBufferSuballocation : public IObject /// Returns a pointer to the parent allocator. virtual IBufferSuballocator* GetAllocator() = 0; + /// Updates the internal buffer object. + + /// \remarks This method is a shortcut for GetAllocator()->Update(pDevice, pContext). + virtual IBuffer* Update(IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + /// Returns a pointer to the internal buffer object. - /// \remarks This method is a shortcut for GetAllocator()->GetBuffer(pDevice, pContext). - virtual IBuffer* GetBuffer(IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + /// \remarks This method is a shortcut for GetAllocator()->GetBuffer(). + virtual IBuffer* GetBuffer() const = 0; /// Stores a pointer to the user-provided data object, which /// may later be retrieved through GetUserData(). @@ -102,7 +107,7 @@ struct BufferSuballocatorUsageStats /// Buffer suballocator. struct IBufferSuballocator : public IObject { - /// Returns a pointer to the internal buffer object. + /// Updates the internal buffer object. /// \param[in] pDevice - A pointer to the render device that will be used to /// create a new internal buffer, if necessary. @@ -113,7 +118,13 @@ struct IBufferSuballocator : public IObject /// be used to create a new buffer and copy existing contents to the new buffer. /// The method is not thread-safe and an application must externally synchronize the /// access. - virtual IBuffer* GetBuffer(IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + virtual IBuffer* Update(IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + + /// Returns a pointer to the internal buffer object. + /// + /// \remarks If the buffer has not been created yet, the method returns null. + /// If the buffer may need to be updated, use the Update() method instead. + virtual IBuffer* GetBuffer() const = 0; /// Performs suballocation from the buffer. @@ -168,7 +179,7 @@ struct BufferSuballocatorCreateInfo /// \param[in] pDevice - A pointer to the render device that will be used to initialize /// the internal buffer object. If this parameter is null, the -/// buffer will be created when GetBuffer() is called. +/// buffer will be created when Update() is called. /// \param[in] CreateInfo - Suballocator create info, see Diligent::BufferSuballocatorCreateInfo. /// \param[in] ppBufferSuballocator - Memory location where pointer to the buffer suballocator will be stored. void CreateBufferSuballocator(IRenderDevice* pDevice, diff --git a/Graphics/GraphicsTools/interface/DynamicBuffer.hpp b/Graphics/GraphicsTools/interface/DynamicBuffer.hpp index 09f14f5a2..65e7da055 100644 --- a/Graphics/GraphicsTools/interface/DynamicBuffer.hpp +++ b/Graphics/GraphicsTools/interface/DynamicBuffer.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -77,7 +77,7 @@ class DynamicBuffer /// \param[in] CreateInfo - Create information, see Diligent::DynamicBufferCreateInfo. /// /// \remarks If pDevice is null, internal buffer creation will be postponed - /// until GetBuffer() or Resize() is called. + /// until Update() or Resize() is called. DynamicBuffer(IRenderDevice* pDevice, const DynamicBufferCreateInfo& CreateInfo); // clang-format off @@ -104,14 +104,14 @@ class DynamicBuffer /// are not null: /// - Both pDevice and pContext are not null: the new internal buffer is created /// and existing contents is copied (for non-sparse buffer), or memory pages - /// are committed (for sparse buffer). GetBuffer() may be called with + /// are committed (for sparse buffer). Update() may be called with /// both pDevice and pContext being null. /// - pDevice is not null, pContext is null: internal buffer is created, /// but existing contents is not copied or memory is not committed. An - /// application must provide non-null device context when calling GetBuffer(). + /// application must provide non-null device context when calling Update(). /// - Both pDevice and pContext are null: internal buffer is not created. /// An application must provide non-null device and device context when calling - /// GetBuffer(). + /// Update(). /// /// Typically pDevice and pContext should be null when the method is called from a worker thread. /// @@ -122,7 +122,7 @@ class DynamicBuffer bool DiscardContent = false); - /// Returns a pointer to the buffer object, initializing it if necessary. + /// Updates the internal buffer object, initializing or resizing it if necessary. /// \param[in] pDevice - Render device that will be used to create a new buffer, /// if necessary (see remarks). @@ -136,15 +136,15 @@ class DynamicBuffer /// /// If buffer does not need to be updated (PendingUpdate() returns false), /// both pDevice and pContext may be null. - IBuffer* GetBuffer(IRenderDevice* pDevice, - IDeviceContext* pContext); + IBuffer* Update(IRenderDevice* pDevice, + IDeviceContext* pContext); /// Returns a pointer to the buffer object. /// /// \remarks If the buffer has not been initialized, the method returns null. - /// If the buffer may need to be updated (resized or initialized), use the overload - /// that takes pDevice and pContext parameters. + /// If the buffer may need to be updated (resized or initialized), use the Update() + /// method. IBuffer* GetBuffer() const { return m_pBuffer; @@ -152,7 +152,7 @@ class DynamicBuffer /// Returns true if the buffer must be updated before use (e.g. it has been resized, /// but internal buffer has not been initialized or updated). - /// When update is not pending, GetBuffer() may be called with null device and context. + /// When update is not pending, Update() may be called with null device and context. bool PendingUpdate() const { return m_PendingSize != m_Desc.Size; diff --git a/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h b/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h index defcaa226..580f0969b 100644 --- a/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h +++ b/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h @@ -136,7 +136,7 @@ struct IDynamicTextureAtlas : public IObject /// Returns a pointer to the internal texture object. /// \remarks If the texture has not been created yet, the method returns null. - /// If the texture may need to be updated (initialzed or resized), use + /// If the texture may need to be updated (initialized or resized), use /// the overload that takes pDevice and pContext parameters. virtual ITexture* GetTexture() const = 0; diff --git a/Graphics/GraphicsTools/interface/VertexPool.h b/Graphics/GraphicsTools/interface/VertexPool.h index d388f4e66..1f35baeea 100644 --- a/Graphics/GraphicsTools/interface/VertexPool.h +++ b/Graphics/GraphicsTools/interface/VertexPool.h @@ -59,12 +59,12 @@ struct IVertexPoolAllocation : public IObject /// Returns a pointer to the parent vertex pool. virtual IVertexPool* GetPool() = 0; - /// Returns a pointer to the internal buffer at given index. + /// Updates internal buffer at the given index. - /// \remarks This method is a shortcut for GetPool()->GetBuffer(Index, pDevice, pContext). - virtual IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + /// \remarks This method is a shortcut for GetPool()->Update(Index, pDevice, pContext). + virtual IBuffer* Update(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) = 0; - /// Returns a pointer to the internal buffer at given index. + /// Returns a pointer to the internal buffer at the given index. /// \remarks This method is a shortcut for GetPool()->GetBuffer(Index). virtual IBuffer* GetBuffer(Uint32 Index) const = 0; @@ -178,7 +178,7 @@ struct VertexPoolDesc /// The vertex pool is a collection of dynamic buffers that can be used to store vertex data. struct IVertexPool : public IObject { - /// Returns a pointer to the internal buffer at given index. + /// Updates the internal buffer object at the given index. /// \param[in] Index - The vertex buffer index. Must be in range [0, Desc.NumElements-1]. /// \param[in] pDevice - A pointer to the render device that will be used to @@ -186,18 +186,20 @@ struct IVertexPool : public IObject /// \param[in] pContext - A pointer to the device context that will be used to /// copy existing contents to the new buffer, if necessary. /// + /// \return A pointer to the internal buffer object. + /// /// \remarks If the internal buffer needs to be resized, pDevice and pContext will /// be used to create a new buffer and copy existing contents to the new buffer. /// The method is not thread-safe and an application must externally synchronize the /// access. - virtual IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + virtual IBuffer* Update(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) = 0; - /// Returns a pointer to the internal buffer at given index. + /// Returns a pointer to the internal buffer at the given index. /// /// \remarks If the internal buffer has not been initialized yet, the method will return null. - /// If the buffer may need to be updated (resized or initialized), use the overload - /// that takes pDevice and pContext parameters. + /// If the buffer may need to be updated (resized or initialized), use the Update() + /// method. virtual IBuffer* GetBuffer(Uint32 Index) const = 0; @@ -251,7 +253,7 @@ struct VertexPoolCreateInfo /// \param[in] pDevice - A pointer to the render device that will be used to initialize /// internal buffer objects. If this parameter is null, the -/// buffers will be created when GetBuffer() is called. +/// buffers will be created when Update() is called. /// \param[in] CreateInfo - Vertex pool create info, see Diligent::VertexPoolCreateInfo. /// \param[in] ppVertexPool - Memory location where a pointer to the vertex pool will be stored. void CreateVertexPool(IRenderDevice* pDevice, diff --git a/Graphics/GraphicsTools/src/BufferSuballocator.cpp b/Graphics/GraphicsTools/src/BufferSuballocator.cpp index 31377fc0a..4f5603177 100644 --- a/Graphics/GraphicsTools/src/BufferSuballocator.cpp +++ b/Graphics/GraphicsTools/src/BufferSuballocator.cpp @@ -93,7 +93,9 @@ class BufferSuballocationImpl final : public ObjectBase virtual IBufferSuballocator* GetAllocator() override final; - virtual IBuffer* GetBuffer(IRenderDevice* pDevice, IDeviceContext* pContext) override final; + virtual IBuffer* Update(IRenderDevice* pDevice, IDeviceContext* pContext) override final; + + virtual IBuffer* GetBuffer() const override final; virtual void SetUserData(IObject* pUserData) override final { @@ -164,7 +166,7 @@ class BufferSuballocatorImpl final : public ObjectBase VERIFY_EXPR(m_AllocationCount.load() == 0); } - virtual IBuffer* GetBuffer(IRenderDevice* pDevice, IDeviceContext* pContext) override final + virtual IBuffer* Update(IRenderDevice* pDevice, IDeviceContext* pContext) override final { // NB: mutex must not be locked here to avoid stalling render thread const auto MgrSize = m_MgrSize.load(); @@ -176,7 +178,12 @@ class BufferSuballocatorImpl final : public ObjectBase // while m_Buffer internally does not use mutex or other synchronization. m_BufferSize.store(m_Buffer.GetDesc().Size); } - return m_Buffer.GetBuffer(pDevice, pContext); + return m_Buffer.Update(pDevice, pContext); + } + + virtual IBuffer* GetBuffer() const override final + { + return m_Buffer.GetBuffer(); } virtual void Allocate(Uint32 Size, @@ -311,9 +318,14 @@ IBufferSuballocator* BufferSuballocationImpl::GetAllocator() return m_pParentAllocator; } -IBuffer* BufferSuballocationImpl::GetBuffer(IRenderDevice* pDevice, IDeviceContext* pContext) +IBuffer* BufferSuballocationImpl::Update(IRenderDevice* pDevice, IDeviceContext* pContext) +{ + return m_pParentAllocator->Update(pDevice, pContext); +} + +IBuffer* BufferSuballocationImpl::GetBuffer() const { - return m_pParentAllocator->GetBuffer(pDevice, pContext); + return m_pParentAllocator->GetBuffer(); } diff --git a/Graphics/GraphicsTools/src/DynamicBuffer.cpp b/Graphics/GraphicsTools/src/DynamicBuffer.cpp index cc0fcbbc7..eb1a51f63 100644 --- a/Graphics/GraphicsTools/src/DynamicBuffer.cpp +++ b/Graphics/GraphicsTools/src/DynamicBuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -308,7 +308,7 @@ IBuffer* DynamicBuffer::Resize(IRenderDevice* pDevice, "There is a non-null stale buffer. This likely indicates that " "Resize() has been called multiple times with different sizes, " "but copy has not been committed by providing non-null device " - "context to either Resize() or GetBuffer()"); + "context to either Resize() or Update()"); } if (m_PendingSize == 0) @@ -328,8 +328,8 @@ IBuffer* DynamicBuffer::Resize(IRenderDevice* pDevice, return m_pBuffer; } -IBuffer* DynamicBuffer::GetBuffer(IRenderDevice* pDevice, - IDeviceContext* pContext) +IBuffer* DynamicBuffer::Update(IRenderDevice* pDevice, + IDeviceContext* pContext) { CommitResize(pDevice, pContext, false /*AllowNull*/); diff --git a/Graphics/GraphicsTools/src/VertexPool.cpp b/Graphics/GraphicsTools/src/VertexPool.cpp index 6a3831f0a..7349ee5cb 100644 --- a/Graphics/GraphicsTools/src/VertexPool.cpp +++ b/Graphics/GraphicsTools/src/VertexPool.cpp @@ -93,7 +93,7 @@ class VertexPoolAllocationImpl final : public ObjectBase virtual IVertexPool* GetPool() override final; - virtual IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) override final; + virtual IBuffer* Update(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) override final; virtual IBuffer* GetBuffer(Uint32 Index) const override final; @@ -197,7 +197,7 @@ class VertexPoolImpl final : public ObjectBase VERIFY_EXPR(m_AllocationCount.load() == 0); } - virtual IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) override final + virtual IBuffer* Update(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) override final { if (Index >= m_Buffers.size()) { @@ -217,7 +217,7 @@ class VertexPoolImpl final : public ObjectBase // while m_Buffer internally does not use mutex or other synchronization. BufferSize.store(Buffer.GetDesc().Size); } - return Buffer.GetBuffer(pDevice, pContext); + return Buffer.Update(pDevice, pContext); } virtual IBuffer* GetBuffer(Uint32 Index) const override final @@ -388,9 +388,9 @@ IVertexPool* VertexPoolAllocationImpl::GetPool() return m_pParentPool; } -IBuffer* VertexPoolAllocationImpl::GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) +IBuffer* VertexPoolAllocationImpl::Update(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) { - return m_pParentPool->GetBuffer(Index, pDevice, pContext); + return m_pParentPool->Update(Index, pDevice, pContext); } IBuffer* VertexPoolAllocationImpl::GetBuffer(Uint32 Index) const diff --git a/Tests/DiligentCoreAPITest/src/BufferSuballocatorTest.cpp b/Tests/DiligentCoreAPITest/src/BufferSuballocatorTest.cpp index 4d4898248..4abe8a4f4 100644 --- a/Tests/DiligentCoreAPITest/src/BufferSuballocatorTest.cpp +++ b/Tests/DiligentCoreAPITest/src/BufferSuballocatorTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -58,8 +58,9 @@ TEST(BufferSuballocatorTest, Create) RefCntAutoPtr pAllocator; CreateBufferSuballocator(pDevice, CI, &pAllocator); - auto* pBuffer = pAllocator->GetBuffer(pDevice, pContext); + auto* pBuffer = pAllocator->Update(pDevice, pContext); EXPECT_NE(pBuffer, nullptr); + EXPECT_EQ(pBuffer, pAllocator->GetBuffer()); RefCntAutoPtr pAlloc; pAllocator->Allocate(256, 16, &pAlloc); @@ -125,8 +126,9 @@ TEST(BufferSuballocatorTest, Allocate) Thread.join(); } - auto* pBuffer = pAllocator->GetBuffer(pDevice, pContext); + auto* pBuffer = pAllocator->Update(pDevice, pContext); EXPECT_NE(pBuffer, nullptr); + EXPECT_EQ(pBuffer, pAllocator->GetBuffer()); { std::vector Threads(NumThreads); diff --git a/Tests/DiligentCoreAPITest/src/DynamicBufferTest.cpp b/Tests/DiligentCoreAPITest/src/DynamicBufferTest.cpp index bc087a7c9..3d385f746 100644 --- a/Tests/DiligentCoreAPITest/src/DynamicBufferTest.cpp +++ b/Tests/DiligentCoreAPITest/src/DynamicBufferTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -87,7 +87,8 @@ TEST_P(DynamicBufferCreateTest, Run) EXPECT_STREQ(DynBuff.GetDesc().Name, BuffDesc.Name); EXPECT_FALSE(DynBuff.PendingUpdate()); - auto* pBuffer = DynBuff.GetBuffer(nullptr, nullptr); + auto* pBuffer = DynBuff.Update(nullptr, nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); EXPECT_FALSE(DynBuff.PendingUpdate()); if (Usage == USAGE_SPARSE) EXPECT_NE(pBuffer, nullptr); @@ -106,8 +107,9 @@ TEST_P(DynamicBufferCreateTest, Run) EXPECT_EQ(DynBuff.GetDesc(), BuffDesc); EXPECT_EQ(DynBuff.PendingUpdate(), BuffDesc.Usage == USAGE_SPARSE); - auto* pBuffer = DynBuff.GetBuffer(nullptr, BuffDesc.Usage == USAGE_SPARSE ? pContext : nullptr); + auto* pBuffer = DynBuff.Update(nullptr, BuffDesc.Usage == USAGE_SPARSE ? pContext : nullptr); ASSERT_NE(pBuffer, nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); BuffDesc.Size = 256 << 10; EXPECT_EQ(DynBuff.GetDesc(), BuffDesc); if (BuffDesc.Usage != USAGE_SPARSE) @@ -124,7 +126,8 @@ TEST_P(DynamicBufferCreateTest, Run) EXPECT_EQ(DynBuff.GetDesc(), BuffDesc); EXPECT_TRUE(DynBuff.PendingUpdate()); - auto* pBuffer = DynBuff.GetBuffer(pDevice, BuffDesc.Usage == USAGE_SPARSE ? pContext : nullptr); + auto* pBuffer = DynBuff.Update(pDevice, BuffDesc.Usage == USAGE_SPARSE ? pContext : nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); EXPECT_FALSE(DynBuff.PendingUpdate()); ASSERT_NE(pBuffer, nullptr); BuffDesc.Size = 256 << 10; @@ -220,7 +223,8 @@ TEST_P(DynamicBufferResizeTest, Run) DynBuff.Resize(nullptr, nullptr, BuffDesc.Size); EXPECT_TRUE(DynBuff.PendingUpdate()); - auto* pBuffer = DynBuff.GetBuffer(pDevice, Usage == USAGE_SPARSE ? pContext : nullptr); + auto* pBuffer = DynBuff.Update(pDevice, Usage == USAGE_SPARSE ? pContext : nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); EXPECT_EQ(DynBuff.GetVersion(), Uint32{1}); EXPECT_FALSE(DynBuff.PendingUpdate()); ASSERT_NE(pBuffer, nullptr); @@ -250,7 +254,8 @@ TEST_P(DynamicBufferResizeTest, Run) EXPECT_EQ(DynBuff.GetDesc(), BuffDesc); EXPECT_TRUE(DynBuff.PendingUpdate()); - auto* pBuffer = DynBuff.GetBuffer(nullptr, pContext); + auto* pBuffer = DynBuff.Update(nullptr, pContext); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); EXPECT_FALSE(DynBuff.PendingUpdate()); BuffDesc.Size = 512 << 10; if (Usage != USAGE_SPARSE) @@ -269,8 +274,9 @@ TEST_P(DynamicBufferResizeTest, Run) BuffDesc = GetSparseBuffDesc("Dynamic buffer resize test 2", Usage, 256 << 10); DynamicBuffer DynBuff{pDevice, CI}; - auto* pBuffer = DynBuff.GetBuffer(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr); + auto* pBuffer = DynBuff.Update(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr); EXPECT_NE(pBuffer, nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); UpdateBuffer(pBuffer, 0, BuffDesc.Size); EXPECT_TRUE(VerifyBuffer(pBuffer, 0, BuffDesc.Size)); @@ -278,13 +284,15 @@ TEST_P(DynamicBufferResizeTest, Run) DynBuff.Resize(pDevice, pContext, BuffDesc.Size); EXPECT_FALSE(DynBuff.PendingUpdate()); EXPECT_EQ(DynBuff.GetVersion(), Usage == USAGE_SPARSE ? 1u : 2u); - pBuffer = DynBuff.GetBuffer(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr); + pBuffer = DynBuff.Update(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr); EXPECT_NE(pBuffer, nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); UpdateBuffer(pBuffer, 256 << 10, 256 << 10); EXPECT_TRUE(VerifyBuffer(pBuffer, 256 << 10, 256 << 10)); - pBuffer = DynBuff.GetBuffer(nullptr, nullptr); + pBuffer = DynBuff.Update(nullptr, nullptr); ASSERT_NE(pBuffer, nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); if (Usage != USAGE_SPARSE) EXPECT_EQ(pBuffer->GetDesc(), BuffDesc); EXPECT_EQ(DynBuff.GetDesc(), BuffDesc); @@ -294,8 +302,9 @@ TEST_P(DynamicBufferResizeTest, Run) EXPECT_FALSE(DynBuff.PendingUpdate()); EXPECT_EQ(DynBuff.GetVersion(), Usage == USAGE_SPARSE ? 1u : 3u); - pBuffer = DynBuff.GetBuffer(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr); + pBuffer = DynBuff.Update(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr); ASSERT_NE(pBuffer, nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); if (Usage != USAGE_SPARSE) EXPECT_EQ(pBuffer->GetDesc(), BuffDesc); EXPECT_EQ(DynBuff.GetDesc(), BuffDesc); @@ -319,7 +328,8 @@ TEST_P(DynamicBufferResizeTest, Run) EXPECT_FALSE(DynBuff.PendingUpdate()); EXPECT_EQ(DynBuff.GetVersion(), Usage == USAGE_SPARSE ? 1u : 2u); - auto* pBuffer = DynBuff.GetBuffer(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr); + auto* pBuffer = DynBuff.Update(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); EXPECT_FALSE(DynBuff.PendingUpdate()); ASSERT_NE(pBuffer, nullptr); } @@ -330,7 +340,7 @@ TEST_P(DynamicBufferResizeTest, Run) auto& BuffDesc{CI.Desc}; BuffDesc = GetSparseBuffDesc("Dynamic buffer resize test 4", Usage, 256 << 10); DynamicBuffer DynBuff{pDevice, CI}; - EXPECT_NE(DynBuff.GetBuffer(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr), nullptr); + EXPECT_NE(DynBuff.Update(nullptr, Usage == USAGE_SPARSE ? pContext : nullptr), nullptr); DynBuff.Resize(nullptr, nullptr, 1024 << 10); @@ -340,9 +350,10 @@ TEST_P(DynamicBufferResizeTest, Run) if (Usage != USAGE_SPARSE) EXPECT_EQ(DynBuff.GetDesc(), BuffDesc); - auto* pBuffer = DynBuff.GetBuffer(pDevice, Usage == USAGE_SPARSE ? pContext : nullptr); + auto* pBuffer = DynBuff.Update(pDevice, Usage == USAGE_SPARSE ? pContext : nullptr); if (Usage != USAGE_SPARSE) EXPECT_EQ(pBuffer, nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); DynBuff.Resize(pDevice, pContext, 512 << 10); EXPECT_FALSE(DynBuff.PendingUpdate()); @@ -350,7 +361,7 @@ TEST_P(DynamicBufferResizeTest, Run) DynBuff.Resize(pDevice, nullptr, 1024 << 10); DynBuff.Resize(nullptr, nullptr, 0); EXPECT_EQ(DynBuff.PendingUpdate(), Usage == USAGE_SPARSE); - pBuffer = DynBuff.GetBuffer(pDevice, Usage == USAGE_SPARSE ? pContext : nullptr); + pBuffer = DynBuff.Update(pDevice, Usage == USAGE_SPARSE ? pContext : nullptr); if (Usage != USAGE_SPARSE) EXPECT_EQ(pBuffer, nullptr); } @@ -362,8 +373,9 @@ TEST_P(DynamicBufferResizeTest, Run) BuffDesc = GetSparseBuffDesc("Dynamic buffer resize test 5", Usage, 256 << 10); DynamicBuffer DynBuff{pDevice, CI}; - auto* pBuffer = DynBuff.GetBuffer(pDevice, pContext); + auto* pBuffer = DynBuff.Update(pDevice, pContext); ASSERT_NE(pBuffer, nullptr); + EXPECT_EQ(pBuffer, DynBuff.GetBuffer()); EXPECT_EQ(DynBuff.GetDesc(), BuffDesc); UpdateBuffer(pBuffer, 0, BuffDesc.Size); EXPECT_TRUE(VerifyBuffer(pBuffer, 0, BuffDesc.Size)); diff --git a/Tests/DiligentCoreAPITest/src/VertexPoolTest.cpp b/Tests/DiligentCoreAPITest/src/VertexPoolTest.cpp index 9e1809720..ce80c1eaa 100644 --- a/Tests/DiligentCoreAPITest/src/VertexPoolTest.cpp +++ b/Tests/DiligentCoreAPITest/src/VertexPoolTest.cpp @@ -64,11 +64,13 @@ TEST(VertexPoolTest, Create) CreateVertexPool(pDevice, CI, &pVtxPool); EXPECT_NE(pVtxPool, nullptr); - auto* pBuffer0 = pVtxPool->GetBuffer(0, pDevice, pContext); + auto* pBuffer0 = pVtxPool->Update(0, pDevice, pContext); EXPECT_NE(pBuffer0, nullptr); + EXPECT_EQ(pBuffer0, pVtxPool->GetBuffer(0)); - auto* pBuffer1 = pVtxPool->GetBuffer(1, pDevice, pContext); + auto* pBuffer1 = pVtxPool->Update(1, pDevice, pContext); EXPECT_NE(pBuffer1, nullptr); + EXPECT_EQ(pBuffer1, pVtxPool->GetBuffer(1)); RefCntAutoPtr pAlloc0; pVtxPool->Allocate(256, &pAlloc0); @@ -152,10 +154,13 @@ TEST(VertexPoolTest, Allocate) Thread.join(); } - auto* pBuffer0 = pVtxPool->GetBuffer(0, pDevice, pContext); + auto* pBuffer0 = pVtxPool->Update(0, pDevice, pContext); EXPECT_NE(pBuffer0, nullptr); - auto* pBuffer1 = pVtxPool->GetBuffer(1, pDevice, pContext); + EXPECT_EQ(pBuffer0, pVtxPool->GetBuffer(0)); + + auto* pBuffer1 = pVtxPool->Update(1, pDevice, pContext); EXPECT_NE(pBuffer1, nullptr); + EXPECT_EQ(pBuffer1, pVtxPool->GetBuffer(1)); { std::vector Threads(NumThreads); From 79c7cd048c03a27bf2b3f64816c2feb2d5ac2420 Mon Sep 17 00:00:00 2001 From: assiduous Date: Mon, 21 Aug 2023 22:32:58 -0700 Subject: [PATCH 06/23] Dynamic texture atlas and array: separated Update and GetTexture methods --- .../interface/DynamicTextureArray.hpp | 18 ++++++------ .../interface/DynamicTextureAtlas.h | 10 +++---- .../GraphicsTools/src/DynamicTextureArray.cpp | 8 ++--- .../GraphicsTools/src/DynamicTextureAtlas.cpp | 8 +++-- .../src/DynamicTextureArrayTest.cpp | 26 +++++++++++------ .../src/DynamicTextureAtlasTest.cpp | 29 ++++++++++++------- 6 files changed, 59 insertions(+), 40 deletions(-) diff --git a/Graphics/GraphicsTools/interface/DynamicTextureArray.hpp b/Graphics/GraphicsTools/interface/DynamicTextureArray.hpp index 64c00c95e..565d09e2c 100644 --- a/Graphics/GraphicsTools/interface/DynamicTextureArray.hpp +++ b/Graphics/GraphicsTools/interface/DynamicTextureArray.hpp @@ -71,7 +71,7 @@ class DynamicTextureArray /// \param[in] CreateInfo - Texture array create information, see Diligent::DynamicTextureArrayCreateInfo. /// /// \remarks If pDevice is null, internal texture creation will be postponed - /// until GetTexture() or Resize() is called. + /// until Update() or Resize() is called. DynamicTextureArray(IRenderDevice* pDevice, const DynamicTextureArrayCreateInfo& CreateInfo); // clang-format off @@ -98,14 +98,14 @@ class DynamicTextureArray /// \remarks The method operation depends on which of pDevice and pContext parameters /// are not null: /// - Both pDevice and pContext are not null: internal texture is created (if necessary) - /// and existing contents is copied (for non-sparse textures). GetTexture() may be called with + /// and existing contents is copied (for non-sparse textures). Update() may be called with /// both pDevice and pContext being null. /// - pDevice is not null, pContext is null: internal texture or additional memory pages /// are created, but existing contents is not copied and memory tiles are not bound. - /// An application must provide non-null device context when calling GetTexture(). + /// An application must provide non-null device context when calling Update(). /// - Both pDevice and pContext are null: internal texture or memory pages are not created. /// An application must provide non-null device and device context when calling - /// GetTexture(). + /// Update(). /// /// Typically pContext is null when the method is called from a worker thread. /// @@ -116,7 +116,7 @@ class DynamicTextureArray bool DiscardContent = false); - /// Returns a pointer to the texture object, initializing it if necessary. + /// Updates the internal texture object. /// \param[in] pDevice - Render device that will be used to create a new texture, /// if necessary (see remarks). @@ -131,15 +131,15 @@ class DynamicTextureArray /// /// If the texture does not need to be updated (PendingUpdate() returns false), /// both pDevice and pContext may be null. - ITexture* GetTexture(IRenderDevice* pDevice, - IDeviceContext* pContext); + ITexture* Update(IRenderDevice* pDevice, + IDeviceContext* pContext); /// Returns a pointer to the texture object. /// \remarks If the texture has not be initialized, the method returns null. /// If the texture may need to be updated (initialized or resized), - /// use the overload that takes pDevice and pContext parameters. + /// use the Update() method. ITexture* GetTexture() const { return m_pTexture; @@ -147,7 +147,7 @@ class DynamicTextureArray /// Returns true if the texture must be updated before use (e.g. it has been resized, /// but internal texture has not been initialized or updated). - /// When update is not pending, GetTexture() may be called with null device and context. + /// When update is not pending, Update() may be called with null device and context. bool PendingUpdate() const { return m_PendingSize != m_Desc.ArraySize; diff --git a/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h b/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h index 580f0969b..72618e28a 100644 --- a/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h +++ b/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h @@ -117,7 +117,7 @@ struct DynamicTextureAtlasUsageStats /// Dynamic texture atlas. struct IDynamicTextureAtlas : public IObject { - /// Returns a pointer to the internal texture object. + /// Updates the internal texture object. /// \param[in] pDevice - A pointer to the render device that will be used to /// create a new internal texture array, if necessary. @@ -130,14 +130,14 @@ struct IDynamicTextureAtlas : public IObject /// /// The method is not thread safe. An application must externally synchronize /// the access. - virtual ITexture* GetTexture(IRenderDevice* pDevice, IDeviceContext* pContext) = 0; + virtual ITexture* Update(IRenderDevice* pDevice, IDeviceContext* pContext) = 0; /// Returns a pointer to the internal texture object. /// \remarks If the texture has not been created yet, the method returns null. /// If the texture may need to be updated (initialized or resized), use - /// the overload that takes pDevice and pContext parameters. + /// the Update() method. virtual ITexture* GetTexture() const = 0; @@ -151,7 +151,7 @@ struct IDynamicTextureAtlas : public IObject /// \remarks The method is thread-safe and can be called from multiple threads simultaneously. /// /// Internal texture array may need to be extended after the allocation happened. - /// An application may call GetTexture() to ensure that the texture is resized and old + /// An application may call the Update() to ensure that the texture is resized and old /// contents is copied. virtual void Allocate(Uint32 Width, Uint32 Height, @@ -250,7 +250,7 @@ Uint32 ComputeTextureAtlasSuballocationAlignment(Uint32 Width, Uint32 Height, Ui /// \param[in] pDevice - Pointer to the render device that will be used to create internal /// texture array. If this parameter is null, the texture will be created -/// when GetTexture() is called. +/// when Update() is called. /// \param[in] CreateInfo - Atlas create info, see Diligent::DynamicTextureAtlasCreateInfo. /// \param[in] ppAtlas - Memory location where pointer to the texture atlas object will be written. void CreateDynamicTextureAtlas(IRenderDevice* pDevice, diff --git a/Graphics/GraphicsTools/src/DynamicTextureArray.cpp b/Graphics/GraphicsTools/src/DynamicTextureArray.cpp index 1682159ea..9057cb28d 100644 --- a/Graphics/GraphicsTools/src/DynamicTextureArray.cpp +++ b/Graphics/GraphicsTools/src/DynamicTextureArray.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -462,7 +462,7 @@ ITexture* DynamicTextureArray::Resize(IRenderDevice* pDevice, "There is a non-null stale Texture. This likely indicates that " "Resize() has been called multiple times with different sizes, " "but copy has not been committed by providing non-null device " - "context to either Resize() or GetTexture()"); + "context to either Resize() or Update()"); } if (m_PendingSize == 0) @@ -482,8 +482,8 @@ ITexture* DynamicTextureArray::Resize(IRenderDevice* pDevice, return m_pTexture; } -ITexture* DynamicTextureArray::GetTexture(IRenderDevice* pDevice, - IDeviceContext* pContext) +ITexture* DynamicTextureArray::Update(IRenderDevice* pDevice, + IDeviceContext* pContext) { CommitResize(pDevice, pContext, false /*AllowNull*/); diff --git a/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp b/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp index 1b1aca670..075e776da 100644 --- a/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp +++ b/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp @@ -449,7 +449,7 @@ class DynamicTextureAtlasImpl final : public ObjectBase IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_DynamicTextureAtlas, TBase) - virtual ITexture* GetTexture(IRenderDevice* pDevice, IDeviceContext* pContext) override final + virtual ITexture* Update(IRenderDevice* pDevice, IDeviceContext* pContext) override final { if (m_DynamicTexArray) { @@ -459,7 +459,7 @@ class DynamicTextureAtlasImpl final : public ObjectBase m_DynamicTexArray->Resize(pDevice, pContext, ArraySize); } - return m_DynamicTexArray->GetTexture(pDevice, pContext); + return m_DynamicTexArray->Update(pDevice, pContext); } else { @@ -477,7 +477,9 @@ class DynamicTextureAtlasImpl final : public ObjectBase virtual ITexture* GetTexture() const override final { - return m_pTexture; + return m_DynamicTexArray ? + m_DynamicTexArray->GetTexture() : + m_pTexture; } virtual Uint32 GetAllocationAlignment(Uint32 Width, Uint32 Height) const override final diff --git a/Tests/DiligentCoreAPITest/src/DynamicTextureArrayTest.cpp b/Tests/DiligentCoreAPITest/src/DynamicTextureArrayTest.cpp index bf5b77ce1..6a94b65c4 100644 --- a/Tests/DiligentCoreAPITest/src/DynamicTextureArrayTest.cpp +++ b/Tests/DiligentCoreAPITest/src/DynamicTextureArrayTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,8 +86,9 @@ TEST_P(DynamicTextureArrayCreateTest, Run) ASSERT_NE(pDynTexArray, nullptr); EXPECT_FALSE(pDynTexArray->PendingUpdate()); - auto* pTexture = pDynTexArray->GetTexture(nullptr, nullptr); + auto* pTexture = pDynTexArray->Update(nullptr, nullptr); EXPECT_EQ(pTexture, nullptr); + EXPECT_EQ(pTexture, pDynTexArray->GetTexture()); } Desc.Name = "Dynamic texture array create test 2"; @@ -97,8 +98,9 @@ TEST_P(DynamicTextureArrayCreateTest, Run) ASSERT_NE(pDynTexArray, nullptr); EXPECT_TRUE(pDynTexArray->PendingUpdate()); - auto* pTexture = pDynTexArray->GetTexture(pDevice, Desc.Usage == USAGE_SPARSE ? pContext : nullptr); + auto* pTexture = pDynTexArray->Update(pDevice, Desc.Usage == USAGE_SPARSE ? pContext : nullptr); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pDynTexArray->GetTexture()); } Desc.Name = "Dynamic texture array create test 3"; @@ -107,8 +109,9 @@ TEST_P(DynamicTextureArrayCreateTest, Run) ASSERT_NE(pDynTexArray, nullptr); EXPECT_EQ(pDynTexArray->PendingUpdate(), Desc.Usage == USAGE_SPARSE); - auto* pTexture = pDynTexArray->GetTexture(nullptr, Desc.Usage == USAGE_SPARSE ? pContext : nullptr); + auto* pTexture = pDynTexArray->Update(nullptr, Desc.Usage == USAGE_SPARSE ? pContext : nullptr); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pDynTexArray->GetTexture()); } } @@ -268,24 +271,28 @@ TEST_P(DynamicTextureArrayResizeTest, Run) pDynTexArray->Resize(pDevice, nullptr, 1); EXPECT_EQ(pDynTexArray->PendingUpdate(), Desc.Usage == USAGE_SPARSE); - auto* pTexture = pDynTexArray->GetTexture(pDevice, pContext); + auto* pTexture = pDynTexArray->Update(pDevice, pContext); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pDynTexArray->GetTexture()); UpdateSlice(pContext, pTexture, 0); VerifySlices(pContext, pTexture, 0, 1); pDynTexArray->Resize(pDevice, pContext, 2); - pTexture = pDynTexArray->GetTexture(nullptr, nullptr); + pTexture = pDynTexArray->Update(nullptr, nullptr); + EXPECT_EQ(pTexture, pDynTexArray->GetTexture()); UpdateSlice(pContext, pTexture, 1); VerifySlices(pContext, pTexture, 1, 1); pDynTexArray->Resize(pDevice, nullptr, 16); - pTexture = pDynTexArray->GetTexture(pDevice, pContext); + pTexture = pDynTexArray->Update(pDevice, pContext); + EXPECT_EQ(pTexture, pDynTexArray->GetTexture()); UpdateSlice(pContext, pTexture, 2); VerifySlices(pContext, pTexture, 2, 1); pDynTexArray->Resize(pDevice, nullptr, 9); - pTexture = pDynTexArray->GetTexture(pDevice, pContext); + pTexture = pDynTexArray->Update(pDevice, pContext); + EXPECT_EQ(pTexture, pDynTexArray->GetTexture()); UpdateSlice(pContext, pTexture, 3); UpdateSlice(pContext, pTexture, 4); UpdateSlice(pContext, pTexture, 5); @@ -293,7 +300,8 @@ TEST_P(DynamicTextureArrayResizeTest, Run) VerifySlices(pContext, pTexture, 0, NumTestSlices); pDynTexArray->Resize(nullptr, nullptr, 0); - pTexture = pDynTexArray->GetTexture(nullptr, pContext); + pTexture = pDynTexArray->Update(nullptr, pContext); + EXPECT_EQ(pTexture, pDynTexArray->GetTexture()); if (Desc.Usage != USAGE_SPARSE) EXPECT_EQ(pTexture, nullptr); } diff --git a/Tests/DiligentCoreAPITest/src/DynamicTextureAtlasTest.cpp b/Tests/DiligentCoreAPITest/src/DynamicTextureAtlasTest.cpp index c92ee44d7..b8da0e753 100644 --- a/Tests/DiligentCoreAPITest/src/DynamicTextureAtlasTest.cpp +++ b/Tests/DiligentCoreAPITest/src/DynamicTextureAtlasTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -77,8 +77,9 @@ TEST(DynamicTextureAtlas, Create) RefCntAutoPtr pAtlas; CreateDynamicTextureAtlas(nullptr, CI, &pAtlas); - auto* pTexture = pAtlas->GetTexture(pDevice, nullptr); + auto* pTexture = pAtlas->Update(pDevice, nullptr); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); RefCntAutoPtr pSuballoc; pAtlas->Allocate(128, 128, &pSuballoc); @@ -116,15 +117,17 @@ TEST(DynamicTextureAtlas, CreateArray) RefCntAutoPtr pAtlas; CreateDynamicTextureAtlas(nullptr, CI, &pAtlas); - auto* pTexture = pAtlas->GetTexture(nullptr, nullptr); + auto* pTexture = pAtlas->Update(nullptr, nullptr); EXPECT_EQ(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); RefCntAutoPtr pSuballoc; pAtlas->Allocate(128, 128, &pSuballoc); EXPECT_TRUE(pSuballoc); - pTexture = pAtlas->GetTexture(pDevice, pContext); + pTexture = pAtlas->Update(pDevice, pContext); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); DynamicTextureAtlasUsageStats Stats; pAtlas->GetUsageStats(Stats); @@ -140,16 +143,18 @@ TEST(DynamicTextureAtlas, CreateArray) RefCntAutoPtr pAtlas; CreateDynamicTextureAtlas(nullptr, CI, &pAtlas); - auto* pTexture = pAtlas->GetTexture(pDevice, pContext); + auto* pTexture = pAtlas->Update(pDevice, pContext); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); } { RefCntAutoPtr pAtlas; CreateDynamicTextureAtlas(pDevice, CI, &pAtlas); - auto* pTexture = pAtlas->GetTexture(pDevice, pContext); + auto* pTexture = pAtlas->Update(pDevice, pContext); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); RefCntAutoPtr pSuballoc; pAtlas->Allocate(128, 128, &pSuballoc); @@ -226,8 +231,9 @@ TEST(DynamicTextureAtlas, Allocate) Thread.join(); } - auto* pTexture = pAtlas->GetTexture(pDevice, pContext); + auto* pTexture = pAtlas->Update(pDevice, pContext); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); { std::vector Threads(NumThreads); @@ -309,8 +315,9 @@ TEST(DynamicTextureAtlas, Overflow) Thread.join(); } - auto* pTexture = pAtlas->GetTexture(pDevice, pContext); + auto* pTexture = pAtlas->Update(pDevice, pContext); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); } } @@ -402,8 +409,9 @@ TEST(DynamicTextureAtlas, AllocRace) ReleaseCompleteSignal.Wait(true, 1); - auto* pTexture = pAtlas->GetTexture(pDevice, pContext); + auto* pTexture = pAtlas->Update(pDevice, pContext); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); } AllocSignal.Trigger(true, -1); @@ -528,8 +536,9 @@ TEST(DynamicTextureAtlas, AllocFreeRace) ReleaseCompleteSignal.Wait(true, 1); - auto* pTexture = pAtlas->GetTexture(pDevice, pContext); + auto* pTexture = pAtlas->Update(pDevice, pContext); EXPECT_NE(pTexture, nullptr); + EXPECT_EQ(pTexture, pAtlas->GetTexture()); } AllocSignal.Trigger(true, -1); From 8dc58528fb2c03fafd054bfbbfe5cd8702266687 Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 23 Aug 2023 10:51:54 -0700 Subject: [PATCH 07/23] GL Context: fixed the warning about unsupported depth clamping --- Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp b/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp index e80555daf..c3cc79c98 100644 --- a/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp @@ -666,6 +666,10 @@ void GLContextState::SetDepthClamp(bool bEnableDepthClamp) glEnable(GL_DEPTH_CLAMP); DEV_CHECK_GL_ERROR("Failed to enable depth clamp"); } + else + { + LOG_WARNING_MESSAGE("Depth clamp is not supported by this device. Check the value of the DepthClamp device feature."); + } } else { @@ -674,10 +678,6 @@ void GLContextState::SetDepthClamp(bool bEnableDepthClamp) glDisable(GL_DEPTH_CLAMP); DEV_CHECK_GL_ERROR("Failed to disable depth clamp"); } - else - { - LOG_WARNING_MESSAGE("Disabling depth clamp is not supported"); - } #pragma warning(pop) } m_RSState.DepthClampEnable = bEnableDepthClamp; From 624d9ff4595f49848c463e0182f83d5fcc644ad9 Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 23 Aug 2023 11:46:41 -0700 Subject: [PATCH 08/23] Pipeline state base: set format of unused render targets to UNKNOWN; validate DepthClipEnable member --- .../include/PipelineStateBase.hpp | 6 +++--- .../GraphicsEngine/src/PipelineStateBase.cpp | 21 ++++++++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp index 3682bf97c..529e25443 100644 --- a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp +++ b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -113,7 +113,7 @@ void CopyRTShaderGroupNames(std::unordered_map& NameTo const RayTracingPipelineStateCreateInfo& CreateInfo, FixedLinearAllocator& MemPool) noexcept; -void CorrectGraphicsPipelineDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept; +void CorrectGraphicsPipelineDesc(GraphicsPipelineDesc& GraphicsPipeline, const DeviceFeatures& Features) noexcept; /// Finds a pipeline resource layout variable with the name 'Name' in shader stage 'ShaderStage' @@ -784,7 +784,7 @@ class PipelineStateBase : public DeviceObjectBasem_pGraphicsPipelineData->pStrides; GraphicsPipeline = CreateInfo.GraphicsPipeline; - CorrectGraphicsPipelineDesc(GraphicsPipeline); + CorrectGraphicsPipelineDesc(GraphicsPipeline, this->GetDevice()->GetDeviceInfo().Features); CopyResourceLayout(CreateInfo.PSODesc.ResourceLayout, this->m_Desc.ResourceLayout, MemPool); CopyResourceSignatures(CreateInfo, MemPool); diff --git a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp index 9cf75e65e..016c116fd 100644 --- a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp @@ -42,13 +42,15 @@ namespace Diligent namespace { -void ValidateRasterizerStateDesc(const PipelineStateDesc& PSODesc, const GraphicsPipelineDesc& GraphicsPipeline) noexcept(false) +void ValidateRasterizerStateDesc(const PipelineStateDesc& PSODesc, const GraphicsPipelineDesc& GraphicsPipeline, const DeviceFeatures& Features) noexcept(false) { const auto& RSDesc = GraphicsPipeline.RasterizerDesc; if (RSDesc.FillMode == FILL_MODE_UNDEFINED) LOG_PSO_ERROR_AND_THROW("RasterizerDesc.FillMode must not be FILL_MODE_UNDEFINED."); if (RSDesc.CullMode == CULL_MODE_UNDEFINED) LOG_PSO_ERROR_AND_THROW("RasterizerDesc.CullMode must not be CULL_MODE_UNDEFINED."); + if (!RSDesc.DepthClipEnable && Features.DepthClamp == DEVICE_FEATURE_STATE_DISABLED) + LOG_ERROR_MESSAGE("Disabling depth clip is not supported by this device. Check the value of the DepthClamp device feature."); } void ValidateDepthStencilDesc(const PipelineStateDesc& PSODesc, const GraphicsPipelineDesc& GraphicsPipeline) noexcept(false) @@ -182,6 +184,15 @@ void CorrectBlendStateDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept } } +void CorrectRasterizerStateDesc(GraphicsPipelineDesc& GraphicsPipeline, const DeviceFeatures& Features) noexcept +{ + auto& RSDesc = GraphicsPipeline.RasterizerDesc; + if (!RSDesc.DepthClipEnable && Features.DepthClamp == DEVICE_FEATURE_STATE_DISABLED) + { + // The error message is printed by ValidateRasterizerStateDesc + RSDesc.DepthClipEnable = True; + } +} void ValidatePipelineResourceSignatures(const PipelineStateCreateInfo& CreateInfo, const IRenderDevice* pDevice) noexcept(false) @@ -542,7 +553,7 @@ void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& C const auto& GraphicsPipeline = CreateInfo.GraphicsPipeline; ValidateBlendStateDesc(PSODesc, GraphicsPipeline); - ValidateRasterizerStateDesc(PSODesc, GraphicsPipeline); + ValidateRasterizerStateDesc(PSODesc, GraphicsPipeline, Features); ValidateDepthStencilDesc(PSODesc, GraphicsPipeline); ValidateGraphicsPipelineDesc(PSODesc, GraphicsPipeline, AdapterInfo.ShadingRate); ValidatePipelineResourceLayoutDesc(PSODesc, Features); @@ -872,9 +883,13 @@ void ValidatePipelineResourceCompatibility(const PipelineResourceDesc& ResDesc, } } -void CorrectGraphicsPipelineDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept +void CorrectGraphicsPipelineDesc(GraphicsPipelineDesc& GraphicsPipeline, const DeviceFeatures& Features) noexcept { + for (size_t rt = GraphicsPipeline.NumRenderTargets; rt < _countof(GraphicsPipeline.RTVFormats); ++rt) + GraphicsPipeline.RTVFormats[rt] = TEX_FORMAT_UNKNOWN; + CorrectBlendStateDesc(GraphicsPipeline); + CorrectRasterizerStateDesc(GraphicsPipeline, Features); CorrectDepthStencilDesc(GraphicsPipeline); } From 3bbd476e016da68fc030a358de7b1639f9257f85 Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 23 Aug 2023 12:29:46 -0700 Subject: [PATCH 09/23] Render state cache: correct graphics PSO desc before serializing it --- Graphics/GraphicsTools/src/RenderStateCache.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Graphics/GraphicsTools/src/RenderStateCache.cpp b/Graphics/GraphicsTools/src/RenderStateCache.cpp index 4464d4974..10944e656 100644 --- a/Graphics/GraphicsTools/src/RenderStateCache.cpp +++ b/Graphics/GraphicsTools/src/RenderStateCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ #include "ObjectBase.hpp" #include "ShaderBase.hpp" +#include "PipelineStateBase.hpp" #include "RefCntAutoPtr.hpp" #include "SerializationDevice.h" #include "SerializedShader.h" @@ -911,6 +912,8 @@ struct RenderStateCacheImpl::SerializedPsoCIWrapper{pSerializationDevice, DeviceType, _CI} { + CorrectGraphicsPipelineDesc(CI.GraphicsPipeline, pSerializationDevice->GetDeviceInfo().Features); + // Replace render pass with serialized render pass if (CI.GraphicsPipeline.pRenderPass != nullptr) { From 5db6a60e1da4f0fbdc23f53bc3f199d65e8c011e Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 23 Aug 2023 12:43:18 -0700 Subject: [PATCH 10/23] ValidateGraphicsPipelineCreateInfo: print warning instead of error if unused RTV format is not UNKNOWN --- Graphics/GraphicsEngine/src/PipelineStateBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp index 016c116fd..b71cd1c3d 100644 --- a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp @@ -644,8 +644,8 @@ void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& C auto RTVFmt = GraphicsPipeline.RTVFormats[rt]; if (RTVFmt != TEX_FORMAT_UNKNOWN) { - LOG_ERROR_MESSAGE("Render target format (", GetTextureFormatAttribs(RTVFmt).Name, ") of unused slot ", rt, - " must be set to TEX_FORMAT_UNKNOWN."); + LOG_WARNING_MESSAGE("Render target format (", GetTextureFormatAttribs(RTVFmt).Name, ") of unused slot ", rt, + " must be set to TEX_FORMAT_UNKNOWN."); } } From 1b4a057ca85324d666cc5dc7484a830bb9dddc00 Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 23 Aug 2023 15:20:18 -0700 Subject: [PATCH 11/23] Updated documentation for DepthClamp device feature and DepthClipEnable member of the RasterizerStateDesc struct --- Graphics/GraphicsEngine/interface/GraphicsTypes.h | 6 ++++++ Graphics/GraphicsEngine/interface/RasterizerState.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/Graphics/GraphicsEngine/interface/GraphicsTypes.h b/Graphics/GraphicsEngine/interface/GraphicsTypes.h index e18be5a03..8bcd252bd 100644 --- a/Graphics/GraphicsEngine/interface/GraphicsTypes.h +++ b/Graphics/GraphicsEngine/interface/GraphicsTypes.h @@ -1678,6 +1678,12 @@ struct DeviceFeatures DEVICE_FEATURE_STATE DepthBiasClamp DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED); /// Indicates if device supports depth clamping + /// + /// \remarks By default polygon faces are clipped against the near and far planes of the view + /// frustum. If depth clipping is disabled in the PSO, the depth of the fragments that + /// would be clipped is clamped to the near/far plane instead of discarding them. + /// If this feature is enabled, the DepthClipEnable member of the RasterizerStateDesc + /// struct can be set to False. Otherwise it must always be set to True. DEVICE_FEATURE_STATE DepthClamp DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED); /// Indicates if device supports depth clamping diff --git a/Graphics/GraphicsEngine/interface/RasterizerState.h b/Graphics/GraphicsEngine/interface/RasterizerState.h index 674c62233..57b6dbc2d 100644 --- a/Graphics/GraphicsEngine/interface/RasterizerState.h +++ b/Graphics/GraphicsEngine/interface/RasterizerState.h @@ -112,6 +112,13 @@ struct RasterizerStateDesc /// Enable clipping against near and far clip planes. /// Default value: True. + /// + /// \remarks By default polygon faces are clipped against the near and far planes of the view + /// frustum. If depth clipping is disabled, the depth of the fragments that would be + /// clipped is clamped to the near/far plane instead of discarding them. + /// + /// To check if the device supports depth clamping, use the DepthClamp device feature. + /// If it is not supported, the value of this member must be True. Bool DepthClipEnable DEFAULT_INITIALIZER(True); /// Enable scissor-rectangle culling. All pixels outside an active scissor rectangle are culled. From 2ebd6cd1b3221a50d7f11f77ac51023a2b33697f Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 23 Aug 2023 17:52:40 -0700 Subject: [PATCH 12/23] BufferD3D11: use GENERIC_READ initial state for dynamic buffers --- Graphics/GraphicsEngineD3D11/src/BufferD3D11Impl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngineD3D11/src/BufferD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/BufferD3D11Impl.cpp index 96bbb0fa4..1bc96a70c 100644 --- a/Graphics/GraphicsEngineD3D11/src/BufferD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/BufferD3D11Impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -129,7 +129,7 @@ BufferD3D11Impl::BufferD3D11Impl(IReferenceCounters* pRefCounters, DEV_CHECK_ERR(SUCCEEDED(hr), "Failed to set buffer name"); } - SetState(RESOURCE_STATE_UNDEFINED); + SetState(m_Desc.Usage == USAGE_DYNAMIC ? RESOURCE_STATE_GENERIC_READ : RESOURCE_STATE_UNDEFINED); // The memory is always coherent in Direct3D11 m_MemoryProperties = MEMORY_PROPERTY_HOST_COHERENT; From 4ede319b43e0ab2448fbe09faa3f250bcfdaaa6e Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 31 Aug 2023 19:00:26 -0700 Subject: [PATCH 13/23] Added ObjectsRegistry template class --- Common/CMakeLists.txt | 1 + Common/interface/ObjectsRegistry.hpp | 262 ++++++++++++++++++ .../src/Common/ObjectsRegistryTest.cpp | 247 +++++++++++++++++ 3 files changed, 510 insertions(+) create mode 100644 Common/interface/ObjectsRegistry.hpp create mode 100644 Tests/DiligentCoreTest/src/Common/ObjectsRegistryTest.cpp diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index 3401bc8a5..9b128314b 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -25,6 +25,7 @@ set(INTERFACE interface/DynamicLinearAllocator.hpp interface/MemoryFileStream.hpp interface/ObjectBase.hpp + interface/ObjectsRegistry.hpp interface/ParsingTools.hpp interface/RefCntAutoPtr.hpp interface/RefCountedObjectImpl.hpp diff --git a/Common/interface/ObjectsRegistry.hpp b/Common/interface/ObjectsRegistry.hpp new file mode 100644 index 000000000..f147eb39b --- /dev/null +++ b/Common/interface/ObjectsRegistry.hpp @@ -0,0 +1,262 @@ +/* Copyright 2023 Diligent Graphics LLC + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include + +#include "../../../DiligentCore/Platforms/Basic/interface/DebugUtilities.hpp" +#include "RefCntAutoPtr.hpp" + +namespace Diligent +{ + +template +struct _StrongPtrHelper; + +// _StrongPtrHelper specialization for RefCntAutoPtr +template +struct _StrongPtrHelper> +{ + using WeakPtrType = RefCntWeakPtr; +}; + +// _StrongPtrHelper specialization for std::shared_ptr +template +struct _StrongPtrHelper> +{ + using WeakPtrType = std::weak_ptr; +}; + +template +auto _LockWeakPtr(RefCntWeakPtr& pWeakPtr) +{ + return pWeakPtr.Lock(); +} + +template +auto _LockWeakPtr(std::weak_ptr& pWeakPtr) +{ + return pWeakPtr.lock(); +} + +/// A thread-safe and exception-safe object registry that works with std::shared_ptr or RefCntAutoPtr. +/// The registry keeps weak pointers to the objects and returns strong pointers if the requested object exits. +/// An application should keep strong pointers to the objects to keep them alive. +/// +/// Usage example for RefCntAutoPtr: +/// +/// ObjectsRegistry> Registry; +/// +/// auto pObj = Registry.Get("Key", +/// []() +/// { +/// RefCntAutoPtr pObj; +/// // Create object +/// return pObj; +/// }); +/// +/// Usage example for shared_ptr: +/// +/// struct RegistryData +/// { +/// // ... +/// }; +/// ObjectsRegistry> Registry; +/// +/// auto pObj = Registry.Get("Key", +/// []() +/// { +/// return std::make_shared(); +/// }); +/// +/// +/// \note If the object is not found in the registry, it is atomically created by the provided initializer function. +/// If the object is found, the initializer function is not called. +/// +/// It is guaranteed, that the Object will only be initialized once, even if multiple threads call Get() simultaneously. +/// +template , + typename KeyEqual = std::equal_to> +class ObjectsRegistry +{ +public: + using WeakPtrType = typename _StrongPtrHelper::WeakPtrType; + + ObjectsRegistry() noexcept + {} + + /// Finds the object in the registry and returns strong pointer to it (std::shared_ptr or RefCntAutoPtr). + /// If the object is not found, it is atomically created using the provided initializer. + /// + /// \param [in] Key - The object key. + /// \param [in] CreateObject - Initializer function that is called if the object is not found in the registry. + /// + /// \return Strong pointer to the object with the specified key, either retrieved from the registry or initialized + /// with the CreateObject function. + /// + /// \remarks CreateObject function may throw in case of an error. + /// + /// It is guaranteed, that the Object will only be initialized once, even if multiple threads call Get() simultaneously. + /// However, if another thread runs an overloaded Get() without the initializer function with the same key, it may + /// remove the entry from the registry, and the object will be initialized multiple times. + /// This is OK as only one object will be added to the registry. + template + StrongPtrType Get(const KeyType& Key, + CreateObjectType&& CreateObject // May throw + ) noexcept(false) + { + // Get the Object wrapper. Since this is a shared pointer, it may not be destroyed + // while we keep one, even if it is popped from the registry by another thread. + std::shared_ptr pObjectWrpr; + { + std::lock_guard Lock{m_CacheMtx}; + + auto it = m_Cache.find(Key); + if (it == m_Cache.end()) + { + it = m_Cache.emplace(Key, std::make_shared()).first; + } + pObjectWrpr = it->second; + } + + StrongPtrType pObject; + try + { + pObject = pObjectWrpr->Get(std::forward(CreateObject)); + } + catch (...) + { + std::lock_guard Lock{m_CacheMtx}; + + auto it = m_Cache.find(Key); + if (it != m_Cache.end()) + { + pObject = it->second->Lock(); + if (pObject) + { + // The object was created by another thread while we were waiting for the lock + return pObject; + } + else + { + m_Cache.erase(it); + } + } + + throw; + } + + { + std::lock_guard Lock{m_CacheMtx}; + + auto it = m_Cache.find(Key); + if (pObject) + { + if (it == m_Cache.end()) + { + // The wrapper was removed from the cache by another thread while we were waiting + // for the lock - add it back. + m_Cache.emplace(Key, pObjectWrpr); + } + } + else + { + if (it != m_Cache.end()) + { + pObject = it->second->Lock(); + // Note that the object may have been created by another thread while we were waiting for the lock + if (!pObject) + m_Cache.erase(it); + } + } + } + + return pObject; + } + + /// Finds the object in the registry and returns a strong pointer to it (std::shared_ptr or RefCntAutoPtr). + /// If the object is not found, returns empty pointer. + /// + /// \param [in] Key - The object key. + /// + /// \return Strong pointer to the object with the specified key, if the object is found in the registry, + /// or empty pointer otherwise. + StrongPtrType Get(const KeyType& Key) + { + std::lock_guard Lock{m_CacheMtx}; + + auto it = m_Cache.find(Key); + if (it != m_Cache.end()) + { + auto pObject = it->second->Lock(); + if (!pObject) + { + // Note that we may remove the entry from the cache while another thread is creating the object. + // This is OK as it will be added back to the cache. + m_Cache.erase(it); + } + + return pObject; + } + + return {}; + } + +private: + class ObjectWrapper + { + public: + template + const StrongPtrType Get(CreateObjectType&& CreateObject) noexcept(false) + { + StrongPtrType pObject; + + std::lock_guard Lock{m_CreateObjectMtx}; + pObject = _LockWeakPtr(m_wpObject); + if (!pObject) + { + pObject = CreateObject(); // May throw + m_wpObject = pObject; + } + + return pObject; + } + + StrongPtrType Lock() + { + return _LockWeakPtr(m_wpObject); + } + + private: + std::mutex m_CreateObjectMtx; + WeakPtrType m_wpObject; + }; + +private: + using CacheType = std::unordered_map, KeyHasher, KeyEqual>; + + std::mutex m_CacheMtx; + CacheType m_Cache; +}; + +} // namespace Diligent diff --git a/Tests/DiligentCoreTest/src/Common/ObjectsRegistryTest.cpp b/Tests/DiligentCoreTest/src/Common/ObjectsRegistryTest.cpp new file mode 100644 index 000000000..dca3e58d9 --- /dev/null +++ b/Tests/DiligentCoreTest/src/Common/ObjectsRegistryTest.cpp @@ -0,0 +1,247 @@ +/* + * Copyright 2023 Diligent Graphics LLC + * + * 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 "ObjectsRegistry.hpp" + +#include "gtest/gtest.h" + +#include +#include + +#include "ObjectBase.hpp" +#include "ThreadSignal.hpp" + +using namespace Diligent; + +namespace +{ + +struct RegistryData +{ + Uint32 Value = ~0u; + + RegistryData() = default; + RegistryData(Uint32 _Value) : + Value{_Value} + {} + + static std::shared_ptr Create(Uint32 _Value) + { + return std::make_shared(_Value); + } +}; + +struct RegistryDataObj : public ObjectBase +{ + RegistryDataObj(IReferenceCounters* pRefCounters, Uint32 _Value) : + ObjectBase{pRefCounters}, + Value{_Value} + {} + + Uint32 Value = ~0u; + + static RefCntAutoPtr Create(Uint32 _Value) + { + return RefCntAutoPtr{MakeNewRCObj()(_Value)}; + } +}; + +template