• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "render_data_store_default_material.h"
17 
18 #include <algorithm>
19 #include <cstdint>
20 
21 #include <3d/render/intf_render_data_store_default_material.h>
22 #include <base/containers/array_view.h>
23 #include <base/math/float_packer.h>
24 #include <core/log.h>
25 #include <render/device/intf_gpu_resource_manager.h>
26 #include <render/device/intf_shader_manager.h>
27 #include <render/intf_render_context.h>
28 #include <render/resource_handle.h>
29 
30 #include "render_data_store_default_material.h"
31 
32 CORE3D_BEGIN_NAMESPACE()
33 using namespace BASE_NS;
34 using namespace RENDER_NS;
35 
36 using RENDER_NS::IShaderManager;
37 using RENDER_NS::RenderHandleReference;
38 
39 namespace {
40 // for packing defines
41 #include <3d/shaders/common/3d_dm_structures_common.h>
42 } // namespace
43 namespace {
44 constexpr uint32_t SHADER_DEFAULT_RENDER_SLOT_COUNT { 3u };
45 
46 constexpr size_t MEMORY_ALIGNMENT { 64 };
47 
48 static constexpr uint32_t MATERIAL_TYPE_SHIFT { 28u };
49 static constexpr uint32_t MATERIAL_TYPE_MASK { 0xF0000000u };
50 static constexpr uint32_t MATERIAL_FLAGS_SHIFT { 8u };
51 static constexpr uint32_t MATERIAL_FLAGS_MASK { 0x0FFFff00u };
52 static constexpr uint32_t SUBMESH_FLAGS_MASK { 0x00000ffu };
53 
54 static constexpr uint32_t COMBINED_GPU_INSTANCING_REMOVAL { ~(
55     RenderMaterialFlagBits::RENDER_MATERIAL_GPU_INSTANCING_BIT |
56     RenderMaterialFlagBits::RENDER_MATERIAL_GPU_INSTANCING_MATERIAL_BIT) };
57 
58 static constexpr RenderDataDefaultMaterial::AllMaterialUniforms INIT_ALL_MATERIAL_UNIFORMS {};
59 
HashCombine32Bit(uint32_t & seed,const uint32_t v)60 inline void HashCombine32Bit(uint32_t& seed, const uint32_t v)
61 {
62     constexpr const uint32_t goldenRatio = 0x9e3779b9;
63     constexpr const uint32_t rotl = 6U;
64     constexpr const uint32_t rotr = 2U;
65     seed ^= hash(v) + goldenRatio + (seed << rotl) + (seed >> rotr);
66 }
67 
ConvertMaterialHandleReferences(const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & inputHandles,vector<RenderHandleReference> & references)68 inline constexpr RenderDataDefaultMaterial::MaterialHandles ConvertMaterialHandleReferences(
69     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& inputHandles,
70     vector<RenderHandleReference>& references)
71 {
72     RenderDataDefaultMaterial::MaterialHandles mh;
73     for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT; ++idx) {
74         if (inputHandles.images[idx]) {
75             mh.images[idx] = inputHandles.images[idx].GetHandle();
76             references.push_back(inputHandles.images[idx]);
77         }
78         if (inputHandles.samplers[idx]) {
79             mh.samplers[idx] = inputHandles.samplers[idx].GetHandle();
80             references.push_back(inputHandles.samplers[idx]);
81         }
82     }
83     return mh;
84 }
85 
GetRenderSortLayerHash(const RenderSubmesh & submesh)86 inline constexpr uint16_t GetRenderSortLayerHash(const RenderSubmesh& submesh)
87 {
88     // if submesh layers are defaults -> use material sort values (the defaults are the same)
89     if ((submesh.layers.meshRenderSortLayer == RenderSceneDataConstants::DEFAULT_RENDER_SORT_LAYER_ID) &&
90         (submesh.layers.meshRenderSortLayerOrder == 0)) {
91         return (static_cast<uint16_t>(submesh.layers.materialRenderSortLayer) << 8u) |
92                (static_cast<uint16_t>(submesh.layers.materialRenderSortLayerOrder) & 0xffu);
93     } else {
94         return (static_cast<uint16_t>(submesh.layers.meshRenderSortLayer) << 8u) |
95                (static_cast<uint16_t>(submesh.layers.meshRenderSortLayerOrder) & 0xffu);
96     }
97 }
98 
ConvertRenderSubmeshInput(const RenderSubmeshWithHandleReference & input,vector<RenderHandleReference> & references)99 RenderSubmesh ConvertRenderSubmeshInput(
100     const RenderSubmeshWithHandleReference& input, vector<RenderHandleReference>& references)
101 {
102     RenderSubmeshBuffers rsb;
103     const auto& ib = input.buffers.indexBuffer;
104     if (ib.bufferHandle) {
105         references.push_back(ib.bufferHandle);
106         rsb.indexBuffer = { ib.bufferHandle.GetHandle(), ib.bufferOffset, ib.byteSize, ib.indexType };
107     }
108     const auto& iargs = input.buffers.indirectArgsBuffer;
109     if (iargs.bufferHandle) {
110         references.push_back(iargs.bufferHandle);
111         rsb.indirectArgsBuffer = { iargs.bufferHandle.GetHandle(), iargs.bufferOffset, iargs.byteSize };
112     }
113     const RenderHandle safetyBuffer = input.buffers.vertexBuffers[0U].bufferHandle.GetHandle();
114     RenderHandle prevHandle {};
115     for (uint32_t idx = 0; idx < RENDER_NS::PipelineStateConstants::MAX_VERTEX_BUFFER_COUNT; ++idx) {
116         const auto& vb = input.buffers.vertexBuffers[idx];
117         // add safety handles to invalid buffers
118         if (vb.bufferHandle) {
119             rsb.vertexBuffers[idx] = { vb.bufferHandle.GetHandle(), vb.bufferOffset, vb.byteSize };
120             // often we have the same buffer (GPU resource)
121             if (rsb.vertexBuffers[idx].bufferHandle != prevHandle) {
122                 references.push_back(vb.bufferHandle);
123             }
124         } else {
125             rsb.vertexBuffers[idx] = { safetyBuffer, 0U, 0U };
126         }
127         prevHandle = rsb.vertexBuffers[idx].bufferHandle;
128     }
129     // NOTE: we will get max amount of vertex buffers if there is at least one
130     rsb.vertexBufferCount = RenderHandleUtil::IsValid(rsb.vertexBuffers[0U].bufferHandle)
131                                 ? RENDER_NS::PipelineStateConstants::MAX_VERTEX_BUFFER_COUNT
132                                 : 0U;
133     rsb.inputAssembly = input.buffers.inputAssembly;
134 
135     return RenderSubmesh { input.submeshFlags, input.renderSubmeshMaterialFlags, input.indices, input.layers,
136         input.bounds, input.drawCommand, move(rsb) };
137 }
138 
139 #if (CORE3D_VALIDATION_ENABLED == 1)
ValidateSubmesh(const RenderSubmeshWithHandleReference & submesh)140 void ValidateSubmesh(const RenderSubmeshWithHandleReference& submesh)
141 {
142     if (((submesh.submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_SKIN_BIT) == 0) &&
143         submesh.indices.skinJointIndex != RenderSceneDataConstants::INVALID_INDEX) {
144         CORE_LOG_W("CORE3D_VALIDATION: skin bit is not set for submesh flags");
145     }
146 }
147 #endif
148 
PackMaterialUVec(const Math::Vec4 & up0,const Math::Vec4 & up1)149 inline Math::UVec4 PackMaterialUVec(const Math::Vec4& up0, const Math::Vec4& up1)
150 {
151     return Math::UVec4 {
152         Math::PackHalf2X16({ up0.x, up0.y }),
153         Math::PackHalf2X16({ up0.z, up0.w }),
154         Math::PackHalf2X16({ up1.x, up1.y }),
155         Math::PackHalf2X16({ up1.z, up1.w }),
156     };
157 }
158 
ExtentRenderMaterialFlagsFromSubmeshValues(const RenderSubmeshFlags & submeshFlags,RenderMaterialFlags & rmf)159 inline void ExtentRenderMaterialFlagsFromSubmeshValues(const RenderSubmeshFlags& submeshFlags, RenderMaterialFlags& rmf)
160 {
161     // if there are normal maps and there are tangets, we allow the normal map flag
162     if ((rmf & RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT) &&
163         (submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_TANGENTS_BIT)) {
164         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT;
165     } else {
166         // remove flag if there were only normal maps but no tangents
167         rmf &= (~RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT);
168     }
169 }
170 
ExtentRenderMaterialFlagsForComplexity(const RenderMaterialType materialType,RenderMaterialFlags & rmf)171 void ExtentRenderMaterialFlagsForComplexity(const RenderMaterialType materialType, RenderMaterialFlags& rmf)
172 {
173     constexpr RenderMaterialFlags complexMask { RENDER_MATERIAL_CLEAR_COAT_BIT | RENDER_MATERIAL_TRANSMISSION_BIT |
174                                                 RENDER_MATERIAL_SHEEN_BIT | RENDER_MATERIAL_SPECULAR_BIT };
175     if ((materialType == RenderMaterialType::CUSTOM) || (materialType == RenderMaterialType::CUSTOM_COMPLEX)) {
176         rmf |= (materialType == RenderMaterialType::CUSTOM_COMPLEX) ? RENDER_MATERIAL_COMPLEX_BIT
177                                                                     : RENDER_MATERIAL_BASIC_BIT;
178         if (materialType < RenderMaterialType::CUSTOM) {
179             rmf |= ((rmf & complexMask) || (materialType == RenderMaterialType::SPECULAR_GLOSSINESS))
180                        ? RENDER_MATERIAL_COMPLEX_BIT
181                        : RENDER_MATERIAL_BASIC_BIT;
182         }
183     } else {
184         rmf |= ((rmf & complexMask) > 0) ? RENDER_MATERIAL_COMPLEX_BIT : RENDER_MATERIAL_BASIC_BIT;
185     }
186 }
187 
HashSubmeshMaterials(const RenderMaterialType materialType,const RenderMaterialFlags materialFlags,const RenderSubmeshFlags submeshFlags)188 inline constexpr uint32_t HashSubmeshMaterials(const RenderMaterialType materialType,
189     const RenderMaterialFlags materialFlags, const RenderSubmeshFlags submeshFlags)
190 {
191     return (((uint32_t)materialType << MATERIAL_TYPE_SHIFT) & MATERIAL_TYPE_MASK) |
192            ((materialFlags << MATERIAL_FLAGS_SHIFT) & MATERIAL_FLAGS_MASK) | (submeshFlags & SUBMESH_FLAGS_MASK);
193 }
194 
PatchRenderMaterialSortLayers(const RenderDataDefaultMaterial::MaterialData & matData,RenderSubmeshLayers & layers)195 inline constexpr void PatchRenderMaterialSortLayers(
196     const RenderDataDefaultMaterial::MaterialData& matData, RenderSubmeshLayers& layers)
197 {
198     // batch render material sort layers if default
199     if (layers.materialRenderSortLayer == RenderSceneDataConstants::DEFAULT_RENDER_SORT_LAYER_ID) {
200         layers.materialRenderSortLayer = matData.renderSortLayer;
201     }
202     if (layers.materialRenderSortLayerOrder == 0U) {
203         layers.materialRenderSortLayerOrder = matData.renderSortLayerOrder;
204     }
205 }
206 
HashMaterialId(const uint64_t id,const uint32_t instanceCount)207 inline uint64_t HashMaterialId(const uint64_t id, const uint32_t instanceCount)
208 {
209     return Hash(id, static_cast<uint64_t>(instanceCount));
210 }
211 
AllocateMatrixMemory(RenderDataStoreDefaultMaterial::LinearAllocatorStruct & allocator,const size_t byteSize)212 void* AllocateMatrixMemory(RenderDataStoreDefaultMaterial::LinearAllocatorStruct& allocator, const size_t byteSize)
213 {
214     void* data = nullptr;
215     if (!allocator.allocators.empty()) {
216         data = allocator.allocators[allocator.currentIndex]->Allocate(byteSize);
217     }
218 
219     if (data) {
220         return data;
221     } else { // current allocator is out of memory
222         allocator.allocators.push_back(make_unique<LinearAllocator>(byteSize, MEMORY_ALIGNMENT));
223         allocator.currentIndex = (uint32_t)(allocator.allocators.size() - 1);
224         data = allocator.allocators[allocator.currentIndex]->Allocate(byteSize);
225         CORE_ASSERT_MSG(data, "render data store default material allocation : out of memory");
226         return data;
227     }
228 }
229 
AllocateMatrices(RenderDataStoreDefaultMaterial::LinearAllocatorStruct & allocator,const size_t count)230 Math::Mat4X4* AllocateMatrices(RenderDataStoreDefaultMaterial::LinearAllocatorStruct& allocator, const size_t count)
231 {
232     const size_t byteSize = count * sizeof(Math::Mat4X4);
233     return static_cast<Math::Mat4X4*>(AllocateMatrixMemory(allocator, byteSize));
234 }
235 
MaterialUniformsPackedFromInput(const RenderDataDefaultMaterial::InputMaterialUniforms & input)236 RenderDataDefaultMaterial::AllMaterialUniforms MaterialUniformsPackedFromInput(
237     const RenderDataDefaultMaterial::InputMaterialUniforms& input)
238 {
239     RenderDataDefaultMaterial::AllMaterialUniforms amu = INIT_ALL_MATERIAL_UNIFORMS;
240     // premultiplication needs to be handled already with some factors like baseColor
241     for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT; ++idx) {
242         amu.factors.factors[idx] = input.textureData[idx].factor;
243     }
244     uint32_t transformBits = 0;
245     {
246         constexpr auto supportedMaterials = RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT;
247         for (uint32_t i = 0; i < supportedMaterials; ++i) {
248             const auto& tex = input.textureData[i];
249 
250             /* TRS matrix for 2D transformations:
251              * { scaleX * cos(rot),  scaleY * sin(rot), transX }
252              * { scaleX * -sin(rot), scaleY * cos(rot), transY }
253              * { 0,                  0,                 1      }
254              */
255             const Math::Vec4 rotateScale =
256                 [](const RenderDataDefaultMaterial::InputMaterialUniforms::TextureData& data) {
257                     if (data.rotation == 0.f) {
258                         return Math::Vec4 { data.scale.x, 0.f, 0.f, data.scale.y };
259                     }
260                     const float sinR = Math::sin(data.rotation);
261                     const float cosR = Math::cos(data.rotation);
262                     return Math::Vec4 { data.scale.x * cosR, data.scale.y * sinR, data.scale.x * -sinR,
263                         data.scale.y * cosR };
264                 }(tex);
265 
266             // set transform bit for texture index
267             static constexpr const auto identityRs = Math::Vec4(1.f, 0.f, 0.f, 1.f);
268             const bool hasTransform =
269                 (tex.translation.x != 0.f) || (tex.translation.y != 0.f) || (rotateScale != identityRs);
270             if (!hasTransform) {
271                 // this matches packing identityRs.xy, identityRs.zw, translation.xy, and 0,0
272                 static constexpr const auto identity = Math::UVec4(0x3c000000, 0x00003c00, 0x0, 0x0);
273                 amu.transforms.packed[CORE_MATERIAL_PACK_TEX_BASE_COLOR_UV_IDX + i] = identity;
274             } else {
275                 // using PackMaterialUVec costs packing the last unused zeros
276                 amu.transforms.packed[CORE_MATERIAL_PACK_TEX_BASE_COLOR_UV_IDX + i] =
277                     Math::UVec4 { Math::PackHalf2X16({ rotateScale.x, rotateScale.y }),
278                         Math::PackHalf2X16({ rotateScale.z, rotateScale.w }),
279                         Math::PackHalf2X16({ tex.translation.x, tex.translation.y }), 0 };
280                 transformBits |= static_cast<uint32_t>(hasTransform) << i;
281             }
282         }
283     }
284     const uint32_t transformInfoBits = (input.texCoordSetBits << 16u) | transformBits; // 16: left remove 16 bits
285     const Math::UVec4 indices = { uint32_t(input.id >> 32u), uint32_t(input.id & 0xFFFFffff), 0u, 0u };
286 
287     // finalize factors
288     amu.factors.factors[RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT] = { input.alphaCutoff,
289         *reinterpret_cast<const float*>(&transformInfoBits), 0.0f, 0.0f };
290     amu.factors.indices = indices;
291     // finalize transforms
292     amu.transforms.packed[CORE_MATERIAL_PACK_ADDITIONAL_IDX] = { Math::PackHalf2X16({ input.alphaCutoff, 0.f }),
293         transformInfoBits, 0, 0 };
294     amu.transforms.indices = indices;
295     return amu;
296 }
297 
DestroyMaterialByIndex(const uint32_t index,RenderDataStoreDefaultMaterial::AllMaterialData & matData)298 void DestroyMaterialByIndex(const uint32_t index, RenderDataStoreDefaultMaterial::AllMaterialData& matData)
299 {
300     // explicit clear
301     if (index < matData.data.size()) {
302         matData.data[index] = {};
303         matData.allUniforms[index] = {};
304         matData.handles[index] = {};
305         matData.customPropertyData[index] = {};
306         matData.customResourceData[index] = {};
307 
308         // add for re-use
309         matData.availableIndices.push_back(index);
310     }
311 }
312 
GetCertainMaterialIndex(const uint32_t index,const RenderDataStoreDefaultMaterial::AllMaterialData & matData)313 uint32_t GetCertainMaterialIndex(const uint32_t index, const RenderDataStoreDefaultMaterial::AllMaterialData& matData)
314 {
315     if (index >= static_cast<uint32_t>(matData.data.size())) {
316         // built-in material in 0
317         CORE_ASSERT(!matData.data.empty());
318         if (!matData.data.empty()) {
319             return 0;
320         }
321     }
322     return index;
323 }
324 
GetDefaultInputMaterialUniforms()325 constexpr RenderDataDefaultMaterial::InputMaterialUniforms GetDefaultInputMaterialUniforms()
326 {
327     CORE_STATIC_ASSERT(RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT == 11U);
328 
329     // needs to match material component for default data
330     RenderDataDefaultMaterial::InputMaterialUniforms imu;
331     imu.textureData[0U].factor = { 1.f, 1.f, 1.f, 1.f };   // base color opaque white
332     imu.textureData[1U].factor = { 1.f, 0.f, 0.f, 0.f };   // normal scale 1
333     imu.textureData[2U].factor = { 0.f, 1.f, 1.f, 0.04f }; // material (empty, roughness, metallic, reflectance)
334     imu.textureData[3U].factor = { 0.f, 0.f, 0.f, 1.f };   // emissive 0
335     imu.textureData[4U].factor = { 1.f, 0.f, 0.f, 0.f };   // ambient occlusion 1
336     imu.textureData[5U].factor = { 0.f, 0.f, 0.f, 0.f };   // clearcoat intensity 0
337     imu.textureData[6U].factor = { 0.f, 0.f, 0.f, 0.f };   // clearcoat roughness 0
338     imu.textureData[7U].factor = { 1.f, 0.f, 0.f, 0.f };   // clearcoat normal scale 1
339     imu.textureData[8U].factor = { 0.f, 0.f, 0.f, 0.f };   // sheen color black, roughness 0
340     imu.textureData[9U].factor = { 0.f, 0.f, 0.f, 0.f };   // transmission 0
341     imu.textureData[10U].factor = { 1.f, 1.f, 1.f, 1.f };  // specular white
342 
343     imu.id = 0xFFFFffffU;
344 
345     return imu;
346 }
347 } // namespace
348 
RenderDataStoreDefaultMaterial(RENDER_NS::IRenderContext & renderContext,const string_view name)349 RenderDataStoreDefaultMaterial::RenderDataStoreDefaultMaterial(
350     RENDER_NS::IRenderContext& renderContext, const string_view name)
351     : name_(name), gpuResourceMgr_(renderContext.GetDevice().GetGpuResourceManager()),
352       shaderMgr_(renderContext.GetDevice().GetShaderManager())
353 {
354     GetDefaultRenderSlots();
355 
356     // add default materials (~0U and max ~0ULL)
357     const uint32_t materialIndex = AddMaterialDataImpl(~0U, GetDefaultInputMaterialUniforms(), {}, {}, {}, {});
358     CORE_ASSERT(materialIndex == 0);
359     matData_.materialIdToIndex.insert_or_assign(0xFFFFffff, materialIndex);
360     matData_.materialIdToIndex.insert_or_assign(0xFFFFFFFFffffffff, materialIndex);
361 }
362 
PostRender()363 void RenderDataStoreDefaultMaterial::PostRender()
364 {
365     Clear();
366 }
367 
Clear()368 void RenderDataStoreDefaultMaterial::Clear()
369 {
370     // NOTE: clear is at the moment called typically two times
371     // this could be further optimized to know if clear has already been called
372 
373     // release references
374     handleReferences_.clear();
375 
376     submeshes_.clear();
377     meshData_.clear();
378     submeshJointMatrixIndices_.clear();
379 
380     {
381         // NOTE: material data is not cleared automatically anymore
382         // we keep the data but update the resource references if data is used
383         // separate destroy
384 
385         matData_.frameIndices.clear();
386         matData_.idHashToFrameIndex.clear();
387 
388 #if (CORE3D_VALIDATION_ENABLED == 1)
389         vector<uint32_t> noIdRemoval;
390 #endif
391         // check that we have id of material (if not, then it's a single frame (usually) debug material
392         for (size_t idx = 0; idx < matData_.data.size(); ++idx) {
393             if (matData_.data[idx].noId) {
394                 DestroyMaterialByIndex(static_cast<uint32_t>(idx), matData_);
395 #if (CORE3D_VALIDATION_ENABLED == 1)
396                 // should not have id
397                 noIdRemoval.push_back(static_cast<uint32_t>(idx));
398 #endif
399             }
400         }
401 #if (CORE3D_VALIDATION_ENABLED == 1)
402         for (const auto& ref : matData_.materialIdToIndex) {
403             for (const auto& noIdRef : noIdRemoval) {
404                 if (ref.second == noIdRef) {
405                     CORE_LOG_E("CORE3D_VALIDATION: Material removal issue");
406                 }
407             }
408         }
409         noIdRemoval.clear();
410 #endif
411     }
412 
413     submeshMaterialFlags_.clear();
414 
415     for (auto& slotRef : slotToSubmeshIndices_) { // does not remove slots from use
416         slotRef.second.indices.clear();
417         slotRef.second.materialData.clear();
418         slotRef.second.objectCounts = {};
419     }
420 
421     if (!submeshJointMatricesAllocator_.allocators.empty()) {
422         submeshJointMatricesAllocator_.currentIndex = 0;
423         if (submeshJointMatricesAllocator_.allocators.size() == 1) { // size is good for this frame
424             submeshJointMatricesAllocator_.allocators[submeshJointMatricesAllocator_.currentIndex]->Reset();
425         } else if (submeshJointMatricesAllocator_.allocators.size() > 1) {
426             size_t fullByteSize = 0;
427             for (auto& ref : submeshJointMatricesAllocator_.allocators) {
428                 fullByteSize += ref->GetCurrentByteSize();
429                 ref.reset();
430             }
431             submeshJointMatricesAllocator_.allocators.clear();
432             // create new single allocation for combined previous size and some extra bytes
433             submeshJointMatricesAllocator_.allocators.push_back(
434                 make_unique<LinearAllocator>(fullByteSize, MEMORY_ALIGNMENT));
435         }
436     }
437 
438     // NOTE: re-fetch if default slots are invalid
439     if (materialRenderSlots_.opaqueMask != 0) {
440         GetDefaultRenderSlots();
441     }
442 }
443 
Ref()444 void RenderDataStoreDefaultMaterial::Ref()
445 {
446     refcnt_.fetch_add(1, std::memory_order_relaxed);
447 }
448 
Unref()449 void RenderDataStoreDefaultMaterial::Unref()
450 {
451     if (std::atomic_fetch_sub_explicit(&refcnt_, 1, std::memory_order_release) == 1) {
452         std::atomic_thread_fence(std::memory_order_acquire);
453         delete this;
454     }
455 }
456 
GetRefCount()457 int32_t RenderDataStoreDefaultMaterial::GetRefCount()
458 {
459     return refcnt_;
460 }
461 
DestroyMaterialData(const uint64_t id)462 void RenderDataStoreDefaultMaterial::DestroyMaterialData(const uint64_t id)
463 {
464     // explicit material destruction
465     // NOTE: does not clear resource handle references for this frame
466 
467     CORE_ASSERT(matData_.allUniforms.size() == matData_.data.size());
468     if (auto iter = matData_.materialIdToIndex.find(id); iter != matData_.materialIdToIndex.cend()) {
469         CORE_ASSERT(iter->second < matData_.allUniforms.size());
470         DestroyMaterialByIndex(iter->second, matData_);
471 
472         // destroy from material map
473         matData_.materialIdToIndex.erase(iter);
474     }
475 }
476 
DestroyMaterialData(const BASE_NS::array_view<uint64_t> ids)477 void RenderDataStoreDefaultMaterial::DestroyMaterialData(const BASE_NS::array_view<uint64_t> ids)
478 {
479     for (const auto& idRef : ids) {
480         DestroyMaterialData(idRef);
481     }
482 }
483 
GetDefaultRenderSlots()484 void RenderDataStoreDefaultMaterial::GetDefaultRenderSlots()
485 {
486     materialRenderSlots_.defaultOpaqueRenderSlot =
487         shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE);
488     materialRenderSlots_.opaqueMask = (1ull << uint64_t(materialRenderSlots_.defaultOpaqueRenderSlot));
489     materialRenderSlots_.opaqueMask |=
490         (1ull << uint64_t(shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEFERRED_OPAQUE)));
491 
492     materialRenderSlots_.defaultTranslucentRenderSlot =
493         shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT);
494     materialRenderSlots_.translucentMask = (1ull << uint64_t(materialRenderSlots_.defaultTranslucentRenderSlot));
495 
496     materialRenderSlots_.defaultDepthRenderSlot =
497         shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH);
498     materialRenderSlots_.depthMask = (1ull << uint64_t(materialRenderSlots_.defaultDepthRenderSlot));
499     materialRenderSlots_.depthMask |=
500         (1ull << uint64_t(shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH_VSM)));
501 }
502 
AddMaterialDataImpl(const uint32_t matIndex,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customData,const array_view<const RenderHandleReference> customResourceData)503 uint32_t RenderDataStoreDefaultMaterial::AddMaterialDataImpl(const uint32_t matIndex,
504     const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
505     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
506     const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customData,
507     const array_view<const RenderHandleReference> customResourceData)
508 {
509     uint32_t materialIndex = matIndex;
510     // matData_.frameIndices can have higher counts)
511     CORE_ASSERT(matData_.allUniforms.size() == matData_.data.size());
512     CORE_ASSERT(matData_.handles.size() == matData_.customPropertyData.size());
513     if ((materialIndex == ~0U) && (matData_.availableIndices.empty())) {
514         // totally new indices and material
515         materialIndex = static_cast<uint32_t>(matData_.allUniforms.size());
516         matData_.handles.push_back(ConvertMaterialHandleReferences(materialHandles, handleReferences_));
517         matData_.allUniforms.push_back(MaterialUniformsPackedFromInput(materialUniforms));
518         matData_.customPropertyData.push_back({});
519         matData_.customResourceData.push_back({});
520         matData_.data.push_back({});
521     } else {
522         if ((materialIndex == ~0U) && (!matData_.availableIndices.empty())) {
523             materialIndex = matData_.availableIndices.back();
524             matData_.availableIndices.pop_back();
525         }
526         if (materialIndex >= matData_.allUniforms.size()) {
527             CORE_ASSERT(true);
528             return ~0U;
529         }
530         matData_.handles[materialIndex] = ConvertMaterialHandleReferences(materialHandles, handleReferences_);
531         matData_.allUniforms[materialIndex] = MaterialUniformsPackedFromInput(materialUniforms);
532         matData_.customPropertyData[materialIndex].data.clear();
533         matData_.customResourceData[materialIndex] = {};
534     }
535 
536     // NOTE: when material is added/updated, we need to keep the handle reference
537     // the user might not have local references
538 
539     // not referenced yet this frame for rendering (false)
540     matData_.data[materialIndex].frameReferenced = false;
541     matData_.data[materialIndex].noId = false;
542     auto& currMaterialData = matData_.data[materialIndex].md;
543     currMaterialData = materialData;
544     ExtentRenderMaterialFlagsForComplexity(currMaterialData.materialType, currMaterialData.renderMaterialFlags);
545 
546     if (!customData.empty()) {
547         const auto maxByteSize = Math::min(static_cast<uint32_t>(customData.size_bytes()),
548             RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_PROPERTY_BYTE_SIZE);
549         auto& cpdRef = matData_.customPropertyData[materialIndex];
550         cpdRef.data.resize(maxByteSize);
551         CloneData(cpdRef.data.data(), maxByteSize, customData.data(), maxByteSize);
552     }
553 
554     if (!customResourceData.empty()) {
555         auto& dataRef = matData_.customResourceData[materialIndex];
556         dataRef.resourceHandleCount = 0U;
557         dataRef.shaderHandle = materialData.materialShader.shader.GetHandle();
558         const uint32_t maxCount = Math::min(static_cast<uint32_t>(customResourceData.size()),
559             RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT);
560         for (uint32_t idx = 0; idx < maxCount; ++idx) {
561             if (customResourceData[idx]) {
562                 // NOTE: when material is added/updated, we need to keep the handle reference
563                 // the user might not have local references
564                 handleReferences_.push_back(customResourceData[idx]);
565                 dataRef.resourceHandles[dataRef.resourceHandleCount++] = customResourceData[idx].GetHandle();
566             }
567         }
568     }
569 
570     return materialIndex;
571 }
572 
UpdateMaterialData(const uint64_t id,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customData,const array_view<const RenderHandleReference> customBindings)573 uint32_t RenderDataStoreDefaultMaterial::UpdateMaterialData(const uint64_t id,
574     const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
575     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
576     const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customData,
577     const array_view<const RenderHandleReference> customBindings)
578 {
579     CORE_STATIC_ASSERT(RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT == MaterialComponent::TEXTURE_COUNT);
580 
581     CORE_ASSERT(matData_.allUniforms.size() == matData_.data.size());
582     if (const auto iter = matData_.materialIdToIndex.find(id); iter != matData_.materialIdToIndex.cend()) {
583         CORE_ASSERT(iter->second < matData_.allUniforms.size());
584         const uint32_t materialIndex = AddMaterialDataImpl(
585             iter->second, materialUniforms, materialHandles, materialData, customData, customBindings);
586         CORE_UNUSED(materialIndex);
587         CORE_ASSERT(materialIndex == iter->second);
588         return iter->second;
589     } else {
590         // create new
591         const uint32_t materialIndex =
592             AddMaterialDataImpl(~0U, materialUniforms, materialHandles, materialData, customData, customBindings);
593         matData_.materialIdToIndex.insert_or_assign(id, materialIndex);
594         return materialIndex;
595     }
596 }
597 
UpdateMaterialData(const uint64_t id,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customData)598 uint32_t RenderDataStoreDefaultMaterial::UpdateMaterialData(const uint64_t id,
599     const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
600     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
601     const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customData)
602 {
603     return UpdateMaterialData(id, materialUniforms, materialHandles, materialData, customData, {});
604 }
605 
UpdateMaterialData(const uint64_t id,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData)606 uint32_t RenderDataStoreDefaultMaterial::UpdateMaterialData(const uint64_t id,
607     const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
608     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
609     const RenderDataDefaultMaterial::MaterialData& materialData)
610 {
611     return UpdateMaterialData(id, materialUniforms, materialHandles, materialData, {}, {});
612 }
613 
AddFrameMaterialData(const uint64_t id,const uint32_t instanceCount)614 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialData(
615     const uint64_t id, const uint32_t instanceCount)
616 {
617     // we expect the material to be in data
618 #if (CORE3D_VALIDATION_ENABLED == 1)
619     if (const auto iter = matData_.materialIdToIndex.find(id); iter == matData_.materialIdToIndex.cend()) {
620         const string name = string("AddFrameMaterialData" + BASE_NS::to_hex(id));
621         CORE_LOG_ONCE_W(name, "AddFrameMaterialData id not updated prior add");
622     }
623 #endif
624     // NOTE: we need to update the reference counts for rendering time
625     // material resource references are not kept alive when just updating the materials
626     // with this approach the resources can be destroyed during the application time
627 
628     const uint64_t searchId = HashMaterialId(id, static_cast<uint32_t>(instanceCount));
629     if (const auto iter = matData_.idHashToFrameIndex.find(searchId); iter != matData_.idHashToFrameIndex.cend()) {
630         if (iter->second < static_cast<uint32_t>(matData_.frameIndices.size())) {
631             // these have been already updated with reference counts, just return the indices
632             return { matData_.frameIndices[iter->second], iter->second };
633         }
634     } else if (const auto matIter = matData_.materialIdToIndex.find(id); matIter != matData_.materialIdToIndex.cend()) {
635         // append instance count amount
636         const uint32_t frameMaterialOffset = static_cast<uint32_t>(matData_.frameIndices.size());
637         matData_.idHashToFrameIndex.insert_or_assign(searchId, frameMaterialOffset);
638         RenderFrameMaterialIndices rfmi { matIter->second, frameMaterialOffset };
639         matData_.frameIndices.append(instanceCount, matIter->second); // add material index
640 
641         // update reference counts for this frame if needed
642         UpdateFrameMaterialResourceReferences(matIter->second);
643 
644         return rfmi;
645     }
646     return {};
647 }
648 
AddFrameMaterialData(const uint64_t id)649 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialData(const uint64_t id)
650 {
651     return AddFrameMaterialData(id, 1U);
652 }
653 
AddFrameMaterialData(const BASE_NS::array_view<const uint64_t> ids)654 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialData(
655     const BASE_NS::array_view<const uint64_t> ids)
656 {
657     if (!ids.empty()) {
658         RenderFrameMaterialIndices rfmi = AddFrameMaterialData(ids[0U], 1U);
659         for (size_t idx = 1; idx < ids.size(); ++idx) {
660             AddFrameMaterialData(ids[idx], 1U);
661         }
662         return rfmi;
663     } else {
664         return {};
665     }
666 }
667 
AddFrameMaterialData(const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const BASE_NS::array_view<const uint8_t> customPropertyData,const BASE_NS::array_view<const RENDER_NS::RenderHandleReference> customBindings)668 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialData(
669     const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
670     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
671     const RenderDataDefaultMaterial::MaterialData& materialData,
672     const BASE_NS::array_view<const uint8_t> customPropertyData,
673     const BASE_NS::array_view<const RENDER_NS::RenderHandleReference> customBindings)
674 {
675     // NOTE: this data is added as is
676     // cannot be retrieved with id or anything
677     // mostly used for some debug meshes etc.
678     const uint32_t materialIndex =
679         AddMaterialDataImpl(~0U, materialUniforms, materialHandles, materialData, customPropertyData, customBindings);
680     // add for automatic destruction after rendering
681     if (materialIndex < matData_.data.size()) {
682         matData_.data[materialIndex].noId = true;
683     }
684 
685     const uint32_t materialOffset = static_cast<uint32_t>(matData_.frameIndices.size());
686     matData_.frameIndices.push_back(materialIndex); // material index
687     return { materialIndex, materialOffset };
688 }
689 
AddFrameMaterialInstanceData(uint32_t materialIndex,uint32_t frameOffset,uint32_t instanceIndex)690 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialInstanceData(
691     uint32_t materialIndex, uint32_t frameOffset, uint32_t instanceIndex)
692 {
693     const uint32_t frameFullOffset = frameOffset + instanceIndex;
694     if (frameFullOffset < static_cast<uint32_t>(matData_.frameIndices.size())) {
695         matData_.frameIndices[frameFullOffset] = materialIndex;
696         return { materialIndex, frameFullOffset };
697     }
698     return {};
699 }
700 
UpdateFrameMaterialResourceReferences(const uint32_t materialIndex)701 void RenderDataStoreDefaultMaterial::UpdateFrameMaterialResourceReferences(const uint32_t materialIndex)
702 {
703     CORE_ASSERT(matData_.data.size() == matData_.handles.size());
704     CORE_ASSERT(matData_.data.size() == matData_.customResourceData.size());
705     if (materialIndex < static_cast<uint32_t>(matData_.handles.size())) {
706         auto& matDataRef = matData_.data[materialIndex];
707         if (!matDataRef.frameReferenced) {
708             // add references
709             matDataRef.frameReferenced = true;
710             const auto& handles = matData_.handles[materialIndex];
711             // NOTE: access to GPU resource manager is locking behaviour
712             // this can be evaluated further and possibly add array_view for get to get all with a one lock
713             for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT; ++idx) {
714                 if (RenderHandleUtil::IsValid(handles.images[idx])) {
715                     handleReferences_.push_back(gpuResourceMgr_.Get(handles.images[idx]));
716                 }
717                 if (RenderHandleUtil::IsValid(handles.samplers[idx])) {
718                     handleReferences_.push_back(gpuResourceMgr_.Get(handles.samplers[idx]));
719                 }
720             }
721             const auto& customHandles = matData_.customResourceData[materialIndex];
722             for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT; ++idx) {
723                 if (RenderHandleUtil::IsValid(customHandles.resourceHandles[idx])) {
724                     handleReferences_.push_back(gpuResourceMgr_.Get(customHandles.resourceHandles[idx]));
725                 }
726             }
727         }
728     }
729 }
730 
AddMeshData(const RenderMeshData & meshData)731 uint32_t RenderDataStoreDefaultMaterial::AddMeshData(const RenderMeshData& meshData)
732 {
733     const uint32_t meshIndex = static_cast<uint32_t>(meshData_.size());
734     meshData_.push_back(meshData);
735     return meshIndex;
736 }
737 
AddSkinJointMatrices(const array_view<const Math::Mat4X4> skinJointMatrices,const array_view<const Math::Mat4X4> prevSkinJointMatrices)738 uint32_t RenderDataStoreDefaultMaterial::AddSkinJointMatrices(
739     const array_view<const Math::Mat4X4> skinJointMatrices, const array_view<const Math::Mat4X4> prevSkinJointMatrices)
740 {
741     // check max supported joint count
742     const uint32_t jointCount =
743         std::min(RenderDataDefaultMaterial::MAX_SKIN_MATRIX_COUNT, static_cast<uint32_t>(skinJointMatrices.size()));
744     uint32_t skinJointIndex = RenderSceneDataConstants::INVALID_INDEX;
745     if (jointCount > 0) {
746         const uint32_t byteSize = sizeof(Math::Mat4X4) * jointCount;
747         const uint32_t fullJointCount = jointCount * 2u;
748         Math::Mat4X4* jointMatrixData = AllocateMatrices(submeshJointMatricesAllocator_, fullJointCount);
749         if (jointMatrixData) {
750             CloneData(jointMatrixData, byteSize, skinJointMatrices.data(), byteSize);
751             if (skinJointMatrices.size() == prevSkinJointMatrices.size()) {
752                 CloneData(jointMatrixData + jointCount, byteSize, prevSkinJointMatrices.data(), byteSize);
753             } else {
754                 // copy current to previous if given prevSkinJointMatrices is not valid
755                 CloneData(jointMatrixData + jointCount, byteSize, skinJointMatrices.data(), byteSize);
756             }
757         }
758         skinJointIndex = static_cast<uint32_t>(submeshJointMatrixIndices_.size());
759         submeshJointMatrixIndices_.push_back(
760             RenderDataDefaultMaterial::JointMatrixData { jointMatrixData, fullJointCount });
761     }
762     return skinJointIndex;
763 }
764 
AddSubmesh(const RenderSubmeshWithHandleReference & submesh)765 void RenderDataStoreDefaultMaterial::AddSubmesh(const RenderSubmeshWithHandleReference& submesh)
766 {
767     const uint32_t materialIndex = GetCertainMaterialIndex(submesh.indices.materialIndex, matData_);
768     if (materialIndex < static_cast<uint32_t>(matData_.data.size())) {
769         uint32_t renderSlotCount = 0u;
770 
771         // default support for 3 render slots
772         IShaderManager::RenderSlotData renderSlotData[SHADER_DEFAULT_RENDER_SLOT_COUNT] {};
773 
774         const auto& matData = matData_.data[materialIndex].md;
775         renderSlotData[0u].shader = matData.materialShader.shader;
776         renderSlotData[0u].graphicsState = matData.materialShader.graphicsState;
777         constexpr uint32_t INVALID_RENDER_SLOT_ID = ~0u;
778         uint32_t renderSlotId = matData.customRenderSlotId;
779         if ((renderSlotId == INVALID_RENDER_SLOT_ID) && renderSlotData[0].graphicsState) {
780             renderSlotId = shaderMgr_.GetRenderSlotId(renderSlotData[0u].graphicsState);
781         }
782         if ((renderSlotId == INVALID_RENDER_SLOT_ID) && renderSlotData[0].shader) {
783             renderSlotId = shaderMgr_.GetRenderSlotId(renderSlotData[0u].shader);
784         }
785         // if all fails, render as opaque
786         if (renderSlotId == INVALID_RENDER_SLOT_ID) {
787             renderSlotId = materialRenderSlots_.defaultOpaqueRenderSlot;
788         }
789         renderSlotData[renderSlotCount].renderSlotId = renderSlotId;
790         renderSlotCount++;
791 
792         if (matData.renderMaterialFlags & RenderMaterialFlagBits::RENDER_MATERIAL_SHADOW_CASTER_BIT) {
793             renderSlotData[renderSlotCount].renderSlotId = materialRenderSlots_.defaultDepthRenderSlot;
794             renderSlotData[renderSlotCount].shader = matData.depthShader.shader;
795             renderSlotData[renderSlotCount].graphicsState = matData.depthShader.graphicsState;
796             renderSlotCount++;
797         }
798         AddSubmesh(submesh, { renderSlotData, renderSlotCount });
799     }
800 }
801 
AddSubmesh(const RenderSubmeshWithHandleReference & submesh,const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders)802 void RenderDataStoreDefaultMaterial::AddSubmesh(const RenderSubmeshWithHandleReference& submesh,
803     const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders)
804 {
805 #if (CORE3D_VALIDATION_ENABLED == 1)
806     ValidateSubmesh(submesh);
807 #endif
808     const uint32_t submeshIndex = static_cast<uint32_t>(submeshes_.size());
809     submeshes_.push_back(ConvertRenderSubmeshInput(submesh, handleReferences_));
810     auto& currSubmesh = submeshes_.back();
811 
812     FillSubmeshImpl(renderSlotAndShaders, submeshIndex, currSubmesh);
813 }
814 
FillSubmeshImpl(const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders,const uint32_t submeshIndex,RenderSubmesh & submesh)815 void RenderDataStoreDefaultMaterial::FillSubmeshImpl(
816     const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders, const uint32_t submeshIndex,
817     RenderSubmesh& submesh)
818 {
819     const uint32_t materialIndex = GetCertainMaterialIndex(submesh.indices.materialIndex, matData_);
820     submesh.indices.materialIndex = materialIndex; // if invalid -> store the default material
821     if (submesh.indices.meshIndex >= static_cast<uint32_t>(meshData_.size())) {
822         CORE_LOG_W("invalid mesh index (%u) given", submesh.indices.meshIndex);
823         submesh.indices.meshIndex = 0;
824     }
825     if ((submesh.submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_SKIN_BIT) &&
826         (submesh.indices.skinJointIndex >= static_cast<uint32_t>(submeshJointMatrixIndices_.size()))) {
827         CORE_LOG_W("invalid skin joint index (%u) given", submesh.indices.skinJointIndex);
828         submesh.indices.skinJointIndex = RenderSceneDataConstants::INVALID_INDEX;
829     }
830 
831     if (submesh.indices.materialIndex >= static_cast<uint32_t>(matData_.allUniforms.size())) {
832         // NOTE: shouldn't come here with basic usage
833         RenderFrameMaterialIndices rfmi = AddFrameMaterialData({}, {}, {}, {}, {});
834         submesh.indices.materialIndex = rfmi.index;
835         submesh.indices.materialFrameOffset = rfmi.frameOffset;
836     } else {
837         CORE_ASSERT(matData_.data.size() == matData_.allUniforms.size());
838     }
839     const RenderDataDefaultMaterial::MaterialData& perMatData = matData_.data[submesh.indices.materialIndex].md;
840     // batch render material sort layers if default values
841     PatchRenderMaterialSortLayers(perMatData, submesh.layers);
842 
843     // combine with submesh specific flags
844     RenderMaterialFlags submeshRenderMaterialFlags =
845         perMatData.renderMaterialFlags | submesh.renderSubmeshMaterialFlags;
846     // remove instancing related things if not available
847     if (submesh.drawCommand.instanceCount) {
848         submeshRenderMaterialFlags |= RenderMaterialFlagBits::RENDER_MATERIAL_GPU_INSTANCING_BIT;
849     } else {
850         submeshRenderMaterialFlags &= COMBINED_GPU_INSTANCING_REMOVAL;
851     }
852     ExtentRenderMaterialFlagsFromSubmeshValues(submesh.submeshFlags, submeshRenderMaterialFlags);
853 
854     const uint32_t renderHash =
855         HashSubmeshMaterials(perMatData.materialType, submeshRenderMaterialFlags, submesh.submeshFlags);
856     // depth optimized flags
857     const uint32_t renderDepthHash = HashSubmeshMaterials(perMatData.materialType,
858         submeshRenderMaterialFlags & RenderDataDefaultMaterial::RENDER_MATERIAL_DEPTH_FLAGS,
859         submesh.submeshFlags & RenderDataDefaultMaterial::RENDER_SUBMESH_DEPTH_FLAGS);
860     submeshMaterialFlags_.push_back(
861         RenderDataDefaultMaterial::SubmeshMaterialFlags { perMatData.materialType, submesh.submeshFlags,
862             perMatData.extraMaterialRenderingFlags, submeshRenderMaterialFlags, renderHash, renderDepthHash });
863 
864     const uint16_t renderSortLayerHash = GetRenderSortLayerHash(submesh);
865     // add submeshs to slots
866     for (const auto& slotRef : renderSlotAndShaders) {
867         SlotSubmeshData& dataRef = slotToSubmeshIndices_[slotRef.renderSlotId];
868         dataRef.indices.push_back(submeshIndex);
869         // hash for sorting (material index is certain for the same material)
870         // shader and gfx state is not needed, because it's already in the material, or they are defaults
871         // inverse winding can affect the final graphics state but it's in renderHash
872         // we hash with material index, and render hash
873         // id generation does not matter to us, because this is per frame
874         uint32_t renderSortHash = submesh.indices.materialIndex;
875         HashCombine32Bit(renderSortHash, renderHash);
876         dataRef.materialData.push_back({ renderSortLayerHash, renderSortHash, submeshRenderMaterialFlags,
877             slotRef.shader.GetHandle(), slotRef.graphicsState.GetHandle() });
878 
879         dataRef.objectCounts.submeshCount++;
880         if (submesh.indices.skinJointIndex != RenderSceneDataConstants::INVALID_INDEX) {
881             dataRef.objectCounts.skinCount++;
882         }
883     }
884 }
885 
SetRenderSlots(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType,const BASE_NS::array_view<const uint32_t> renderSlotIds)886 void RenderDataStoreDefaultMaterial::SetRenderSlots(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType,
887     const BASE_NS::array_view<const uint32_t> renderSlotIds)
888 {
889     uint64_t mask = 0;
890     for (const auto renderSlotId : renderSlotIds) {
891         mask |= 1ULL << uint64_t(renderSlotId);
892     }
893     if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_OPAQUE) {
894         materialRenderSlots_.opaqueMask = mask;
895     } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_TRANSLUCENT) {
896         materialRenderSlots_.translucentMask = mask;
897     } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_DEPTH) {
898         materialRenderSlots_.depthMask = mask;
899     }
900 }
901 
GetRenderSlotMask(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType) const902 uint64_t RenderDataStoreDefaultMaterial::GetRenderSlotMask(
903     const RenderDataDefaultMaterial::MaterialSlotType materialSlotType) const
904 {
905     uint64_t mask = 0;
906     if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_OPAQUE) {
907         mask = materialRenderSlots_.opaqueMask;
908     } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_TRANSLUCENT) {
909         mask = materialRenderSlots_.translucentMask;
910     } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_DEPTH) {
911         mask = materialRenderSlots_.depthMask;
912     }
913     return mask;
914 }
915 
GetRenderSlotIdFromMasks(const uint32_t renderSlotId) const916 uint32_t RenderDataStoreDefaultMaterial::GetRenderSlotIdFromMasks(const uint32_t renderSlotId) const
917 {
918     // compare to masks
919     const uint64_t renderSlotMask = 1ULL << uint64_t(renderSlotId & 0x3F); // 63
920     uint32_t newRenderSlotId = renderSlotId;
921     if (renderSlotMask & materialRenderSlots_.opaqueMask) {
922         newRenderSlotId = materialRenderSlots_.defaultOpaqueRenderSlot;
923     } else if (renderSlotMask & materialRenderSlots_.translucentMask) {
924         newRenderSlotId = materialRenderSlots_.defaultTranslucentRenderSlot;
925     } else if (renderSlotMask & materialRenderSlots_.depthMask) {
926         newRenderSlotId = materialRenderSlots_.defaultDepthRenderSlot;
927     }
928     return newRenderSlotId;
929 }
930 
GetSlotSubmeshIndices(const uint32_t renderSlotId) const931 array_view<const uint32_t> RenderDataStoreDefaultMaterial::GetSlotSubmeshIndices(const uint32_t renderSlotId) const
932 {
933     const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
934     if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
935         const auto& slotRef = iter->second;
936         return slotRef.indices;
937     } else {
938         return {};
939     }
940 }
941 
942 array_view<const RenderDataDefaultMaterial::SlotMaterialData>
GetSlotSubmeshMaterialData(const uint32_t renderSlotId) const943 RenderDataStoreDefaultMaterial::GetSlotSubmeshMaterialData(const uint32_t renderSlotId) const
944 {
945     const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
946     if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
947         const auto& slotRef = iter->second;
948         return slotRef.materialData;
949     } else {
950         return {};
951     }
952 }
953 
GetSlotObjectCounts(const uint32_t renderSlotId) const954 RenderDataDefaultMaterial::ObjectCounts RenderDataStoreDefaultMaterial::GetSlotObjectCounts(
955     const uint32_t renderSlotId) const
956 {
957     const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
958     if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
959         return {
960             static_cast<uint32_t>(meshData_.size()),
961             iter->second.objectCounts.submeshCount,
962             iter->second.objectCounts.skinCount,
963             static_cast<uint32_t>(matData_.frameIndices.size()),
964             static_cast<uint32_t>(matData_.allUniforms.size()),
965         };
966     } else {
967         return {};
968     }
969 }
970 
GetObjectCounts() const971 RenderDataDefaultMaterial::ObjectCounts RenderDataStoreDefaultMaterial::GetObjectCounts() const
972 {
973     return {
974         static_cast<uint32_t>(meshData_.size()),
975         static_cast<uint32_t>(submeshes_.size()),
976         static_cast<uint32_t>(submeshJointMatrixIndices_.size()),
977         static_cast<uint32_t>(matData_.frameIndices.size()),
978         static_cast<uint32_t>(matData_.allUniforms.size()),
979     };
980 }
981 
GetSubmeshes() const982 array_view<const RenderSubmesh> RenderDataStoreDefaultMaterial::GetSubmeshes() const
983 {
984     return submeshes_;
985 }
986 
GetMeshData() const987 array_view<const RenderMeshData> RenderDataStoreDefaultMaterial::GetMeshData() const
988 {
989     return meshData_;
990 }
991 
992 array_view<const RenderDataDefaultMaterial::JointMatrixData>
GetMeshJointMatrices() const993 RenderDataStoreDefaultMaterial::GetMeshJointMatrices() const
994 {
995     return submeshJointMatrixIndices_;
996 }
997 
GetSubmeshJointMatrixData(const uint32_t skinJointIndex) const998 array_view<const Math::Mat4X4> RenderDataStoreDefaultMaterial::GetSubmeshJointMatrixData(
999     const uint32_t skinJointIndex) const
1000 {
1001     if (skinJointIndex < static_cast<uint32_t>(submeshJointMatrixIndices_.size())) {
1002         const RenderDataDefaultMaterial::JointMatrixData& jm = submeshJointMatrixIndices_[skinJointIndex];
1003         return array_view<const Math::Mat4X4>(jm.data, static_cast<size_t>(jm.count));
1004     } else {
1005         return {};
1006     }
1007 }
1008 
1009 array_view<const RenderDataDefaultMaterial::AllMaterialUniforms>
GetMaterialUniforms() const1010 RenderDataStoreDefaultMaterial::GetMaterialUniforms() const
1011 {
1012     return matData_.allUniforms;
1013 }
1014 
GetMaterialFrameIndices() const1015 array_view<const uint32_t> RenderDataStoreDefaultMaterial::GetMaterialFrameIndices() const
1016 {
1017     return matData_.frameIndices;
1018 }
1019 
GetMaterialHandles() const1020 array_view<const RenderDataDefaultMaterial::MaterialHandles> RenderDataStoreDefaultMaterial::GetMaterialHandles() const
1021 {
1022     return matData_.handles;
1023 }
1024 
GetMaterialCustomPropertyData(const uint32_t materialIndex) const1025 array_view<const uint8_t> RenderDataStoreDefaultMaterial::GetMaterialCustomPropertyData(
1026     const uint32_t materialIndex) const
1027 {
1028     if (materialIndex < static_cast<uint32_t>(matData_.customPropertyData.size())) {
1029         return matData_.customPropertyData[materialIndex].data;
1030     } else {
1031         return {};
1032     }
1033 }
1034 
1035 array_view<const RenderDataDefaultMaterial::SubmeshMaterialFlags>
GetSubmeshMaterialFlags() const1036 RenderDataStoreDefaultMaterial::GetSubmeshMaterialFlags() const
1037 {
1038     return submeshMaterialFlags_;
1039 }
1040 
1041 array_view<const RenderDataDefaultMaterial::CustomResourceData>
GetCustomResourceHandles() const1042 RenderDataStoreDefaultMaterial::GetCustomResourceHandles() const
1043 {
1044     return matData_.customResourceData;
1045 }
1046 
GenerateRenderHash(const RenderDataDefaultMaterial::SubmeshMaterialFlags & flags) const1047 uint32_t RenderDataStoreDefaultMaterial::GenerateRenderHash(
1048     const RenderDataDefaultMaterial::SubmeshMaterialFlags& flags) const
1049 {
1050     return HashSubmeshMaterials(flags.materialType, flags.renderMaterialFlags, flags.submeshFlags);
1051 }
1052 
GetMaterialIndex(const uint64_t id) const1053 uint32_t RenderDataStoreDefaultMaterial::GetMaterialIndex(const uint64_t id) const
1054 {
1055     if (const auto iter = matData_.materialIdToIndex.find(id); iter != matData_.materialIdToIndex.cend()) {
1056         return iter->second;
1057     } else {
1058         return RenderSceneDataConstants::INVALID_INDEX;
1059     }
1060 }
1061 
AddRenderSlotSubmeshesFrameMaterialData(const uint32_t toSlotId,const array_view<const uint32_t> fromSlotIds,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customPropertyData,const array_view<const RenderHandleReference> customBindings)1062 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddRenderSlotSubmeshesFrameMaterialData(
1063     const uint32_t toSlotId, const array_view<const uint32_t> fromSlotIds,
1064     const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
1065     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
1066     const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customPropertyData,
1067     const array_view<const RenderHandleReference> customBindings)
1068 {
1069     const RenderFrameMaterialIndices rfmi =
1070         AddFrameMaterialData(materialUniforms, materialHandles, materialData, customPropertyData, customBindings);
1071 
1072     const IShaderManager::RenderSlotData rds { toSlotId, {}, {}, {}, {} };
1073     const array_view<const IShaderManager::RenderSlotData> rdsView = { &rds, 1U };
1074     for (uint32_t slotIdx = 0U; slotIdx < fromSlotIds.size(); ++slotIdx) {
1075         const uint32_t currSlotIdx = fromSlotIds[slotIdx];
1076         const auto slotSubmeshIndices = GetSlotSubmeshIndices(currSlotIdx);
1077         submeshes_.reserve(submeshes_.size() + slotSubmeshIndices.size());
1078         for (uint32_t ssIdx = 0U; ssIdx < static_cast<uint32_t>(slotSubmeshIndices.size()); ++ssIdx) {
1079             const uint32_t currSubmeshIdx = slotSubmeshIndices[ssIdx];
1080             if (currSubmeshIdx < static_cast<uint32_t>(submeshes_.size())) {
1081                 // duplicate
1082                 RenderSubmesh newSubmesh = submeshes_[currSubmeshIdx];
1083                 newSubmesh.indices.materialIndex = rfmi.index;
1084                 newSubmesh.indices.materialFrameOffset = rfmi.frameOffset;
1085 
1086                 const uint32_t submeshIndex = static_cast<uint32_t>(submeshes_.size());
1087                 submeshes_.push_back(newSubmesh);
1088 
1089                 auto& currSubmesh = submeshes_.back();
1090                 FillSubmeshImpl(rdsView, submeshIndex, currSubmesh);
1091             }
1092         }
1093     }
1094     return rfmi;
1095 }
1096 
1097 // for plugin / factory interface
Create(RENDER_NS::IRenderContext & renderContext,char const * name)1098 refcnt_ptr<IRenderDataStore> RenderDataStoreDefaultMaterial::Create(
1099     RENDER_NS::IRenderContext& renderContext, char const* name)
1100 {
1101     return refcnt_ptr<IRenderDataStore>(new RenderDataStoreDefaultMaterial(renderContext, name));
1102 }
1103 CORE3D_END_NAMESPACE()
1104