summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsTools
diff options
context:
space:
mode:
authorassiduous <assiduous@diligentgraphics.com>2020-12-07 06:13:42 +0000
committerassiduous <assiduous@diligentgraphics.com>2020-12-07 06:13:42 +0000
commit512a111585b6e10968c8b31704c4ea5f3b7775a0 (patch)
tree6deefc3b35e3f1b0e2c199cb67b16f050e71ceb9 /Graphics/GraphicsTools
parentGraphicsTools: added IBufferSuballocator (diff)
downloadDiligentCore-512a111585b6e10968c8b31704c4ea5f3b7775a0.tar.gz
DiligentCore-512a111585b6e10968c8b31704c4ea5f3b7775a0.zip
Graphics tools: added Dynamic Texture Atlas
Diffstat (limited to 'Graphics/GraphicsTools')
-rw-r--r--Graphics/GraphicsTools/CMakeLists.txt2
-rw-r--r--Graphics/GraphicsTools/interface/BufferSuballocator.h2
-rw-r--r--Graphics/GraphicsTools/interface/DynamicBuffer.hpp2
-rw-r--r--Graphics/GraphicsTools/interface/DynamicTextureAtlas.h168
-rw-r--r--Graphics/GraphicsTools/src/BufferSuballocator.cpp14
-rw-r--r--Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp411
6 files changed, 592 insertions, 7 deletions
diff --git a/Graphics/GraphicsTools/CMakeLists.txt b/Graphics/GraphicsTools/CMakeLists.txt
index 2b39f073..aeb4131f 100644
--- a/Graphics/GraphicsTools/CMakeLists.txt
+++ b/Graphics/GraphicsTools/CMakeLists.txt
@@ -6,6 +6,7 @@ set(INTERFACE
interface/BufferSuballocator.h
interface/CommonlyUsedStates.h
interface/DynamicBuffer.hpp
+ interface/DynamicTextureAtlas.h
interface/DurationQueryHelper.hpp
interface/GraphicsUtilities.h
interface/MapHelper.hpp
@@ -22,6 +23,7 @@ set(SOURCE
src/BufferSuballocator.cpp
src/DurationQueryHelper.cpp
src/DynamicBuffer.cpp
+ src/DynamicTextureAtlas.cpp
src/GraphicsUtilities.cpp
src/ScopedQueryHelper.cpp
src/ScreenCapture.cpp
diff --git a/Graphics/GraphicsTools/interface/BufferSuballocator.h b/Graphics/GraphicsTools/interface/BufferSuballocator.h
index c1017110..ce7f2c8e 100644
--- a/Graphics/GraphicsTools/interface/BufferSuballocator.h
+++ b/Graphics/GraphicsTools/interface/BufferSuballocator.h
@@ -99,6 +99,8 @@ struct IBufferSuballocator : public IObject
/// The method itself is thread-safe and can be called from multiple threads simultaneously.
/// However, if non-null pDevice and pContext are provided, an appliction must externally
/// synchronize access to these objects.
+ ///
+ /// Typically pDevice and pContext should be null when the method is called from a worker thread.
virtual void Allocate(Uint32 Size,
Uint32 Alignment,
IRenderDevice* pDevice,
diff --git a/Graphics/GraphicsTools/interface/DynamicBuffer.hpp b/Graphics/GraphicsTools/interface/DynamicBuffer.hpp
index 86165295..940dbcf4 100644
--- a/Graphics/GraphicsTools/interface/DynamicBuffer.hpp
+++ b/Graphics/GraphicsTools/interface/DynamicBuffer.hpp
@@ -81,6 +81,8 @@ public:
/// An application must provide non-null device and device context when calling
/// GetBuffer().
///
+ /// Typically pDevice and pContext should be null when the method is called from a worker thread.
+ ///
/// If NewSize is zero, internal buffer will be released.
IBuffer* Resize(IRenderDevice* pDevice,
IDeviceContext* pContext,
diff --git a/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h b/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h
new file mode 100644
index 00000000..39c1cb82
--- /dev/null
+++ b/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2019-2020 Diligent Graphics LLC
+ * Copyright 2015-2019 Egor Yusov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#pragma once
+
+/// \file
+/// Declaration of DynamicTextureAtlas interface and related data structures
+
+#include "../../GraphicsEngine/interface/RenderDevice.h"
+#include "../../GraphicsEngine/interface/DeviceContext.h"
+#include "../../GraphicsEngine/interface/Texture.h"
+#include "../../../Common/interface/BasicMath.hpp"
+
+namespace Diligent
+{
+
+struct IDynamicTextureAtlas;
+
+// {F7ACDCFB-074F-4E31-94C5-DD4C988E4532}
+static const INTERFACE_ID IID_TextureAtlasSuballocation =
+ {0xf7acdcfb, 0x74f, 0x4e31, {0x94, 0xc5, 0xdd, 0x4c, 0x98, 0x8e, 0x45, 0x32}};
+
+
+// {00E1D6FA-47B4-4062-B96C-D3E191A02351}
+static const INTERFACE_ID IID_DynamicTextureAtlas =
+ {0xe1d6fa, 0x47b4, 0x4062, {0xb9, 0x6c, 0xd3, 0xe1, 0x91, 0xa0, 0x23, 0x51}};
+
+
+/// Texture atlas suballocation interface.
+struct ITextureAtlasSuballocation : public IObject
+{
+ /// Returns the suballocation origin.
+ virtual uint2 GetOrigin() const = 0;
+
+ /// Returns the suballocation slice.
+ virtual Uint32 GetSlice() const = 0;
+
+ /// Returns the suballocation size.
+ virtual uint2 GetSize() const = 0;
+
+ /// Returns the texture coordinate scale (xy) and bias (zw).
+ virtual float4 GetUVScaleBias() const = 0;
+
+ /// Returns the pointer to the parent texture atlas.
+ virtual IDynamicTextureAtlas* GetAtlas() = 0;
+};
+
+/// Dynamic texture atlas interface.
+struct IDynamicTextureAtlas : public IObject
+{
+ /// Returns the pointer to the internal texture object.
+
+ /// \param[in] pDevice - Pointer to the render device that will be used to
+ /// create new internal texture array, if necessary.
+ /// \param[in] pContext - Pointer to the device context that will be used to
+ /// copy existing contents to the new texture array, if
+ /// necessary.
+ ///
+ /// \remarks If the internal texture needs to be resized, pDevice and pContext will
+ /// be used to create a new texture and copy existing contents to the new texture.
+ virtual ITexture* GetTexture(IRenderDevice* pDevice, IDeviceContext* pContext) = 0;
+
+
+ /// Performs suballocation from the atlas.
+
+ /// \param[in] Width - Suballocation width.
+ /// \param[in] Height - Suballocation height.
+ /// \param[in] pDevice - Pointer to the render device that will be used to create
+ /// new internal texture array, if necessary. May be null (see remarks).
+ /// \param[in] pContext - Pointer to the device context that will be used to
+ /// copy existing contents to the new texture array, if
+ /// necessary. May be null (see remarks).
+ /// \param[out] ppSuballocation - Memory location where pointer to the new suballocation will be
+ /// stored.
+ ///
+ /// \remarks If there is not enough space in the internal texture array, it will need to be expanded.
+ /// An application may provide non-null pDevice and pContext to resize the array
+ /// immediately. Otherwise the texture will be resized when GetTexture() is called.
+ /// In this case an application must provide non-null pDevice and pContext to GetTexture().
+ ///
+ /// The method itself is thread-safe and can be called from multiple threads simultaneously.
+ /// However, if non-null pDevice and pContext are provided, an appliction must externally
+ /// synchronize access to these objects.
+ ///
+ /// Typically pDevice and pContext should be null when the method is called from a worker thread.
+ virtual void Allocate(Uint32 Width,
+ Uint32 Height,
+ IRenderDevice* pDevice,
+ IDeviceContext* pContext,
+ ITextureAtlasSuballocation** ppSuballocation) = 0;
+
+
+ /// Returns the texture atlas description
+ const TextureDesc& GetAtlasDesc() const;
+
+ /// Returns internal texture array version. The version is incremented every time
+ /// the array is expanded.
+ virtual Uint32 GetVersion() const = 0;
+};
+
+
+/// Dynamic texture atlas create information.
+struct DynamicTextureAtlasCreateInfo
+{
+ /// Pointer to the render device.
+ /// May be null, in which case internal texture initialization will
+ /// be postponed.
+ IRenderDevice* pDevice = nullptr;
+
+
+ /// Texture description
+
+ /// Texture type must be 2D or 2D array. When the type is
+ /// texture 2D, resizes will be disallowed
+ TextureDesc Desc;
+
+
+ /// Texture region allocation granularity.
+
+ /// The width and height of the texture region will be aligned to the
+ /// TextureGranularity value.
+ /// Texture granularity must be power of two.
+ Uint32 TextureGranularity = 128;
+
+ /// The number of extra slices.
+
+ /// When non-zero, the array will be expanded by the specified number of slices every time
+ /// there is insufficient space. If zero, the array size will be doubled when
+ /// more space is needed.
+ Uint32 ExtraSliceCount = 0;
+
+
+ /// Maximum number of slices in texture array.
+ Uint32 MaxSliceCount = 2048;
+
+
+ /// Allocation granularity for ITextureAtlasSuballocation objects.
+ Uint32 SuballocationObjAllocationGranularity = 64;
+};
+
+/// Creates a new dynamic texture atlas.
+void CreateDynamicTextureAtlas(DynamicTextureAtlasCreateInfo& CreateInfo, IDynamicTextureAtlas** ppAtlas);
+
+} // namespace Diligent
diff --git a/Graphics/GraphicsTools/src/BufferSuballocator.cpp b/Graphics/GraphicsTools/src/BufferSuballocator.cpp
index 6669ca31..a87c9ae2 100644
--- a/Graphics/GraphicsTools/src/BufferSuballocator.cpp
+++ b/Graphics/GraphicsTools/src/BufferSuballocator.cpp
@@ -83,10 +83,10 @@ public:
private:
RefCntAutoPtr<BufferSuballocatorImpl> m_pParentAllocator;
+ VariableSizeAllocationsManager::Allocation m_Subregion;
+
const Uint32 m_Offset;
const Uint32 m_Size;
-
- VariableSizeAllocationsManager::Allocation m_Subregion;
};
class BufferSuballocatorImpl final : public ObjectBase<IBufferSuballocator>
@@ -117,11 +117,11 @@ public:
return m_Buffer.GetBuffer(pDevice, pContext);
}
- void Allocate(Uint32 Size,
- Uint32 Alignment,
- IRenderDevice* pDevice,
- IDeviceContext* pContext,
- IBufferSuballocation** ppSuballocation)
+ virtual void Allocate(Uint32 Size,
+ Uint32 Alignment,
+ IRenderDevice* pDevice,
+ IDeviceContext* pContext,
+ IBufferSuballocation** ppSuballocation) override final
{
if (Size == 0)
{
diff --git a/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp b/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp
new file mode 100644
index 00000000..fb771c0e
--- /dev/null
+++ b/Graphics/GraphicsTools/src/DynamicTextureAtlas.cpp
@@ -0,0 +1,411 @@
+/*
+ * Copyright 2019-2020 Diligent Graphics LLC
+ * Copyright 2015-2019 Egor Yusov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#include "DynamicTextureAtlas.h"
+
+#include <mutex>
+#include <algorithm>
+#include <atomic>
+
+#include "DynamicAtlasManager.hpp"
+#include "ObjectBase.hpp"
+#include "RefCntAutoPtr.hpp"
+#include "FixedBlockMemoryAllocator.hpp"
+#include "DefaultRawMemoryAllocator.hpp"
+#include "GraphicsAccessories.hpp"
+#include "Align.hpp"
+
+namespace Diligent
+{
+
+class DynamicTextureAtlasImpl;
+
+class TextureAtlasSuballocationImpl final : public ObjectBase<ITextureAtlasSuballocation>
+{
+public:
+ using TBase = ObjectBase<ITextureAtlasSuballocation>;
+ TextureAtlasSuballocationImpl(IReferenceCounters* pRefCounters,
+ DynamicTextureAtlasImpl* pParentAtlas,
+ DynamicAtlasManager::Region&& Subregion,
+ Uint32 Slice,
+ const uint2& Size) :
+ // clang-format off
+ TBase {pRefCounters},
+ m_pParentAtlas{pParentAtlas},
+ m_Subregion {std::move(Subregion)},
+ m_Slice {Slice},
+ m_Size {Size}
+ // clang-format on
+ {
+ VERIFY_EXPR(m_pParentAtlas);
+ VERIFY_EXPR(!m_Subregion.IsEmpty());
+ }
+
+ ~TextureAtlasSuballocationImpl();
+
+ IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_TextureAtlasSuballocation, TBase)
+
+ virtual uint2 GetOrigin() const override final
+ {
+ return uint2{m_Subregion.x, m_Subregion.y};
+ }
+
+ virtual Uint32 GetSlice() const override final
+ {
+ return m_Slice;
+ }
+
+ virtual uint2 GetSize() const override final
+ {
+ return m_Size;
+ }
+
+ virtual float4 GetUVScaleBias() const override final;
+
+ /// Returns the pointer to the parent texture atlas.
+ virtual IDynamicTextureAtlas* GetAtlas() override final;
+
+private:
+ RefCntAutoPtr<DynamicTextureAtlasImpl> m_pParentAtlas;
+
+ DynamicAtlasManager::Region m_Subregion;
+
+ const Uint32 m_Slice;
+ const uint2 m_Size;
+};
+
+
+class DynamicTextureAtlasImpl final : public ObjectBase<IDynamicTextureAtlas>
+{
+public:
+ using TBase = ObjectBase<IDynamicTextureAtlas>;
+
+ IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_DynamicTextureAtlas, TBase)
+
+ DynamicTextureAtlasImpl(IReferenceCounters* pRefCounters,
+ DynamicTextureAtlasCreateInfo& CreateInfo) :
+ // clang-format off
+ TBase {pRefCounters},
+ m_Desc {CreateInfo.Desc},
+ m_Granularity {CreateInfo.TextureGranularity},
+ m_ExtraSliceCount {CreateInfo.ExtraSliceCount},
+ m_MaxSliceCount {CreateInfo.Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY ? std::min(CreateInfo.MaxSliceCount, Uint32{2048}) : 1},
+ m_SuballocationsAllocator
+ {
+ DefaultRawMemoryAllocator::GetAllocator(),
+ sizeof(TextureAtlasSuballocationImpl),
+ CreateInfo.SuballocationObjAllocationGranularity
+ }
+ // clang-format on
+ {
+ if (m_Desc.Type != RESOURCE_DIM_TEX_2D && m_Desc.Type != RESOURCE_DIM_TEX_2D_ARRAY)
+ LOG_ERROR_AND_THROW(GetResourceDimString(m_Desc.Type), " is not a valid resource dimension. Only 2D and 2D array textures are allowed");
+
+ if (m_Desc.Format == TEX_FORMAT_UNKNOWN)
+ LOG_ERROR_AND_THROW("Texture format must not be UNKNOWN");
+
+ if (m_Granularity == 0)
+ LOG_ERROR_AND_THROW("Texture granularity can't be zero");
+
+ if (!IsPowerOfTwo(m_Granularity))
+ LOG_ERROR_AND_THROW("Texture granularity (", m_Granularity, ") is not power of two");
+
+ if (m_Desc.Width == 0)
+ LOG_ERROR_AND_THROW("Texture width must not be zero");
+
+ if (m_Desc.Height == 0)
+ LOG_ERROR_AND_THROW("Texture height must not be zero");
+
+ if ((m_Desc.Width % m_Granularity) != 0)
+ LOG_ERROR_AND_THROW("Texture width (", m_Desc.Width, ") is not multiple of granularity (", m_Granularity, ")");
+
+ if ((m_Desc.Height % m_Granularity) != 0)
+ LOG_ERROR_AND_THROW("Texture height (", m_Desc.Height, ") is not multiple of granularity (", m_Granularity, ")");
+
+ m_Name = m_Desc.Name;
+ m_Desc.Name = m_Name.c_str();
+
+ for (Uint32 slice = 0; slice < m_Desc.ArraySize; ++slice)
+ {
+ m_Slices.emplace_back(new SliceManager{m_Desc.Width / m_Granularity, m_Desc.Height / m_Granularity});
+ }
+
+ if (m_Desc.ArraySize > 0 && CreateInfo.pDevice != nullptr)
+ {
+ CreateInfo.pDevice->CreateTexture(m_Desc, nullptr, &m_pTexture);
+ if (!m_pTexture)
+ LOG_ERROR_AND_THROW("Failed to create texture atlas texture");
+ }
+
+ m_Version.store(0);
+ }
+
+
+ virtual ITexture* GetTexture(IRenderDevice* pDevice, IDeviceContext* pContext) override final
+ {
+ CommitResize(pDevice, pContext);
+
+ return m_pTexture;
+ }
+
+ virtual void Allocate(Uint32 Width,
+ Uint32 Height,
+ IRenderDevice* pDevice,
+ IDeviceContext* pContext,
+ ITextureAtlasSuballocation** ppSuballocation) override final
+ {
+ if (Width == 0 || Height == 0)
+ {
+ UNEXPECTED("Subregion size must not be zero");
+ return;
+ }
+
+ if (Width > m_Desc.Width || Height > m_Desc.Height)
+ {
+ LOG_ERROR_MESSAGE("Requested size ", Width, " x ", Height, " exceeds the atlas dimensions ", m_Desc.Width, " x ", m_Desc.Height);
+ return;
+ }
+
+ DynamicAtlasManager::Region Subregion;
+
+ Uint32 Slice = 0;
+ while (Slice < m_MaxSliceCount)
+ {
+ SliceManager* pSliceMgr = nullptr;
+ {
+ std::lock_guard<std::mutex> Lock{m_SlicesMtx};
+ if (Slice == m_Slices.size() && Slice + 1 < m_MaxSliceCount)
+ {
+ const auto ExtraSliceCount = m_ExtraSliceCount != 0 ?
+ m_ExtraSliceCount :
+ static_cast<Uint32>(m_Slices.size());
+
+ for (Uint32 ExtraSlice = 1; ExtraSlice <= ExtraSliceCount && Slice + ExtraSlice < m_MaxSliceCount; ++ExtraSlice)
+ {
+ m_Slices.emplace_back(new SliceManager{m_Desc.Width / m_Granularity, m_Desc.Height / m_Granularity});
+ }
+ }
+ pSliceMgr = m_Slices[Slice].get();
+
+ if (m_Slices.size() > m_Desc.ArraySize)
+ {
+ m_Desc.ArraySize = static_cast<Uint32>(m_Slices.size());
+ if (pDevice != nullptr && pContext != nullptr)
+ {
+ CommitResize(pDevice, pContext);
+ }
+ }
+ }
+
+ Subregion = pSliceMgr->Allocate((Width + m_Granularity - 1) / m_Granularity,
+ (Height + m_Granularity - 1) / m_Granularity);
+ if (!Subregion.IsEmpty())
+ break;
+ else
+ ++Slice;
+ }
+
+ if (Subregion.IsEmpty())
+ {
+ LOG_ERROR_MESSAGE("Failed to suballocate texture subregion ", Width, " x ", Height, " from texture atlas");
+ return;
+ }
+
+ Subregion.x *= m_Granularity;
+ Subregion.y *= m_Granularity;
+ Subregion.width *= m_Granularity;
+ Subregion.height *= m_Granularity;
+
+ // clang-format off
+ TextureAtlasSuballocationImpl* pSuballocation{
+ NEW_RC_OBJ(m_SuballocationsAllocator, "TextureAtlasSuballocationImpl instance", TextureAtlasSuballocationImpl)
+ (
+ this,
+ std::move(Subregion),
+ Slice,
+ uint2{Width, Height}
+ )
+ };
+ // clang-format on
+
+ pSuballocation->QueryInterface(IID_TextureAtlasSuballocation, reinterpret_cast<IObject**>(ppSuballocation));
+ }
+
+ virtual void Free(Uint32 Slice, DynamicAtlasManager::Region&& Subregion)
+ {
+ VERIFY((Subregion.x % m_Granularity) == 0, "Subregion x (", Subregion.x, ") is not multiple of granularity (", m_Granularity, ")");
+ VERIFY((Subregion.y % m_Granularity) == 0, "Subregion y (", Subregion.y, ") is not multiple of granularity (", m_Granularity, ")");
+ VERIFY((Subregion.width % m_Granularity) == 0, "Subregion width (", Subregion.width, ") is not multiple of granularity (", m_Granularity, ")");
+ VERIFY((Subregion.height % m_Granularity) == 0, "Subregion height (", Subregion.height, ") is not multiple of granularity (", m_Granularity, ")");
+
+ Subregion.x /= m_Granularity;
+ Subregion.y /= m_Granularity;
+ Subregion.width /= m_Granularity;
+ Subregion.height /= m_Granularity;
+
+ SliceManager* pSliceMgr = nullptr;
+ {
+ std::lock_guard<std::mutex> Lock{m_SlicesMtx};
+ pSliceMgr = m_Slices[Slice].get();
+ }
+ pSliceMgr->Free(std::move(Subregion));
+ }
+
+ virtual const TextureDesc& GetAtlasDesc() const
+ {
+ return m_Desc;
+ }
+
+ virtual Uint32 GetVersion() const override final
+ {
+ return m_Version.load();
+ }
+
+private:
+ void CommitResize(IRenderDevice* pDevice,
+ IDeviceContext* pContext)
+ {
+ VERIFY_EXPR(pDevice != nullptr && pContext != nullptr);
+
+ if (m_pTexture->GetDesc().ArraySize == m_Desc.ArraySize)
+ return;
+
+ RefCntAutoPtr<ITexture> pNewTexture;
+ pDevice->CreateTexture(m_Desc, nullptr, &pNewTexture);
+ VERIFY_EXPR(pNewTexture);
+ m_Version.fetch_add(1);
+
+ LOG_INFO_MESSAGE("Dynamic texture atlas: expanding texture array '", m_Desc.Name,
+ "' (", m_Desc.Width, " x ", m_Desc.Height, " ", m_Desc.MipLevels, "-mip ",
+ GetTextureFormatAttribs(m_Desc.Format).Name, ") to ",
+ m_Desc.ArraySize, " slices. Version: ", GetVersion());
+
+ const auto& StaleTexDesc = m_pTexture->GetDesc();
+
+ if (m_pTexture)
+ {
+ CopyTextureAttribs CopyAttribs;
+ CopyAttribs.pSrcTexture = m_pTexture;
+ CopyAttribs.pDstTexture = pNewTexture;
+ CopyAttribs.SrcTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
+ CopyAttribs.DstTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
+
+ for (Uint32 slice = 0; slice < StaleTexDesc.ArraySize; ++slice)
+ {
+ for (Uint32 mip = 0; mip < StaleTexDesc.MipLevels; ++mip)
+ {
+ CopyAttribs.SrcSlice = slice;
+ CopyAttribs.DstSlice = slice;
+ CopyAttribs.SrcMipLevel = mip;
+ CopyAttribs.DstMipLevel = mip;
+ pContext->CopyTexture(CopyAttribs);
+ }
+ }
+ }
+
+ m_pTexture = std::move(pNewTexture);
+ }
+
+ TextureDesc m_Desc;
+ std::string m_Name;
+
+ const Uint32 m_Granularity;
+ const Uint32 m_ExtraSliceCount;
+ const Uint32 m_MaxSliceCount;
+
+ RefCntAutoPtr<ITexture> m_pTexture;
+
+ FixedBlockMemoryAllocator m_SuballocationsAllocator;
+
+ std::atomic_uint32_t m_Version = {};
+
+
+ struct SliceManager
+ {
+ SliceManager(Uint32 Width, Uint32 Height) :
+ Mgr{Width, Height}
+ {}
+
+ DynamicAtlasManager::Region Allocate(Uint32 Width, Uint32 Height)
+ {
+ std::lock_guard<std::mutex> Lock{Mtx};
+ return Mgr.Allocate(Width, Height);
+ }
+ void Free(DynamicAtlasManager::Region&& Region)
+ {
+ std::lock_guard<std::mutex> Lock{Mtx};
+ Mgr.Free(std::move(Region));
+ }
+
+ private:
+ std::mutex Mtx;
+ DynamicAtlasManager Mgr;
+ };
+ std::mutex m_SlicesMtx;
+ std::vector<std::unique_ptr<SliceManager>> m_Slices;
+};
+
+
+TextureAtlasSuballocationImpl::~TextureAtlasSuballocationImpl()
+{
+ m_pParentAtlas->Free(m_Slice, std::move(m_Subregion));
+}
+
+IDynamicTextureAtlas* TextureAtlasSuballocationImpl::GetAtlas()
+{
+ return m_pParentAtlas;
+}
+
+float4 TextureAtlasSuballocationImpl::GetUVScaleBias() const
+{
+ const auto Origin = GetOrigin().Recast<float>();
+ const auto Size = GetSize().Recast<float>();
+ const auto& AtlasDesc = m_pParentAtlas->GetAtlasDesc();
+ return float4 //
+ {
+ Size.x / static_cast<float>(AtlasDesc.Width),
+ Size.y / static_cast<float>(AtlasDesc.Height),
+ Origin.x / static_cast<float>(AtlasDesc.Width),
+ Origin.y / static_cast<float>(AtlasDesc.Height) //
+ };
+}
+
+
+void CreateDynamicTextureAtlas(DynamicTextureAtlasCreateInfo& CreateInfo, IDynamicTextureAtlas** ppAtlas)
+{
+ try
+ {
+ auto* pAllocator = MakeNewRCObj<DynamicTextureAtlasImpl>()(CreateInfo);
+ pAllocator->QueryInterface(IID_DynamicTextureAtlas, reinterpret_cast<IObject**>(ppAtlas));
+ }
+ catch (...)
+ {
+ LOG_ERROR_MESSAGE("Failed to create buffer suballocator");
+ }
+}
+
+} // namespace Diligent