git.s-ol.nu ~forks/DiligentTools / 60ce41d
Reworked GLTF resource manager to use BufferSuballocator and DynamicTextureAtlas assiduous 11 months ago
4 changed file(s) with 197 addition(s) and 587 deletion(s). Raw diff Collapse all Expand all
5555
5656 struct GLTFCacheInfo
5757 {
58 GLTFResourceManager* pResourceMgr = nullptr;
58 ResourceManager* pResourceMgr = nullptr;
5959
6060 Uint8 IndexBufferIdx = 0;
6161 Uint8 VertexBuffer0Idx = 0;
345345
346346 void PrepareGPUResources(IRenderDevice* pDevice, IDeviceContext* pCtx);
347347
348 IBuffer* GetBuffer(BUFFER_ID BuffId, IRenderDevice* pDevice, IDeviceContext* pCtx)
349 {
350 VERIFY_EXPR(BuffId < BUFFER_ID_NUM_BUFFERS);
351 auto& Buff = Buffers[BuffId];
352 if (Buff.pBuffer)
353 return Buff.pBuffer;
354 else if (Buff.pCacheAllocation)
355 return Buff.pCacheAllocation->GetBuffer(pDevice, pCtx);
356 else
357 return nullptr;
348 IBuffer* GetBuffer(BUFFER_ID BuffId)
349 {
350 return Buffers[BuffId].pBuffer;
351 }
352
353 ITexture* GetTexture(Uint32 Index)
354 {
355 return Textures[Index].pTexture;
358356 }
359357
360358 Uint32 GetFirstIndexLocation() const
361359 {
362360 auto& IndBuff = Buffers[BUFFER_ID_INDEX];
363 VERIFY(!IndBuff.pCacheAllocation || IndBuff.pCacheAllocation->GetRegion().UnalignedOffset % sizeof(Uint32) == 0,
361 VERIFY(!IndBuff.pSuballocation || IndBuff.pSuballocation->GetOffset() % sizeof(Uint32) == 0,
364362 "Allocation offset is not multiple of sizeof(Uint32)");
365 return IndBuff.pCacheAllocation ?
366 static_cast<Uint32>(IndBuff.pCacheAllocation->GetRegion().UnalignedOffset / sizeof(Uint32)) :
363 return IndBuff.pSuballocation ?
364 static_cast<Uint32>(IndBuff.pSuballocation->GetOffset() / sizeof(Uint32)) :
367365 0;
368366 }
369367
370368 Uint32 GetBaseVertex() const
371369 {
372370 auto& VertBuff = Buffers[BUFFER_ID_VERTEX0];
373 VERIFY(!VertBuff.pCacheAllocation || VertBuff.pCacheAllocation->GetRegion().UnalignedOffset % sizeof(VertexAttribs0) == 0,
371 VERIFY(!VertBuff.pSuballocation || VertBuff.pSuballocation->GetOffset() % sizeof(VertexAttribs0) == 0,
374372 "Allocation offset is not multiple of sizeof(VertexAttribs0)");
375 return VertBuff.pCacheAllocation ?
376 static_cast<Uint32>(VertBuff.pCacheAllocation->GetRegion().UnalignedOffset / sizeof(VertexAttribs0)) :
373 return VertBuff.pSuballocation ?
374 static_cast<Uint32>(VertBuff.pSuballocation->GetOffset() / sizeof(VertexAttribs0)) :
377375 0;
378 }
379
380 ITexture* GetTexture(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pCtx)
381 {
382 auto& TexInfo = Textures[Index];
383 if (TexInfo.pTexture)
384 return TexInfo.pTexture;
385 else if (TexInfo.pCacheAllocation)
386 return TexInfo.pCacheAllocation->GetTexture(pDevice, pCtx);
387 else
388 return nullptr;
389 }
390
391 const float4& GetUVScaleBias(Uint32 Index)
392 {
393 return Textures[Index].UVScaleBias;
394 }
395
396 float GetSlice(Uint32 Index)
397 {
398 return Textures[Index].Slice;
399376 }
400377
401378 private:
433410
434411 struct BufferInfo
435412 {
436 RefCntAutoPtr<IBuffer> pBuffer;
437
438 RefCntAutoPtr<GLTFResourceManager::BufferAllocation> pCacheAllocation;
413 RefCntAutoPtr<IBuffer> pBuffer;
414 RefCntAutoPtr<IBufferSuballocation> pSuballocation;
439415 };
440416 std::array<BufferInfo, BUFFER_ID_NUM_BUFFERS> Buffers;
441417
442418 struct TextureInfo
443419 {
444 RefCntAutoPtr<ITexture> pTexture;
445 float4 UVScaleBias{1, 1, 0, 0};
446 float Slice = 0;
447
448 RefCntAutoPtr<GLTFResourceManager::TextureAllocation> pCacheAllocation;
420 RefCntAutoPtr<ITexture> pTexture;
421 RefCntAutoPtr<ITextureAtlasSuballocation> pAtlasSuballocation;
449422
450423 bool IsValid() const
451424 {
452 return pTexture || pCacheAllocation;
425 return pTexture || pAtlasSuballocation;
453426 }
454427 };
455428 std::vector<TextureInfo> Textures;
3535 #include "../../../DiligentCore/Graphics/GraphicsEngine/interface/DeviceContext.h"
3636 #include "../../../DiligentCore/Common/interface/RefCntAutoPtr.hpp"
3737 #include "../../../DiligentCore/Common/interface/ObjectBase.hpp"
38 #include "../../../DiligentCore/Graphics/GraphicsAccessories/interface/VariableSizeAllocationsManager.hpp"
39 #include "../../../DiligentCore/Graphics/GraphicsAccessories/interface/DynamicAtlasManager.hpp"
38 #include "../../../DiligentCore/Graphics/GraphicsTools/interface/BufferSuballocator.h"
39 #include "../../../DiligentCore/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h"
4040
4141
4242 namespace Diligent
4343 {
4444
45 namespace GLTF
46 {
47
4548 /// GLTF resource manager
46 class GLTFResourceManager final : public ObjectBase<IObject>
49 class ResourceManager final : public ObjectBase<IObject>
4750 {
48 class BufferCache;
49 class TextureCache;
50
5151 public:
5252 using TBase = ObjectBase<IObject>;
5353
54 class BufferAllocation final : public ObjectBase<IObject>
54 struct CreateInfo
5555 {
56 public:
57 BufferAllocation(IReferenceCounters* pRefCounters,
58 RefCntAutoPtr<GLTFResourceManager> pResourceMg,
59 BufferCache& ParentCache,
60 VariableSizeAllocationsManager::Allocation&& Region) :
61 // clang-format off
62 ObjectBase<IObject>{pRefCounters},
63 m_pResMgr {std::move(pResourceMg)},
64 m_ParentCache {ParentCache},
65 m_Region {std::move(Region)}
66 // clang-format on
67 {
68 VERIFY_EXPR(m_Region.IsValid());
69 }
56 const BufferSuballocatorCreateInfo* BuffSuballocators = nullptr; // [NumBuffSuballocators]
57 const DynamicTextureAtlasCreateInfo* TexAtlases = nullptr; // [NumTexAtlases]
7058
71 ~BufferAllocation()
72 {
73 m_ParentCache.FreeAllocation(std::move(m_Region));
74 }
59 Uint32 NumBuffSuballocators = 0;
60 Uint32 NumTexAtlases = 0;
7561
76 IBuffer* GetBuffer(IRenderDevice* pDevice, IDeviceContext* pContext) const
77 {
78 return m_ParentCache.GetBuffer(pDevice, pContext);
79 }
80
81 const VariableSizeAllocationsManager::Allocation& GetRegion() const
82 {
83 return m_Region;
84 }
85
86 private:
87 RefCntAutoPtr<GLTFResourceManager> m_pResMgr;
88 BufferCache& m_ParentCache;
89 VariableSizeAllocationsManager::Allocation m_Region;
62 DynamicTextureAtlasCreateInfo DefaultAtlasDesc;
9063 };
9164
92 class TextureAllocation final : public ObjectBase<IObject>
65 static RefCntAutoPtr<ResourceManager> Create(IRenderDevice* pDevice,
66 const CreateInfo& CI);
67
68 RefCntAutoPtr<IBufferSuballocation> AllocateBufferSpace(Uint32 BufferIndex,
69 Uint32 Size,
70 Uint32 Alignment)
9371 {
94 public:
95 TextureAllocation(IReferenceCounters* pRefCounters,
96 RefCntAutoPtr<GLTFResourceManager> pResourceMg,
97 TextureCache& ParentCache,
98 Uint32 Width,
99 Uint32 Height,
100 Uint32 Slice,
101 DynamicAtlasManager::Region&& Region) :
102 // clang-format off
103 ObjectBase<IObject>{pRefCounters},
104 m_pResMgr {std::move(pResourceMg)},
105 m_ParentCache {ParentCache},
106 m_Width {Width},
107 m_Height {Height},
108 m_Slice {Slice},
109 m_Region {std::move(Region)}
110 // clang-format on
111 {
112 VERIFY_EXPR(!m_Region.IsEmpty());
113 }
114
115 ~TextureAllocation()
116 {
117 m_ParentCache.FreeAllocation(m_Slice, std::move(m_Region));
118 }
119
120 ITexture* GetTexture(IRenderDevice* pDevice, IDeviceContext* pContext) const
121 {
122 return m_ParentCache.GetTexture(pDevice, pContext);
123 }
124
125 const TextureDesc& GetTexDesc() const
126 {
127 return m_ParentCache.GetTexDesc();
128 }
129
130 const DynamicAtlasManager::Region& GetRegion()
131 {
132 return m_Region;
133 }
134
135 Uint32 GetWidth() const { return m_Width; }
136 Uint32 GetHeight() const { return m_Height; }
137 Uint32 GetSlice() const { return m_Slice; }
138
139 private:
140 RefCntAutoPtr<GLTFResourceManager> m_pResMgr;
141 TextureCache& m_ParentCache;
142 const Uint32 m_Width;
143 const Uint32 m_Height;
144 const Uint32 m_Slice;
145 DynamicAtlasManager::Region m_Region;
146 };
147
148 struct TextureCacheAttribs
149 {
150 TextureDesc Desc;
151
152 Uint32 Granularity = 128;
153 Uint32 ExtraSliceCount = 2;
154 Uint32 MaxSlices = 2048;
155 };
156 struct CreateInfo
157 {
158 const BufferDesc* Buffers = nullptr; // [NumBuffers]
159 Uint32 NumBuffers = 0;
160
161 const TextureCacheAttribs* Textures = nullptr; // [NumTextures]
162 Uint32 NumTextures = 0;
163
164 TextureDesc DefaultTexDesc;
165
166 Uint32 DefaultExtraSliceCount = 1;
167 };
168
169 static RefCntAutoPtr<GLTFResourceManager> Create(IRenderDevice* pDevice,
170 const CreateInfo& CI);
171
172 RefCntAutoPtr<BufferAllocation> AllocateBufferSpace(Uint32 BufferIndex, Uint32 Size, Uint32 Alignment)
173 {
174 return m_Buffers[BufferIndex].Allocate(Size, Alignment);
72 RefCntAutoPtr<IBufferSuballocation> pSuballoc;
73 m_BufferSuballocators[BufferIndex]->Allocate(Size, Alignment, &pSuballoc);
74 return pSuballoc;
17575 }
17676
177 RefCntAutoPtr<TextureAllocation> AllocateTextureSpace(TEXTURE_FORMAT Fmt, Uint32 Width, Uint32 Height, const char* CacheId = nullptr);
77 RefCntAutoPtr<ITextureAtlasSuballocation> AllocateTextureSpace(TEXTURE_FORMAT Fmt,
78 Uint32 Width,
79 Uint32 Height,
80 const char* CacheId = nullptr);
17881
179 RefCntAutoPtr<TextureAllocation> FindAllocation(const char* CacheId);
82 RefCntAutoPtr<ITextureAtlasSuballocation> FindAllocation(const char* CacheId);
18083
181 Uint32 GetResourceVersion() const
84 Uint32 GetResourceVersion()
18285 {
183 return m_ResourceVersion.load();
86 Uint32 Version = 0;
87
88 std::lock_guard<std::mutex> Lock{m_AtlasesMtx};
89 for (auto atlas_it : m_Atlases)
90 Version += atlas_it.second->GetVersion();
91
92 return Version;
18493 }
18594
18695 IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext)
18796 {
188 return m_Buffers[Index].GetBuffer(pDevice, pContext);
97 return m_BufferSuballocators[Index]->GetBuffer(pDevice, pContext);
18998 }
99
190100 ITexture* GetTexture(TEXTURE_FORMAT Fmt, IRenderDevice* pDevice, IDeviceContext* pContext)
191101 {
192 decltype(m_Textures)::iterator cache_it; // NB: can't initialize it without locking the mutex
102 decltype(m_Atlases)::iterator cache_it; // NB: can't initialize it without locking the mutex
193103 {
194 std::lock_guard<std::mutex> Lock{m_TexturesMtx};
195 cache_it = m_Textures.find(Fmt);
196 if (cache_it == m_Textures.end())
104 std::lock_guard<std::mutex> Lock{m_AtlasesMtx};
105 cache_it = m_Atlases.find(Fmt);
106 if (cache_it == m_Atlases.end())
197107 return nullptr;
198108 }
199109
200 return cache_it->second.GetTexture(pDevice, pContext);
110 return cache_it->second->GetTexture(pDevice, pContext);
201111 }
202112
203113 private:
204114 template <typename AllocatorType, typename ObjectType>
205115 friend class MakeNewRCObj;
206116
207 GLTFResourceManager(IReferenceCounters* pRefCounters,
208 IRenderDevice* pDevice,
209 const CreateInfo& CI);
117 ResourceManager(IReferenceCounters* pRefCounters,
118 IRenderDevice* pDevice,
119 const CreateInfo& CI);
210120
211 class BufferCache
212 {
213 public:
214 BufferCache(GLTFResourceManager& Owner, IRenderDevice* pDevice, const BufferDesc& BuffDesc);
121 std::vector<RefCntAutoPtr<IBufferSuballocator>> m_BufferSuballocators;
215122
216 // clang-format off
217 BufferCache (const BufferCache&) = delete;
218 BufferCache& operator=(const BufferCache&) = delete;
219 BufferCache& operator=(BufferCache&&) = delete;
220 // clang-format on
123 DynamicTextureAtlasCreateInfo m_DefaultAtlasDesc;
124 const std::string m_DefaultAtlasName;
221125
222 BufferCache(BufferCache&& Cache) noexcept :
223 // clang-format off
224 m_Owner {Cache.m_Owner},
225 m_Mgr {std::move(Cache.m_Mgr)},
226 m_pBuffer{std::move(Cache.m_pBuffer)}
227 // clang-format on
228 {}
126 std::mutex m_AtlasesMtx;
127 std::unordered_map<TEXTURE_FORMAT, RefCntAutoPtr<IDynamicTextureAtlas>> m_Atlases;
229128
230 IBuffer* GetBuffer(IRenderDevice* pDevice, IDeviceContext* pContext);
231
232 RefCntAutoPtr<BufferAllocation> Allocate(Uint32 Size, Uint32 Alignment);
233
234 void FreeAllocation(VariableSizeAllocationsManager::Allocation&& Allocation)
235 {
236 std::lock_guard<std::mutex> Lock{m_Mtx};
237 m_Mgr.Free(std::move(Allocation));
238 }
239
240 private:
241 GLTFResourceManager& m_Owner;
242
243 std::mutex m_Mtx;
244 VariableSizeAllocationsManager m_Mgr;
245 RefCntAutoPtr<IBuffer> m_pBuffer;
246 };
247 std::vector<BufferCache> m_Buffers;
248
249
250 class TextureCache
251 {
252 public:
253 TextureCache(GLTFResourceManager& Owner, IRenderDevice* pDevice, const TextureCacheAttribs& CacheCI);
254
255 // clang-format off
256 TextureCache (const TextureCache&) = delete;
257 TextureCache& operator=(const TextureCache&) = delete;
258 TextureCache& operator=(TextureCache&&) = delete;
259 // clang-format on
260
261 TextureCache(TextureCache&& Cache) noexcept :
262 // clang-format off
263 m_Owner {Cache.m_Owner},
264 m_Attribs {Cache.m_Attribs},
265 m_TexName {std::move(m_TexName)},
266 m_Slices {std::move(Cache.m_Slices)},
267 m_pTexture{std::move(Cache.m_pTexture)}
268 // clang-format on
269 {
270 m_Attribs.Desc.Name = m_TexName.c_str();
271 }
272
273 ITexture* GetTexture(IRenderDevice* pDevice, IDeviceContext* pContext);
274
275 const TextureDesc& GetTexDesc() const
276 {
277 return m_Attribs.Desc;
278 }
279
280 RefCntAutoPtr<TextureAllocation> Allocate(Uint32 Width, Uint32 Height);
281
282 void FreeAllocation(Uint32 Slice, DynamicAtlasManager::Region&& Allocation);
283
284 private:
285 GLTFResourceManager& m_Owner;
286
287 TextureCacheAttribs m_Attribs;
288
289 std::string m_TexName;
290
291 struct SliceManager
292 {
293 SliceManager(Uint32 Width, Uint32 Height) :
294 Mgr{Width, Height}
295 {}
296
297 DynamicAtlasManager::Region Allocate(Uint32 Width, Uint32 Height)
298 {
299 std::lock_guard<std::mutex> Lock{Mtx};
300 return Mgr.Allocate(Width, Height);
301 }
302 void Free(DynamicAtlasManager::Region&& Region)
303 {
304 std::lock_guard<std::mutex> Lock{Mtx};
305 Mgr.Free(std::move(Region));
306 }
307
308 private:
309 std::mutex Mtx;
310 DynamicAtlasManager Mgr;
311 };
312 std::mutex m_SlicesMtx;
313 std::vector<std::unique_ptr<SliceManager>> m_Slices;
314
315 RefCntAutoPtr<ITexture> m_pTexture;
316 };
317
318 TextureDesc m_DefaultTexDesc;
319 const Uint32 m_DefaultExtraSliceCount;
320
321
322 std::mutex m_TexturesMtx;
323 std::unordered_map<TEXTURE_FORMAT, TextureCache> m_Textures;
324
325 std::mutex m_AllocationsMtx;
326 std::unordered_map<std::string, RefCntWeakPtr<TextureAllocation>> m_Allocations;
129 std::mutex m_TexAllocationsMtx;
130 std::unordered_map<std::string, RefCntWeakPtr<ITextureAtlasSuballocation>> m_TexAllocations;
327131
328132 std::atomic_uint32_t m_ResourceVersion = {};
329133 };
330134
135 } // namespace GLTF
136
331137 } // namespace Diligent
681681 const tinygltf::Image& gltf_image = gltf_model.images[gltf_tex.source];
682682
683683 // TODO: simplify path
684 const auto CacheId = BaseDir + gltf_image.uri;
684 const auto CacheId = !gltf_image.uri.empty() ? BaseDir + gltf_image.uri : "";
685685
686686 TextureInfo TexInfo;
687687 if (CacheInfo.pResourceMgr != nullptr)
688688 {
689 TexInfo.pCacheAllocation = CacheInfo.pResourceMgr->FindAllocation(CacheId.c_str());
690 if (TexInfo.pCacheAllocation)
691 {
692 const auto& TexDesc = TexInfo.pCacheAllocation->GetTexDesc();
693 const auto& Region = TexInfo.pCacheAllocation->GetRegion();
694 VERIFY_EXPR(gltf_image.width == static_cast<int>(TexInfo.pCacheAllocation->GetWidth()));
695 VERIFY_EXPR(gltf_image.height == static_cast<int>(TexInfo.pCacheAllocation->GetHeight()));
696
697 TexInfo.UVScaleBias.x = static_cast<float>(gltf_image.width) / static_cast<float>(TexDesc.Width);
698 TexInfo.UVScaleBias.y = static_cast<float>(gltf_image.height) / static_cast<float>(TexDesc.Height);
699 TexInfo.UVScaleBias.z = static_cast<float>(Region.x) / static_cast<float>(TexDesc.Width);
700 TexInfo.UVScaleBias.w = static_cast<float>(Region.y) / static_cast<float>(TexDesc.Height);
701
702 TexInfo.Slice = static_cast<float>(TexInfo.pCacheAllocation->GetSlice());
689 TexInfo.pAtlasSuballocation = CacheInfo.pResourceMgr->FindAllocation(CacheId.c_str());
690 if (TexInfo.pAtlasSuballocation)
691 {
692 // Note that the texture may appear in the cache after the call to LoadImageData because
693 // it can be loaded by another thread
694 VERIFY_EXPR(gltf_image.width == -1 || gltf_image.width == static_cast<int>(TexInfo.pAtlasSuballocation->GetSize().x));
695 VERIFY_EXPR(gltf_image.height == -1 || gltf_image.height == static_cast<int>(TexInfo.pAtlasSuballocation->GetSize().y));
703696 }
704697 }
705698 else if (pTextureCache != nullptr)
750743 {
751744 if (CacheInfo.pResourceMgr != nullptr)
752745 {
753 TexInfo.pCacheAllocation = CacheInfo.pResourceMgr->AllocateTextureSpace(TEX_FORMAT_RGBA8_UNORM, gltf_image.width, gltf_image.height);
746 TexInfo.pAtlasSuballocation =
747 CacheInfo.pResourceMgr->AllocateTextureSpace(TEX_FORMAT_RGBA8_UNORM, gltf_image.width, gltf_image.height, CacheId.c_str());
754748 }
755749 else
756750 {
767761
768762 pDevice->CreateTexture(TexDesc, nullptr, &TexInfo.pTexture);
769763 TexInfo.pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE)->SetSampler(pSampler);
770 TexInfo.UVScaleBias = float4{1, 1, 0, 0};
771 TexInfo.Slice = 0;
772764
773765 TexInitData = PrepareGLTFTextureInitData(gltf_image, AlphaCutoff, 0, 0, 1);
774766 }
781773 TextureLoadInfo LoadInfo;
782774 if (CacheInfo.pResourceMgr != nullptr)
783775 {
776 LoadInfo.Name = "Staging upload texture for compressed";
784777 LoadInfo.Usage = USAGE_STAGING;
785778 LoadInfo.BindFlags = BIND_NONE;
786779 LoadInfo.CPUAccessFlags = CPU_ACCESS_WRITE;
800793 }
801794 if (CacheInfo.pResourceMgr != nullptr && TexInitData.pStagingTex)
802795 {
803 const auto& TexDesc = TexInitData.pStagingTex->GetDesc();
804 TexInfo.pCacheAllocation = CacheInfo.pResourceMgr->AllocateTextureSpace(TexDesc.Format, TexDesc.Width, TexDesc.Height);
805 }
806 }
807
808 if (TexInfo.pCacheAllocation)
809 {
810 const auto& AtlasDesc = TexInfo.pCacheAllocation->GetTexDesc();
811
812 const auto& Region = TexInfo.pCacheAllocation->GetRegion();
796 const auto& TexDesc = TexInitData.pStagingTex->GetDesc();
797 TexInfo.pAtlasSuballocation = CacheInfo.pResourceMgr->AllocateTextureSpace(TexDesc.Format, TexDesc.Width, TexDesc.Height, CacheId.c_str());
798 }
799 }
800
801 if (TexInfo.pAtlasSuballocation)
802 {
803 const auto& AtlasDesc = TexInfo.pAtlasSuballocation->GetAtlas()->GetAtlasDesc();
804 const auto& Origin = TexInfo.pAtlasSuballocation->GetOrigin();
805
813806 if (!TexInitData.pStagingTex)
814807 {
815 TexInitData = PrepareGLTFTextureInitData(gltf_image, AlphaCutoff, Region.x, Region.y, AtlasDesc.MipLevels);
816 }
817
818 TexInfo.UVScaleBias.x = static_cast<float>(gltf_image.width) / static_cast<float>(AtlasDesc.Width);
819 TexInfo.UVScaleBias.y = static_cast<float>(gltf_image.height) / static_cast<float>(AtlasDesc.Height);
820 TexInfo.UVScaleBias.z = static_cast<float>(Region.x) / static_cast<float>(AtlasDesc.Width);
821 TexInfo.UVScaleBias.w = static_cast<float>(Region.y) / static_cast<float>(AtlasDesc.Height);
822
823 TexInfo.Slice = static_cast<float>(TexInfo.pCacheAllocation->GetSlice());
808 TexInitData = PrepareGLTFTextureInitData(gltf_image, AlphaCutoff, Origin.x, Origin.y, AtlasDesc.MipLevels);
809 }
824810 }
825811
826812 if (!InitData)
865851 VERIFY_EXPR(InitData->Textures.size() == Textures.size());
866852 for (Uint32 i = 0; i < Textures.size(); ++i)
867853 {
868 auto* pTexture = GetTexture(i, pDevice, pCtx);
854 auto& TexInfo = Textures[i];
855 ITexture* pTexture = TexInfo.pAtlasSuballocation ?
856 TexInfo.pAtlasSuballocation->GetAtlas()->GetTexture(pDevice, pCtx) :
857 TexInfo.pTexture;
869858 if (!pTexture)
870859 continue;
871860
872861 auto& TexData = InitData->Textures[i];
873862
874863 const auto& Levels = TexData.Levels;
875 const auto DstSlice = static_cast<Uint32>(Textures[i].Slice);
864 const auto DstSlice = TexInfo.pAtlasSuballocation ? TexInfo.pAtlasSuballocation->GetSlice() : 0;
876865 if (!Levels.empty())
877866 {
878867 VERIFY_EXPR(Levels.size() == 1 || Levels.size() == pTexture->GetDesc().MipLevels);
902891 CopyAttribs.pDstTexture = pTexture;
903892 CopyAttribs.SrcTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
904893 CopyAttribs.DstTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
905
906 const auto& DstTexDesc = pTexture->GetDesc();
907 for (Uint32 mip = 0; mip < DstTexDesc.MipLevels; ++mip)
908 {
909 CopyAttribs.SrcSlice = 0;
910 CopyAttribs.DstSlice = DstSlice;
894 CopyAttribs.SrcSlice = 0;
895 CopyAttribs.DstSlice = DstSlice;
896
897 if (Textures[i].pAtlasSuballocation)
898 {
899 const auto& Origin = Textures[i].pAtlasSuballocation->GetOrigin();
900 CopyAttribs.DstX = Origin.x;
901 CopyAttribs.DstY = Origin.y;
902 }
903 const auto& DstTexDesc = pTexture->GetDesc();
904 auto NumMipLevels = std::min(DstTexDesc.MipLevels, TexData.pStagingTex->GetDesc().MipLevels);
905 for (Uint32 mip = 0; mip < NumMipLevels; ++mip)
906 {
911907 CopyAttribs.SrcMipLevel = mip;
912908 CopyAttribs.DstMipLevel = mip;
913909 pCtx->CopyTexture(CopyAttribs);
910 CopyAttribs.DstX /= 2;
911 CopyAttribs.DstY /= 2;
914912 }
915913 }
916914 else
922920
923921 auto UpdateBuffer = [&](BUFFER_ID BuffId, const void* pData, size_t Size) //
924922 {
925 auto* pBuffer = GetBuffer(BuffId, pDevice, pCtx);
926 VERIFY_EXPR(pBuffer != nullptr);
927 auto Offset = Buffers[BuffId].pCacheAllocation ? Buffers[BuffId].pCacheAllocation->GetRegion().UnalignedOffset : 0;
928 pCtx->UpdateBuffer(pBuffer, static_cast<Uint32>(Offset), static_cast<Uint32>(Size), pData, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
923 auto& BuffInfo = Buffers[BuffId];
924 IBuffer* pBuffer = nullptr;
925 Uint32 Offset = 0;
926 if (BuffInfo.pSuballocation)
927 {
928 pBuffer = BuffInfo.pSuballocation->GetAllocator()->GetBuffer(pDevice, pCtx);
929 Offset = BuffInfo.pSuballocation->GetOffset();
930 }
931 else
932 {
933 pBuffer = BuffInfo.pBuffer;
934 }
935
936 pCtx->UpdateBuffer(pBuffer, Offset, static_cast<Uint32>(Size), pData, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
929937 if (Buffers[BuffId].pBuffer != nullptr)
930938 {
931939 VERIFY_EXPR(Buffers[BuffId].pBuffer == pBuffer);
11621170 auto TexIndex = Mat.TextureIds[Param.TextureId];
11631171 if (TexIndex >= 0)
11641172 {
1165 Param.UVScaleBias = GetUVScaleBias(TexIndex);
1166 Param.Slice = GetSlice(TexIndex);
1173 const auto& TexInfo = Textures[TexIndex];
1174 if (TexInfo.pAtlasSuballocation)
1175 {
1176 Param.UVScaleBias = TexInfo.pAtlasSuballocation->GetUVScaleBias();
1177 Param.Slice = static_cast<float>(TexInfo.pAtlasSuballocation->GetSlice());
1178 }
11671179 }
11681180 }
11691181
13201332
13211333 struct ImageLoaderData
13221334 {
1323 using TexAllocationsVector = std::vector<RefCntAutoPtr<GLTFResourceManager::TextureAllocation>>;
1335 using TexAllocationsVector = std::vector<RefCntAutoPtr<ITextureAtlasSuballocation>>;
13241336 using TexturesVector = std::vector<RefCntAutoPtr<ITexture>>;
13251337
13261338 Model::TextureCacheType* const pTextureCache;
13271339 TexturesVector* const pTextureHold;
1328 GLTFResourceManager* const pResourceMgr;
1340 ResourceManager* const pResourceMgr;
13291341 TexAllocationsVector* const pTextureAllocationsHold;
13301342
13311343 std::string BaseDir;
13481360 if (pLoaderData != nullptr)
13491361 {
13501362 // TODO: simplify path
1351 auto CacheId = pLoaderData->BaseDir + gltf_image->uri;
1363 auto CacheId = !gltf_image->uri.empty() ? pLoaderData->BaseDir + gltf_image->uri : "";
13521364
13531365 if (pLoaderData->pResourceMgr != nullptr)
13541366 {
13551367 if (auto pAllocation = pLoaderData->pResourceMgr->FindAllocation(CacheId.c_str()))
13561368 {
1357 const auto& TexDesc = pAllocation->GetTexDesc();
1369 const auto& TexDesc = pAllocation->GetAtlas()->GetAtlasDesc();
13581370 const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format);
1359
1360 gltf_image->width = pAllocation->GetWidth();
1361 gltf_image->height = pAllocation->GetHeight();
1371 const auto Size = pAllocation->GetSize();
1372
1373 gltf_image->width = Size.x;
1374 gltf_image->height = Size.y;
13621375 gltf_image->component = FmtAttribs.NumComponents;
13631376 gltf_image->bits = FmtAttribs.ComponentSize * 8;
13641377 gltf_image->pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
16591672 auto BufferSize = static_cast<Uint32>(VertexData0.size() * sizeof(VertexData0[0]));
16601673 if (CacheInfo.pResourceMgr != nullptr)
16611674 {
1662 Buffers[BUFFER_ID_VERTEX0].pCacheAllocation = CacheInfo.pResourceMgr->AllocateBufferSpace(CacheInfo.VertexBuffer0Idx, BufferSize, 1);
1675 Buffers[BUFFER_ID_VERTEX0].pSuballocation = CacheInfo.pResourceMgr->AllocateBufferSpace(CacheInfo.VertexBuffer0Idx, BufferSize, 1);
16631676 }
16641677 else
16651678 {
16831696 auto BufferSize = static_cast<Uint32>(VertexData1.size() * sizeof(VertexData1[0]));
16841697 if (CacheInfo.pResourceMgr != nullptr)
16851698 {
1686 Buffers[BUFFER_ID_VERTEX1].pCacheAllocation = CacheInfo.pResourceMgr->AllocateBufferSpace(CacheInfo.VertexBuffer1Idx, BufferSize, 1);
1699 Buffers[BUFFER_ID_VERTEX1].pSuballocation = CacheInfo.pResourceMgr->AllocateBufferSpace(CacheInfo.VertexBuffer1Idx, BufferSize, 1);
16871700 }
16881701 else
16891702 {
17081721 auto BufferSize = static_cast<Uint32>(IndexBuffer.size() * sizeof(IndexBuffer[0]));
17091722 if (CacheInfo.pResourceMgr != nullptr)
17101723 {
1711 Buffers[BUFFER_ID_INDEX].pCacheAllocation = CacheInfo.pResourceMgr->AllocateBufferSpace(CacheInfo.IndexBufferIdx, BufferSize, 1);
1724 Buffers[BUFFER_ID_INDEX].pSuballocation = CacheInfo.pResourceMgr->AllocateBufferSpace(CacheInfo.IndexBufferIdx, BufferSize, 1);
17121725 }
17131726 else
17141727 {
2727 #include "GLTFResourceManager.hpp"
2828 #include "DefaultRawMemoryAllocator.hpp"
2929 #include "Align.hpp"
30 #include "GraphicsAccessories.hpp"
3031
3132 namespace Diligent
3233 {
3334
34 GLTFResourceManager::BufferCache::BufferCache(GLTFResourceManager& Owner,
35 IRenderDevice* pDevice,
36 const BufferDesc& BuffDesc) :
37 m_Owner{Owner},
38 m_Mgr{BuffDesc.uiSizeInBytes, DefaultRawMemoryAllocator::GetAllocator()}
35 namespace GLTF
3936 {
40 pDevice->CreateBuffer(BuffDesc, nullptr, &m_pBuffer);
37
38 RefCntAutoPtr<ResourceManager> ResourceManager::Create(IRenderDevice* pDevice,
39 const CreateInfo& CI)
40 {
41 return RefCntAutoPtr<ResourceManager>{MakeNewRCObj<ResourceManager>()(pDevice, CI)};
4142 }
4243
43 IBuffer* GLTFResourceManager::BufferCache::GetBuffer(IRenderDevice* pDevice, IDeviceContext* pContext)
44 ResourceManager::ResourceManager(IReferenceCounters* pRefCounters,
45 IRenderDevice* pDevice,
46 const CreateInfo& CI) :
47 TBase{pRefCounters},
48 m_DefaultAtlasDesc{CI.DefaultAtlasDesc},
49 m_DefaultAtlasName{CI.DefaultAtlasDesc.Desc.Name != nullptr ? CI.DefaultAtlasDesc.Desc.Name : "GLTF texture atlas"}
4450 {
45 std::lock_guard<std::mutex> Lock{m_Mtx};
46
47 const auto& BuffDesc = m_pBuffer->GetDesc();
48 const auto MgrSize = m_Mgr.GetMaxSize();
49 if (BuffDesc.uiSizeInBytes < MgrSize)
51 m_DefaultAtlasDesc.Desc.Name = m_DefaultAtlasName.c_str();
52 m_BufferSuballocators.resize(CI.NumBuffSuballocators);
53 for (Uint32 i = 0; i < CI.NumBuffSuballocators; ++i)
5054 {
51 // Extend the buffer
52 auto NewBuffDesc = BuffDesc;
53
54 NewBuffDesc.uiSizeInBytes = static_cast<Uint32>(MgrSize);
55
56 RefCntAutoPtr<IBuffer> pNewBuffer;
57 pDevice->CreateBuffer(NewBuffDesc, nullptr, &pNewBuffer);
58 pContext->CopyBuffer(m_pBuffer, 0, RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
59 pNewBuffer, 0, BuffDesc.uiSizeInBytes, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
60
61 m_pBuffer = std::move(pNewBuffer);
55 CreateBufferSuballocator(pDevice, CI.BuffSuballocators[i], &m_BufferSuballocators[i]);
6256 }
6357
64 return m_pBuffer.RawPtr();
65 }
66
67 RefCntAutoPtr<GLTFResourceManager::BufferAllocation> GLTFResourceManager::BufferCache::Allocate(Uint32 Size, Uint32 Alignment)
68 {
69 VERIFY_EXPR(Size > 0 && IsPowerOfTwo(Alignment));
70
71 std::lock_guard<std::mutex> Lock{m_Mtx};
72
73 auto Region = m_Mgr.Allocate(Size, Alignment);
74 while (!Region.IsValid())
58 m_Atlases.reserve(CI.NumTexAtlases);
59 for (Uint32 i = 0; i < CI.NumTexAtlases; ++i)
7560 {
76 m_Mgr.Extend(m_Mgr.GetMaxSize());
77 Region = m_Mgr.Allocate(Size, Alignment);
78 }
79
80 return RefCntAutoPtr<BufferAllocation>{
81 MakeNewRCObj<BufferAllocation>()(
82 RefCntAutoPtr<GLTFResourceManager>{&m_Owner},
83 *this,
84 std::move(Region) //
85 ) //
86 };
87 }
88
89 GLTFResourceManager::TextureCache::TextureCache(GLTFResourceManager& Owner,
90 IRenderDevice* pDevice,
91 const TextureCacheAttribs& CacheCI) :
92 m_Owner{Owner},
93 m_Attribs{CacheCI}
94 {
95 m_Attribs.Desc.Name = m_TexName.c_str();
96
97 const auto& Desc = m_Attribs.Desc;
98 for (Uint32 slice = 0; slice < Desc.ArraySize; ++slice)
99 {
100 m_Slices.emplace_back(new SliceManager{Desc.Width / m_Attribs.Granularity, Desc.Height / m_Attribs.Granularity});
101 }
102
103 DEV_CHECK_ERR(IsPowerOfTwo(m_Attribs.Granularity), "Granularity (", m_Attribs.Granularity, ") must be a power of two");
104 DEV_CHECK_ERR((Desc.Width % m_Attribs.Granularity) == 0, "Atlas width (", Desc.Width, ") is not multiple of granularity (", m_Attribs.Granularity, ")");
105 DEV_CHECK_ERR((Desc.Height % m_Attribs.Granularity) == 0, "Atlas height (", Desc.Height, ") is not multiple of granularity (", m_Attribs.Granularity, ")");
106
107 if (pDevice != nullptr)
108 {
109 pDevice->CreateTexture(Desc, nullptr, &m_pTexture);
61 RefCntAutoPtr<IDynamicTextureAtlas> pAtlas;
62 CreateDynamicTextureAtlas(pDevice, CI.TexAtlases[i], &pAtlas);
63 m_Atlases.emplace(CI.TexAtlases[i].Desc.Format, std::move(pAtlas));
11064 }
11165 }
11266
67 RefCntAutoPtr<ITextureAtlasSuballocation> ResourceManager::FindAllocation(const char* CacheId)
68 {
69 RefCntAutoPtr<ITextureAtlasSuballocation> pAllocation;
11370
114 ITexture* GLTFResourceManager::TextureCache::GetTexture(IRenderDevice* pDevice, IDeviceContext* pContext)
115 {
116 RefCntAutoPtr<ITexture> pNewTexture;
117 {
118 std::lock_guard<std::mutex> Lock{m_SlicesMtx};
119 if (!m_pTexture || m_pTexture->GetDesc().ArraySize < m_Slices.size())
120 {
121 m_Attribs.Desc.ArraySize = static_cast<Uint32>(m_Slices.size());
122 VERIFY_EXPR(pDevice != nullptr);
123 pDevice->CreateTexture(m_Attribs.Desc, nullptr, &pNewTexture);
124 }
125 if (pNewTexture)
126 {
127 if (m_pTexture)
128 {
129 const auto& OldDesc = m_pTexture->GetDesc();
71 std::lock_guard<std::mutex> Lock{m_TexAllocationsMtx};
13072
131 CopyTextureAttribs CopyAttribs;
132 CopyAttribs.pSrcTexture = m_pTexture;
133 CopyAttribs.pDstTexture = pNewTexture;
134 CopyAttribs.SrcTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
135 CopyAttribs.DstTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
136
137 for (Uint32 slice = 0; slice < OldDesc.ArraySize; ++slice)
138 {
139 for (Uint32 mip = 0; mip < OldDesc.MipLevels; ++mip)
140 {
141 CopyAttribs.SrcSlice = slice;
142 CopyAttribs.DstSlice = slice;
143 CopyAttribs.SrcMipLevel = mip;
144 CopyAttribs.DstMipLevel = mip;
145 pContext->CopyTexture(CopyAttribs);
146 }
147 }
148 }
149 m_pTexture = std::move(pNewTexture);
150 m_Owner.m_ResourceVersion.fetch_add(1);
151 }
152 }
153
154 return m_pTexture;
155 }
156
157 RefCntAutoPtr<GLTFResourceManager::TextureAllocation> GLTFResourceManager::TextureCache::Allocate(Uint32 Width, Uint32 Height)
158 {
159 VERIFY_EXPR(Width > 0 && Height > 0);
160 const auto& TexDesc = m_Attribs.Desc;
161 if (Width > TexDesc.Width || Height > TexDesc.Height)
162 {
163 LOG_ERROR_MESSAGE("Requested size ", Width, " x ", Height, " exceeds the texture size ", TexDesc.Width, " x ", TexDesc.Height);
164 return {};
165 }
166
167 const auto Granularity = m_Attribs.Granularity;
168 for (Uint32 Slice = 0; Slice < m_Attribs.MaxSlices; ++Slice)
169 {
170 SliceManager* pSliceMgr = nullptr;
171 {
172 std::lock_guard<std::mutex> Lock{m_SlicesMtx};
173 if (Slice == m_Slices.size())
174 {
175 for (Uint32 ExtraSlice = 0; ExtraSlice < m_Attribs.ExtraSliceCount && Slice + ExtraSlice < m_Attribs.MaxSlices; ++ExtraSlice)
176 {
177 m_Slices.emplace_back(new SliceManager{TexDesc.Width / Granularity, TexDesc.Height / Granularity});
178 }
179 }
180 pSliceMgr = m_Slices[Slice].get();
181 }
182
183 auto Region = pSliceMgr->Allocate((Width + Granularity - 1) / Granularity, (Height + Granularity - 1) / Granularity);
184 if (!Region.IsEmpty())
185 {
186 Region.x *= Granularity;
187 Region.y *= Granularity;
188 Region.width *= Granularity;
189 Region.height *= Granularity;
190
191 return RefCntAutoPtr<TextureAllocation>{
192 MakeNewRCObj<TextureAllocation>()(
193 RefCntAutoPtr<GLTFResourceManager>{&m_Owner},
194 *this,
195 Width,
196 Height,
197 Slice,
198 std::move(Region) //
199 ) //
200 };
201 }
202 }
203
204 return RefCntAutoPtr<TextureAllocation>{};
205 }
206
207 void GLTFResourceManager::TextureCache::FreeAllocation(Uint32 Slice, DynamicAtlasManager::Region&& Allocation)
208 {
209 const auto Granularity = m_Attribs.Granularity;
210 VERIFY((Allocation.x % Granularity) == 0, "Allocation x (", Allocation.x, ") is not multiple of granularity (", Granularity, ")");
211 VERIFY((Allocation.y % Granularity) == 0, "Allocation y (", Allocation.y, ") is not multiple of granularity (", Granularity, ")");
212 VERIFY((Allocation.width % Granularity) == 0, "Allocation width (", Allocation.width, ") is not multiple of granularity (", Granularity, ")");
213 VERIFY((Allocation.height % Granularity) == 0, "Allocation height (", Allocation.height, ") is not multiple of granularity (", Granularity, ")");
214
215 Allocation.x /= Granularity;
216 Allocation.y /= Granularity;
217 Allocation.width /= Granularity;
218 Allocation.height /= Granularity;
219
220 SliceManager* pSliceMgr = nullptr;
221 {
222 std::lock_guard<std::mutex> Lock{m_SlicesMtx};
223 pSliceMgr = m_Slices[Slice].get();
224 }
225 pSliceMgr->Free(std::move(Allocation));
226 }
227
228
229
230 RefCntAutoPtr<GLTFResourceManager> GLTFResourceManager::Create(IRenderDevice* pDevice,
231 const CreateInfo& CI)
232 {
233 return RefCntAutoPtr<GLTFResourceManager>{MakeNewRCObj<GLTFResourceManager>()(pDevice, CI)};
234 }
235
236 GLTFResourceManager::GLTFResourceManager(IReferenceCounters* pRefCounters,
237 IRenderDevice* pDevice,
238 const CreateInfo& CI) :
239 TBase{pRefCounters},
240 m_DefaultTexDesc{CI.DefaultTexDesc},
241 m_DefaultExtraSliceCount{CI.DefaultExtraSliceCount}
242 {
243 m_Buffers.reserve(CI.NumBuffers);
244 for (Uint32 i = 0; i < CI.NumBuffers; ++i)
245 {
246 m_Buffers.emplace_back(*this, pDevice, CI.Buffers[i]);
247 }
248
249 m_Textures.reserve(CI.NumTextures);
250 for (Uint32 i = 0; i < CI.NumTextures; ++i)
251 {
252 m_Textures.emplace(CI.Textures[i].Desc.Format, TextureCache{*this, pDevice, CI.Textures[i]});
253 }
254 }
255
256 RefCntAutoPtr<GLTFResourceManager::TextureAllocation> GLTFResourceManager::FindAllocation(const char* CacheId)
257 {
258 RefCntAutoPtr<TextureAllocation> pAllocation;
259
260 std::lock_guard<std::mutex> Lock{m_AllocationsMtx};
261
262 auto it = m_Allocations.find(CacheId);
263 if (it != m_Allocations.end())
73 auto it = m_TexAllocations.find(CacheId);
74 if (it != m_TexAllocations.end())
26475 {
26576 pAllocation = it->second.Lock();
26677 if (!pAllocation)
267 m_Allocations.erase(it);
78 m_TexAllocations.erase(it);
26879 }
26980
27081 return pAllocation;
27182 }
27283
273 RefCntAutoPtr<GLTFResourceManager::TextureAllocation> GLTFResourceManager::AllocateTextureSpace(
84 RefCntAutoPtr<ITextureAtlasSuballocation> ResourceManager::AllocateTextureSpace(
27485 TEXTURE_FORMAT Fmt,
27586 Uint32 Width,
27687 Uint32 Height,
27788 const char* CacheId)
27889 {
279 RefCntAutoPtr<TextureAllocation> pAllocation;
90 RefCntAutoPtr<ITextureAtlasSuballocation> pAllocation;
28091 if (CacheId != nullptr && *CacheId != 0)
28192 {
28293 pAllocation = FindAllocation(CacheId);
28495
28596 if (!pAllocation)
28697 {
287 decltype(m_Textures)::iterator cache_it; // NB: can't initialize it without locking the mutex
98 decltype(m_Atlases)::iterator cache_it; // NB: can't initialize it without locking the mutex
28899 {
289 std::lock_guard<std::mutex> Lock{m_TexturesMtx};
290 cache_it = m_Textures.find(Fmt);
291 if (cache_it == m_Textures.end())
100 std::lock_guard<std::mutex> Lock{m_AtlasesMtx};
101 cache_it = m_Atlases.find(Fmt);
102 if (cache_it == m_Atlases.end())
292103 {
293 DEV_CHECK_ERR(m_DefaultTexDesc.Width > 0 && m_DefaultTexDesc.Height > 0, "Default texture description is not initialized");
294 TextureCacheAttribs NewCacheAttribs;
295 NewCacheAttribs.Desc = m_DefaultTexDesc;
104 DEV_CHECK_ERR(m_DefaultAtlasDesc.Desc.Width > 0 &&
105 m_DefaultAtlasDesc.Desc.Height > 0 &&
106 m_DefaultAtlasDesc.Desc.Type != RESOURCE_DIM_UNDEFINED,
107 "Default texture description is not initialized");
296108
297 NewCacheAttribs.Desc.Name = "GLTF Texture cache";
298 NewCacheAttribs.Desc.Format = Fmt;
109 auto AtalsCreateInfo = m_DefaultAtlasDesc;
110 AtalsCreateInfo.Desc.Format = Fmt;
299111
300 cache_it = m_Textures.emplace(Fmt, TextureCache{*this, nullptr, NewCacheAttribs}).first;
112 RefCntAutoPtr<IDynamicTextureAtlas> pAtlas;
113 CreateDynamicTextureAtlas(nullptr, AtalsCreateInfo, &pAtlas);
114 DEV_CHECK_ERR(pAtlas, "Failed to create new texture atlas");
115
116 cache_it = m_Atlases.emplace(Fmt, std::move(pAtlas)).first;
301117 }
302118 }
303119 // Allocate outside of mutex
304 pAllocation = cache_it->second.Allocate(Width, Height);
120 cache_it->second->Allocate(Width, Height, &pAllocation);
305121 }
306122
307123 if (CacheId != nullptr && *CacheId != 0)
308124 {
309 auto inserted = m_Allocations.emplace(CacheId, pAllocation).second;
125 auto inserted = m_TexAllocations.emplace(CacheId, pAllocation).second;
310126 VERIFY_EXPR(inserted);
311127 }
312128
313129 return pAllocation;
314130 }
315131
132 } // namespace GLTF
133
316134 } // namespace Diligent