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_shader_manager.h>
26 #include <render/intf_render_context.h>
27 #include <render/resource_handle.h>
28 
29 #include "render_data_store_default_material.h"
30 
31 CORE3D_BEGIN_NAMESPACE()
32 using namespace BASE_NS;
33 
34 using RENDER_NS::IShaderManager;
35 using RENDER_NS::RenderHandleReference;
36 
37 namespace {
38 // for packing defines
39 #include <3d/shaders/common/3d_dm_structures_common.h>
40 } // namespace
41 namespace {
42 constexpr uint32_t SHADER_DEFAULT_RENDER_SLOT_COUNT { 3u };
43 
44 constexpr size_t MEMORY_ALIGNMENT { 64 };
45 
46 static constexpr uint32_t MATERIAL_TYPE_SHIFT { 28u };
47 static constexpr uint32_t MATERIAL_TYPE_MASK { 0xF0000000u };
48 static constexpr uint32_t MATERIAL_FLAGS_SHIFT { 8u };
49 static constexpr uint32_t MATERIAL_FLAGS_MASK { 0x0FFFff00u };
50 static constexpr uint32_t SUBMESH_FLAGS_MASK { 0x00000ffu };
51 
HashCombine32Bit(uint32_t & seed,const uint32_t v)52 inline void HashCombine32Bit(uint32_t& seed, const uint32_t v)
53 {
54     constexpr const uint32_t goldenRatio = 0x9e3779b9;
55     constexpr const uint32_t rotl = 6U;
56     constexpr const uint32_t rotr = 2U;
57     seed ^= hash(v) + goldenRatio + (seed << rotl) + (seed >> rotr);
58 }
59 
60 #if (CORE3D_VALIDATION_ENABLED == 1)
ValidateSubmesh(const RenderSubmesh & submesh,const vector<RenderDataDefaultMaterial::CustomResourceData> & customResourceData)61 void ValidateSubmesh(
62     const RenderSubmesh& submesh, const vector<RenderDataDefaultMaterial::CustomResourceData>& customResourceData)
63 {
64     if (((submesh.submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_SKIN_BIT) == 0) &&
65         submesh.skinJointIndex != RenderSceneDataConstants::INVALID_INDEX) {
66         CORE_LOG_W("CORE3D_VALIDATION: skin bit is not set for submesh flags");
67     }
68     if ((submesh.customResourcesIndex != RenderSceneDataConstants::INVALID_INDEX) &&
69         (submesh.customResourcesIndex >= static_cast<uint32_t>(customResourceData.size()))) {
70         CORE_LOG_W("CORE3D_VALIDATION: invalid custom resource index");
71     }
72 }
73 #endif
74 
PackMaterialUVec(const Math::Vec4 & up0,const Math::Vec4 & up1)75 inline Math::UVec4 PackMaterialUVec(const Math::Vec4& up0, const Math::Vec4& up1)
76 {
77     return Math::UVec4 {
78         Math::PackHalf2X16({ up0.x, up0.y }),
79         Math::PackHalf2X16({ up0.z, up0.w }),
80         Math::PackHalf2X16({ up1.x, up1.y }),
81         Math::PackHalf2X16({ up1.z, up1.w }),
82     };
83 }
84 
ExtentRenderMaterialFlagsFromSubmeshValues(const RenderSubmeshFlags submeshFlags,RenderMaterialFlags & rmf)85 inline void ExtentRenderMaterialFlagsFromSubmeshValues(const RenderSubmeshFlags submeshFlags, RenderMaterialFlags& rmf)
86 {
87     // if there are normal maps and there are tangets, we allow the normal map flag
88     if ((rmf & RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT) &&
89         (submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_TANGENTS_BIT)) {
90         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT;
91     } else {
92         // remove flag if there were only normal maps but no tangents
93         rmf &= (~RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT);
94     }
95 }
96 
ExtentRenderMaterialFlagsForComplexity(const RenderMaterialType materialType,RenderMaterialFlags & rmf)97 void ExtentRenderMaterialFlagsForComplexity(const RenderMaterialType materialType, RenderMaterialFlags& rmf)
98 {
99     constexpr RenderMaterialFlags complexMask { RENDER_MATERIAL_CLEAR_COAT_BIT | RENDER_MATERIAL_TRANSMISSION_BIT |
100                                                 RENDER_MATERIAL_SHEEN_BIT | RENDER_MATERIAL_SPECULAR_BIT };
101     if ((materialType == RenderMaterialType::CUSTOM) || (materialType == RenderMaterialType::CUSTOM_COMPLEX)) {
102         rmf |= (materialType == RenderMaterialType::CUSTOM_COMPLEX) ? RENDER_MATERIAL_COMPLEX_BIT
103                                                                     : RENDER_MATERIAL_BASIC_BIT;
104         if (materialType < RenderMaterialType::CUSTOM) {
105             rmf |= ((rmf & complexMask) || (materialType == RenderMaterialType::SPECULAR_GLOSSINESS))
106                        ? RENDER_MATERIAL_COMPLEX_BIT
107                        : RENDER_MATERIAL_BASIC_BIT;
108         }
109     } else {
110         rmf |= ((rmf & complexMask) > 0) ? RENDER_MATERIAL_COMPLEX_BIT : RENDER_MATERIAL_BASIC_BIT;
111     }
112 }
113 
HashSubmeshMaterials(const RenderMaterialType materialType,const RenderMaterialFlags materialFlags,const RenderSubmeshFlags submeshFlags)114 inline constexpr uint32_t HashSubmeshMaterials(const RenderMaterialType materialType,
115     const RenderMaterialFlags materialFlags, const RenderSubmeshFlags submeshFlags)
116 {
117     return ((static_cast<uint32_t>(materialType) << MATERIAL_TYPE_SHIFT) & MATERIAL_TYPE_MASK) |
118            ((materialFlags << MATERIAL_FLAGS_SHIFT) & MATERIAL_FLAGS_MASK) | (submeshFlags & SUBMESH_FLAGS_MASK);
119 }
120 
HashMaterialId(const uint64_t id,const uint32_t instanceCount)121 inline uint64_t HashMaterialId(const uint64_t id, const uint32_t instanceCount)
122 {
123     return Hash(id, static_cast<uint64_t>(instanceCount));
124 }
125 
AllocateMatrixMemory(RenderDataStoreDefaultMaterial::LinearAllocatorStruct & allocator,const size_t byteSize)126 void* AllocateMatrixMemory(RenderDataStoreDefaultMaterial::LinearAllocatorStruct& allocator, const size_t byteSize)
127 {
128     void* data = nullptr;
129     if (!allocator.allocators.empty()) {
130         data = allocator.allocators[allocator.currentIndex]->Allocate(byteSize);
131     }
132 
133     if (data) {
134         return data;
135     } else { // current allocator is out of memory
136         allocator.allocators.push_back(make_unique<LinearAllocator>(byteSize, MEMORY_ALIGNMENT));
137         allocator.currentIndex = static_cast<uint32_t>(allocator.allocators.size() - 1);
138         data = allocator.allocators[allocator.currentIndex]->Allocate(byteSize);
139         CORE_ASSERT_MSG(data, "render data store default material allocation : out of memory");
140         return data;
141     }
142 }
143 
AllocateMatrices(RenderDataStoreDefaultMaterial::LinearAllocatorStruct & allocator,const size_t count)144 Math::Mat4X4* AllocateMatrices(RenderDataStoreDefaultMaterial::LinearAllocatorStruct& allocator, const size_t count)
145 {
146     const size_t byteSize = count * sizeof(Math::Mat4X4);
147     return static_cast<Math::Mat4X4*>(AllocateMatrixMemory(allocator, byteSize));
148 }
149 
MaterialUniformsPackedFromInput(const RenderDataDefaultMaterial::InputMaterialUniforms & input)150 RenderDataDefaultMaterial::AllMaterialUniforms MaterialUniformsPackedFromInput(
151     const RenderDataDefaultMaterial::InputMaterialUniforms& input)
152 {
153     static constexpr const RenderDataDefaultMaterial::AllMaterialUniforms initial {};
154     RenderDataDefaultMaterial::AllMaterialUniforms amu = initial;
155     // premultiplication needs to be handled already with some factors like baseColor
156     for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT; ++idx) {
157         amu.factors.factors[idx] = input.textureData[idx].factor;
158     }
159     uint32_t transformBits = 0;
160     {
161         constexpr auto supportedMaterials = RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT;
162         for (uint32_t i = 0; i < supportedMaterials; ++i) {
163             const auto& tex = input.textureData[i];
164 
165             /* TRS matrix for 2D transformations:
166              * { scaleX * cos(rot),  scaleY * sin(rot), transX }
167              * { scaleX * -sin(rot), scaleY * cos(rot), transY }
168              * { 0,                  0,                 1      }
169              */
170             Math::Vec4 rotateScale;
171             if (tex.rotation == 0.f) {
172                 rotateScale.x = tex.scale.x;
173                 rotateScale.w = tex.scale.y;
174             } else {
175                 const float sinR = Math::sin(tex.rotation);
176                 const float cosR = Math::cos(tex.rotation);
177                 rotateScale = { tex.scale.x * cosR, tex.scale.y * sinR, tex.scale.x * -sinR, tex.scale.y * cosR };
178             }
179             const Math::Vec2 translate = { tex.translation.x, tex.translation.y };
180 
181             // set transform bit for texture index
182             static constexpr const auto identityRs = Math::Vec4(1.f, 0.f, 0.f, 1.f);
183             const bool hasTransform = (translate.x != 0.f) || (translate.y != 0.f) || (rotateScale != identityRs);
184             if (!hasTransform) {
185                 // this matches packing identityRs.xy, identityRs.zw, translation.xy, and 0,0
186                 static constexpr const auto identity = Math::UVec4(0x3c000000, 0x00003c00, 0x0, 0x0);
187                 amu.transforms.packed[CORE_MATERIAL_PACK_TEX_BASE_COLOR_UV_IDX + i] = identity;
188             } else {
189                 // using PackMaterialUVec costs packing the last unused zeros
190                 amu.transforms.packed[CORE_MATERIAL_PACK_TEX_BASE_COLOR_UV_IDX + i] =
191                     Math::UVec4 { Math::PackHalf2X16({ rotateScale.x, rotateScale.y }),
192                         Math::PackHalf2X16({ rotateScale.z, rotateScale.w }),
193                         Math::PackHalf2X16({ translate.x, translate.y }), 0 };
194                 transformBits |= static_cast<uint32_t>(hasTransform) << i;
195             }
196         }
197     }
198     const uint32_t transformInfoBits = (input.texCoordSetBits << 16u) | transformBits; // 16: left remove 16 bits
199     const Math::UVec4 indices = { uint32_t(input.id >> 32u), uint32_t(input.id & 0xFFFFffff), 0u, 0u };
200 
201     // finalize factors
202     amu.factors.factors[RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT] = { input.alphaCutoff,
203         *reinterpret_cast<const float*>(&transformInfoBits), 0.0f, 0.0f };
204     amu.factors.indices = indices;
205     // finalize transforms
206     amu.transforms.packed[CORE_MATERIAL_PACK_ADDITIONAL_IDX] = { Math::PackHalf2X16({ input.alphaCutoff, 0.f }),
207         transformInfoBits, 0, 0 };
208     amu.transforms.indices = indices;
209     return amu;
210 }
211 } // namespace
212 
RenderDataStoreDefaultMaterial(RENDER_NS::IRenderContext & renderContext,const string_view name)213 RenderDataStoreDefaultMaterial::RenderDataStoreDefaultMaterial(
214     RENDER_NS::IRenderContext& renderContext, const string_view name)
215     : name_(name), shaderMgr_(renderContext.GetDevice().GetShaderManager())
216 {
217     GetDefaultRenderSlots();
218 }
219 
PostRender()220 void RenderDataStoreDefaultMaterial::PostRender()
221 {
222     Clear();
223 }
224 
Clear()225 void RenderDataStoreDefaultMaterial::Clear()
226 {
227     submeshes_.clear();
228     meshData_.clear();
229     submeshJointMatrixIndices_.clear();
230     customResourceData_.clear();
231 
232     materialAllUniforms_.clear();
233     materialHandles_.clear();
234     materialData_.clear();
235     materialIdToIndices_.clear();
236     materialCustomPropertyOffsets_.clear();
237     materialCustomPropertyData_.clear();
238     submeshMaterialFlags_.clear();
239 
240     for (auto& slotRef : slotToSubmeshIndices_) { // does not remove slots from use
241         slotRef.second.indices.clear();
242         slotRef.second.materialData.clear();
243         slotRef.second.objectCounts = {};
244     }
245 
246     if (!submeshJointMatricesAllocator_.allocators.empty()) {
247         submeshJointMatricesAllocator_.currentIndex = 0;
248         if (submeshJointMatricesAllocator_.allocators.size() == 1) { // size is good for this frame
249             submeshJointMatricesAllocator_.allocators[submeshJointMatricesAllocator_.currentIndex]->Reset();
250         } else if (submeshJointMatricesAllocator_.allocators.size() > 1) {
251             size_t fullByteSize = 0;
252             for (auto& ref : submeshJointMatricesAllocator_.allocators) {
253                 fullByteSize += ref->GetCurrentByteSize();
254                 ref.reset();
255             }
256             submeshJointMatricesAllocator_.allocators.clear();
257             // create new single allocation for combined previous size and some extra bytes
258             submeshJointMatricesAllocator_.allocators.push_back(
259                 make_unique<LinearAllocator>(fullByteSize, MEMORY_ALIGNMENT));
260         }
261     }
262 
263     // NOTE: re-fetch if default slots are invalid
264     if (materialRenderSlots_.opaqueMask != 0) {
265         GetDefaultRenderSlots();
266     }
267 }
268 
GetDefaultRenderSlots()269 void RenderDataStoreDefaultMaterial::GetDefaultRenderSlots()
270 {
271     materialRenderSlots_.defaultOpaqueRenderSlot =
272         shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE);
273     materialRenderSlots_.opaqueMask = (1ull << uint64_t(materialRenderSlots_.defaultOpaqueRenderSlot));
274     materialRenderSlots_.opaqueMask |=
275         (1ull << uint64_t(shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEFERRED_OPAQUE)));
276 
277     materialRenderSlots_.defaultTranslucentRenderSlot =
278         shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT);
279     materialRenderSlots_.translucentMask = (1ull << uint64_t(materialRenderSlots_.defaultTranslucentRenderSlot));
280 
281     materialRenderSlots_.defaultDepthRenderSlot =
282         shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH);
283     materialRenderSlots_.depthMask = (1ull << uint64_t(materialRenderSlots_.defaultDepthRenderSlot));
284     materialRenderSlots_.depthMask |=
285         (1ull << uint64_t(shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH_VSM)));
286 }
287 
AddMaterialData(const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandles & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customData)288 uint32_t RenderDataStoreDefaultMaterial::AddMaterialData(
289     const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
290     const RenderDataDefaultMaterial::MaterialHandles& materialHandles,
291     const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customData)
292 {
293     CORE_ASSERT(materialAllUniforms_.size() == materialData_.size());
294     const uint32_t materialIndex = static_cast<uint32_t>(materialAllUniforms_.size());
295 
296     materialHandles_.push_back(materialHandles);
297     materialAllUniforms_.push_back(MaterialUniformsPackedFromInput(materialUniforms));
298     materialData_.push_back(materialData);
299     auto& currMaterialData = materialData_.back();
300     ExtentRenderMaterialFlagsForComplexity(currMaterialData.materialType, currMaterialData.renderMaterialFlags);
301 
302     MaterialCustomPropertyOffset mcpo;
303     if (!customData.empty()) {
304         const auto offset = static_cast<uint32_t>(materialCustomPropertyData_.size_in_bytes());
305         const auto maxByteSize = Math::min(static_cast<uint32_t>(customData.size_bytes()),
306             RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_PROPERTY_BYTE_SIZE);
307         mcpo.offset = offset;
308         mcpo.byteSize = maxByteSize;
309         materialCustomPropertyData_.resize(offset + maxByteSize);
310         CloneData(materialCustomPropertyData_.data() + offset, maxByteSize, customData.data(), maxByteSize);
311     }
312     materialCustomPropertyOffsets_.push_back(mcpo);
313 
314     return materialIndex;
315 }
316 
AddMaterialData(const uint64_t id,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandles & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customData)317 uint32_t RenderDataStoreDefaultMaterial::AddMaterialData(const uint64_t id,
318     const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
319     const RenderDataDefaultMaterial::MaterialHandles& materialHandles,
320     const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customData)
321 {
322     CORE_STATIC_ASSERT(RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT == MaterialComponent::TEXTURE_COUNT);
323 
324     CORE_ASSERT(materialAllUniforms_.size() == materialData_.size());
325     auto searchId = HashMaterialId(id, static_cast<uint32_t>(1U));
326     if (const auto iter = materialIdToIndices_.find(searchId); iter != materialIdToIndices_.cend()) {
327         CORE_ASSERT(iter->second.materialIndex < static_cast<uint32_t>(materialAllUniforms_.size()));
328         return iter->second.materialIndex;
329     } else {
330         const uint32_t materialIndex = AddMaterialData(materialUniforms, materialHandles, materialData, customData);
331         materialIdToIndices_[searchId] = { materialIndex, RenderSceneDataConstants::INVALID_INDEX };
332         return materialIndex;
333     }
334 }
335 
AllocateMaterials(uint64_t id,uint32_t instanceCount)336 uint32_t RenderDataStoreDefaultMaterial::AllocateMaterials(uint64_t id, uint32_t instanceCount)
337 {
338     if (!instanceCount) {
339         return RenderSceneDataConstants::INVALID_INDEX;
340     }
341     auto searchId = HashMaterialId(id, instanceCount);
342     if (const auto iter = materialIdToIndices_.find(searchId); iter != materialIdToIndices_.cend()) {
343         CORE_ASSERT(iter->second.materialIndex < static_cast<uint32_t>(materialAllUniforms_.size()));
344         // NOTE: should probably know how many instances have been created, insert if instanceCount if bigger, update
345         // materialIdToIndex_.
346         return iter->second.materialIndex;
347     } else {
348         const uint32_t materialIndex = static_cast<uint32_t>(materialAllUniforms_.size());
349         materialHandles_.resize(materialIndex + instanceCount);
350         materialAllUniforms_.resize(materialIndex + instanceCount);
351         materialData_.resize(materialIndex + instanceCount);
352         materialCustomPropertyOffsets_.resize(materialIndex + instanceCount);
353         materialIdToIndices_[searchId] = { materialIndex, RenderSceneDataConstants::INVALID_INDEX };
354         return materialIndex;
355     }
356 }
357 
AddInstanceMaterialData(uint32_t materialIndex,uint32_t materialInstanceIndex,uint32_t materialInstanceCount,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandles & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const BASE_NS::array_view<const uint8_t> customPropertyData)358 void RenderDataStoreDefaultMaterial::AddInstanceMaterialData(uint32_t materialIndex, uint32_t materialInstanceIndex,
359     uint32_t materialInstanceCount, const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
360     const RenderDataDefaultMaterial::MaterialHandles& materialHandles,
361     const RenderDataDefaultMaterial::MaterialData& materialData,
362     const BASE_NS::array_view<const uint8_t> customPropertyData)
363 {
364     if ((materialIndex + materialInstanceIndex + materialInstanceCount) <= materialHandles_.size()) {
365         materialHandles_[materialIndex + materialInstanceIndex] = materialHandles;
366         auto& currMaterialData = materialData_[materialIndex + materialInstanceIndex];
367         currMaterialData = materialData;
368         ExtentRenderMaterialFlagsForComplexity(currMaterialData.materialType, currMaterialData.renderMaterialFlags);
369 
370         // uniforms and custom property data
371         AddInstanceMaterialData(
372             materialIndex, materialInstanceIndex, materialInstanceCount, materialUniforms, customPropertyData);
373     }
374 }
375 
AddInstanceMaterialData(uint32_t materialIndex,uint32_t materialInstanceIndex,uint32_t materialInstanceCount,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const BASE_NS::array_view<const uint8_t> customPropertyData)376 void RenderDataStoreDefaultMaterial::AddInstanceMaterialData(uint32_t materialIndex, uint32_t materialInstanceIndex,
377     uint32_t materialInstanceCount, const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
378     const BASE_NS::array_view<const uint8_t> customPropertyData)
379 {
380     if ((materialIndex + materialInstanceIndex + materialInstanceCount) <= materialAllUniforms_.size()) {
381         materialAllUniforms_[materialIndex + materialInstanceIndex] = MaterialUniformsPackedFromInput(materialUniforms);
382         if (!customPropertyData.empty()) {
383             const auto offset = static_cast<uint32_t>(materialCustomPropertyData_.size_in_bytes());
384             const auto maxByteSize = Math::min(static_cast<uint32_t>(customPropertyData.size_bytes()),
385                 RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_PROPERTY_BYTE_SIZE);
386             auto& mcpo = materialCustomPropertyOffsets_[materialIndex + materialInstanceIndex];
387             mcpo.offset = offset;
388             mcpo.byteSize = maxByteSize;
389             materialCustomPropertyData_.resize(offset + maxByteSize);
390             CloneData(materialCustomPropertyData_.data() + offset, maxByteSize, customPropertyData.data(), maxByteSize);
391         }
392         // duplicate the data, note handles are not duplicated
393         const auto& baseUniforms = materialAllUniforms_[materialIndex + materialInstanceIndex];
394         for (uint32_t idx = 1u; idx < materialInstanceCount; ++idx) {
395             const uint32_t currMaterialIndex = materialIndex + materialInstanceIndex + idx;
396             CORE_ASSERT(currMaterialIndex < static_cast<uint32_t>(materialAllUniforms_.size()));
397             materialAllUniforms_[currMaterialIndex] = baseUniforms;
398         }
399     }
400 }
401 
AddMeshData(const RenderMeshData & meshData)402 uint32_t RenderDataStoreDefaultMaterial::AddMeshData(const RenderMeshData& meshData)
403 {
404     const uint32_t meshIndex = static_cast<uint32_t>(meshData_.size());
405     meshData_.push_back(meshData);
406     return meshIndex;
407 }
408 
AddSkinJointMatrices(const array_view<const Math::Mat4X4> skinJointMatrices,const array_view<const Math::Mat4X4> prevSkinJointMatrices)409 uint32_t RenderDataStoreDefaultMaterial::AddSkinJointMatrices(
410     const array_view<const Math::Mat4X4> skinJointMatrices, const array_view<const Math::Mat4X4> prevSkinJointMatrices)
411 {
412     // check max supported joint count
413     const uint32_t jointCount =
414         std::min(RenderDataDefaultMaterial::MAX_SKIN_MATRIX_COUNT, static_cast<uint32_t>(skinJointMatrices.size()));
415     uint32_t skinJointIndex = RenderSceneDataConstants::INVALID_INDEX;
416     if (jointCount > 0) {
417         const uint32_t byteSize = sizeof(Math::Mat4X4) * jointCount;
418         const uint32_t fullJointCount = jointCount * 2u;
419         Math::Mat4X4* jointMatrixData = AllocateMatrices(submeshJointMatricesAllocator_, fullJointCount);
420         if (jointMatrixData) {
421             CloneData(jointMatrixData, byteSize, skinJointMatrices.data(), byteSize);
422             if (skinJointMatrices.size() == prevSkinJointMatrices.size()) {
423                 CloneData(jointMatrixData + jointCount, byteSize, prevSkinJointMatrices.data(), byteSize);
424             } else {
425                 // copy current to previous if given prevSkinJointMatrices is not valid
426                 CloneData(jointMatrixData + jointCount, byteSize, skinJointMatrices.data(), byteSize);
427             }
428         }
429         skinJointIndex = static_cast<uint32_t>(submeshJointMatrixIndices_.size());
430         submeshJointMatrixIndices_.push_back(
431             RenderDataDefaultMaterial::JointMatrixData { jointMatrixData, fullJointCount });
432     }
433     return skinJointIndex;
434 }
435 
AddMaterialCustomResources(const uint64_t id,const array_view<const RenderHandleReference> bindings)436 uint32_t RenderDataStoreDefaultMaterial::AddMaterialCustomResources(
437     const uint64_t id, const array_view<const RenderHandleReference> bindings)
438 {
439     uint32_t customResIndex = RenderSceneDataConstants::INVALID_INDEX;
440     auto searchId = HashMaterialId(id, static_cast<uint32_t>(1U));
441     if (auto iter = materialIdToIndices_.find(searchId); iter != materialIdToIndices_.end()) {
442         if (iter->second.materialCustomResourceIndex <= static_cast<uint32_t>(customResourceData_.size())) {
443             customResIndex = iter->second.materialCustomResourceIndex;
444         } else {
445             if (!bindings.empty()) {
446                 customResIndex = static_cast<uint32_t>(customResourceData_.size());
447                 customResourceData_.push_back({});
448                 auto& dataRef = customResourceData_.back();
449                 const uint32_t maxCount = Math::min(static_cast<uint32_t>(bindings.size()),
450                     RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT);
451                 for (uint32_t idx = 0; idx < maxCount; ++idx) {
452                     if (bindings[idx]) {
453                         dataRef.resourceHandles[dataRef.resourceHandleCount++] = bindings[idx];
454                     }
455                 }
456             }
457             iter->second.materialCustomResourceIndex = customResIndex;
458         }
459     } else {
460 #if (CORE3D_VALIDATION_ENABLED == 1)
461         CORE_LOG_ONCE_W("AddMaterialCustomResources_" + to_string(id),
462             "CORE3D_VALIDATION: AddMaterialCustomResources cannot add custom resources to non-existing material");
463 #endif
464     }
465     return customResIndex;
466 }
467 
AddSubmesh(const RenderSubmesh & submesh)468 void RenderDataStoreDefaultMaterial::AddSubmesh(const RenderSubmesh& submesh)
469 {
470 #if (CORE3D_VALIDATION_ENABLED == 1)
471     if (submesh.materialIndex >= static_cast<uint32_t>(materialData_.size())) {
472         CORE_LOG_W("CORE_VALIDATION: submesh cannot be sent to rendering without a valid materialIndex");
473     }
474 #endif
475     if (submesh.materialIndex < static_cast<uint32_t>(materialData_.size())) {
476         uint32_t renderSlotCount = 0u;
477 
478         // default support for 3 render slots
479         IShaderManager::RenderSlotData renderSlotData[SHADER_DEFAULT_RENDER_SLOT_COUNT] {};
480 
481         const auto& matData = materialData_[submesh.materialIndex];
482         renderSlotData[0u].shader = matData.materialShader.shader;
483         renderSlotData[0u].graphicsState = matData.materialShader.graphicsState;
484         constexpr uint32_t INVALID_RENDER_SLOT_ID = ~0u;
485         uint32_t renderSlotId = matData.customRenderSlotId;
486         if ((renderSlotId == INVALID_RENDER_SLOT_ID) && renderSlotData[0].graphicsState) {
487             renderSlotId = shaderMgr_.GetRenderSlotId(renderSlotData[0u].graphicsState);
488         }
489         if ((renderSlotId == INVALID_RENDER_SLOT_ID) && renderSlotData[0].shader) {
490             renderSlotId = shaderMgr_.GetRenderSlotId(renderSlotData[0u].shader);
491         }
492         // if all fails, render as opaque
493         if (renderSlotId == INVALID_RENDER_SLOT_ID) {
494             renderSlotId = materialRenderSlots_.defaultOpaqueRenderSlot;
495         }
496         renderSlotData[renderSlotCount].renderSlotId = renderSlotId;
497         renderSlotCount++;
498 
499         if (matData.renderMaterialFlags & RenderMaterialFlagBits::RENDER_MATERIAL_SHADOW_CASTER_BIT) {
500             renderSlotData[renderSlotCount].renderSlotId = materialRenderSlots_.defaultDepthRenderSlot;
501             renderSlotData[renderSlotCount].shader = matData.depthShader.shader;
502             renderSlotData[renderSlotCount].graphicsState = matData.depthShader.graphicsState;
503             renderSlotCount++;
504         }
505         AddSubmesh(submesh, { renderSlotData, renderSlotCount });
506     }
507 }
508 
AddSubmesh(const RenderSubmesh & submesh,const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders)509 void RenderDataStoreDefaultMaterial::AddSubmesh(
510     const RenderSubmesh& submesh, const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders)
511 {
512 #if (CORE3D_VALIDATION_ENABLED == 1)
513     ValidateSubmesh(submesh, customResourceData_);
514 #endif
515 
516     const uint32_t submeshIndex = static_cast<uint32_t>(submeshes_.size());
517     submeshes_.push_back(submesh);
518     auto& currSubmesh = submeshes_.back();
519     if (currSubmesh.meshIndex >= static_cast<uint32_t>(meshData_.size())) {
520         CORE_LOG_W("invalid mesh index (%u) given", currSubmesh.meshIndex);
521         currSubmesh.meshIndex = 0;
522     }
523     if ((currSubmesh.submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_SKIN_BIT) &&
524         (currSubmesh.skinJointIndex >= static_cast<uint32_t>(submeshJointMatrixIndices_.size()))) {
525         CORE_LOG_W("invalid skin joint index (%u) given", currSubmesh.skinJointIndex);
526         currSubmesh.skinJointIndex = RenderSceneDataConstants::INVALID_INDEX;
527     }
528 
529     if (currSubmesh.materialIndex >= static_cast<uint32_t>(materialAllUniforms_.size())) {
530         // NOTE: shouldn't come here with basic usage
531         currSubmesh.materialIndex = AddMaterialData({}, {}, {}, {});
532     } else {
533         CORE_ASSERT(materialData_.size() == materialAllUniforms_.size());
534     }
535     const RenderDataDefaultMaterial::MaterialData& perMaterialData = materialData_[currSubmesh.materialIndex];
536 
537     auto submeshRenderMaterialFlags = perMaterialData.renderMaterialFlags;
538     ExtentRenderMaterialFlagsFromSubmeshValues(currSubmesh.submeshFlags, submeshRenderMaterialFlags);
539 
540     const uint32_t renderHash =
541         HashSubmeshMaterials(perMaterialData.materialType, submeshRenderMaterialFlags, currSubmesh.submeshFlags);
542     submeshMaterialFlags_.push_back(
543         RenderDataDefaultMaterial::SubmeshMaterialFlags { perMaterialData.materialType, currSubmesh.submeshFlags,
544             perMaterialData.extraMaterialRenderingFlags, submeshRenderMaterialFlags, renderHash });
545 
546     const uint16_t renderSortLayerHash = (static_cast<uint16_t>(currSubmesh.renderSortLayer) << 8u) |
547                                          (static_cast<uint16_t>(currSubmesh.renderSortLayerOrder) & 0xffu);
548     // add submeshs to slots
549     for (const auto& slotRef : renderSlotAndShaders) {
550         SlotSubmeshData& dataRef = slotToSubmeshIndices_[slotRef.renderSlotId];
551         dataRef.indices.push_back(submeshIndex);
552         // hash for sorting (material index is certain for the same material)
553         // shader and gfx state is not needed, because it's already in the material, or they are defaults
554         // inverse winding can affect the final graphics state but it's in renderHash
555         // we has with material index, and render hash
556         // id generation does not matter to us, because this is per frame
557         uint32_t renderSortHash = currSubmesh.materialIndex;
558         HashCombine32Bit(renderSortHash, renderHash);
559         dataRef.materialData.push_back(
560             { renderSortLayerHash, renderSortHash, submeshRenderMaterialFlags, slotRef.shader, slotRef.graphicsState });
561 
562         dataRef.objectCounts.submeshCount++;
563         if (currSubmesh.skinJointIndex != RenderSceneDataConstants::INVALID_INDEX) {
564             dataRef.objectCounts.skinCount++;
565         }
566         if ((currSubmesh.customResourcesIndex != RenderSceneDataConstants::INVALID_INDEX) &&
567             (currSubmesh.customResourcesIndex < static_cast<uint32_t>(customResourceData_.size()))) {
568             customResourceData_[currSubmesh.customResourcesIndex].shaderHandle = perMaterialData.materialShader.shader;
569         }
570     }
571 }
572 
SetRenderSlots(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType,const BASE_NS::array_view<const uint32_t> renderSlotIds)573 void RenderDataStoreDefaultMaterial::SetRenderSlots(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType,
574     const BASE_NS::array_view<const uint32_t> renderSlotIds)
575 {
576     uint64_t mask = 0;
577     for (const auto renderSlotId : renderSlotIds) {
578         mask |= 1ULL << uint64_t(renderSlotId);
579     }
580     if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_OPAQUE) {
581         materialRenderSlots_.opaqueMask = mask;
582     } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_TRANSLUCENT) {
583         materialRenderSlots_.translucentMask = mask;
584     } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_DEPTH) {
585         materialRenderSlots_.depthMask = mask;
586     }
587 }
588 
GetRenderSlotMask(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType) const589 uint64_t RenderDataStoreDefaultMaterial::GetRenderSlotMask(
590     const RenderDataDefaultMaterial::MaterialSlotType materialSlotType) const
591 {
592     uint64_t mask = 0;
593     if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_OPAQUE) {
594         mask = materialRenderSlots_.opaqueMask;
595     } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_TRANSLUCENT) {
596         mask = materialRenderSlots_.translucentMask;
597     } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_DEPTH) {
598         mask = materialRenderSlots_.depthMask;
599     }
600     return mask;
601 }
602 
GetRenderSlotIdFromMasks(const uint32_t renderSlotId) const603 uint32_t RenderDataStoreDefaultMaterial::GetRenderSlotIdFromMasks(const uint32_t renderSlotId) const
604 {
605     // compare to masks
606     const uint64_t renderSlotMask = 1ULL << uint64_t(renderSlotId);
607     uint32_t newRenderSlotId = renderSlotId;
608     if (renderSlotMask & materialRenderSlots_.opaqueMask) {
609         newRenderSlotId = materialRenderSlots_.defaultOpaqueRenderSlot;
610     } else if (renderSlotMask & materialRenderSlots_.translucentMask) {
611         newRenderSlotId = materialRenderSlots_.defaultTranslucentRenderSlot;
612     } else if (renderSlotMask & materialRenderSlots_.depthMask) {
613         newRenderSlotId = materialRenderSlots_.defaultDepthRenderSlot;
614     }
615     return newRenderSlotId;
616 }
617 
GetSlotSubmeshIndices(const uint32_t renderSlotId) const618 array_view<const uint32_t> RenderDataStoreDefaultMaterial::GetSlotSubmeshIndices(const uint32_t renderSlotId) const
619 {
620     const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
621     if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
622         const auto& slotRef = iter->second;
623         return slotRef.indices;
624     } else {
625         return {};
626     }
627 }
628 
629 array_view<const RenderDataDefaultMaterial::SlotMaterialData>
GetSlotSubmeshMaterialData(const uint32_t renderSlotId) const630 RenderDataStoreDefaultMaterial::GetSlotSubmeshMaterialData(const uint32_t renderSlotId) const
631 {
632     const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
633     if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
634         const auto& slotRef = iter->second;
635         return slotRef.materialData;
636     } else {
637         return {};
638     }
639 }
640 
GetSlotObjectCounts(const uint32_t renderSlotId) const641 RenderDataDefaultMaterial::ObjectCounts RenderDataStoreDefaultMaterial::GetSlotObjectCounts(
642     const uint32_t renderSlotId) const
643 {
644     const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
645     if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
646         return { static_cast<uint32_t>(meshData_.size()), iter->second.objectCounts.submeshCount,
647             iter->second.objectCounts.skinCount, static_cast<uint32_t>(materialAllUniforms_.size()) };
648     } else {
649         return {};
650     }
651 }
652 
GetObjectCounts() const653 RenderDataDefaultMaterial::ObjectCounts RenderDataStoreDefaultMaterial::GetObjectCounts() const
654 {
655     return {
656         static_cast<uint32_t>(meshData_.size()),
657         static_cast<uint32_t>(submeshes_.size()),
658         static_cast<uint32_t>(submeshJointMatrixIndices_.size()),
659         static_cast<uint32_t>(materialAllUniforms_.size()),
660     };
661 }
662 
GetSubmeshes() const663 array_view<const RenderSubmesh> RenderDataStoreDefaultMaterial::GetSubmeshes() const
664 {
665     return submeshes_;
666 }
667 
GetMeshData() const668 array_view<const RenderMeshData> RenderDataStoreDefaultMaterial::GetMeshData() const
669 {
670     return meshData_;
671 }
672 
673 array_view<const RenderDataDefaultMaterial::JointMatrixData>
GetMeshJointMatrices() const674 RenderDataStoreDefaultMaterial::GetMeshJointMatrices() const
675 {
676     return submeshJointMatrixIndices_;
677 }
678 
GetSubmeshJointMatrixData(const uint32_t skinJointIndex) const679 array_view<const Math::Mat4X4> RenderDataStoreDefaultMaterial::GetSubmeshJointMatrixData(
680     const uint32_t skinJointIndex) const
681 {
682     if (skinJointIndex < static_cast<uint32_t>(submeshJointMatrixIndices_.size())) {
683         const RenderDataDefaultMaterial::JointMatrixData& jm = submeshJointMatrixIndices_[skinJointIndex];
684         return array_view<const Math::Mat4X4>(jm.data, static_cast<size_t>(jm.count));
685     } else {
686         return {};
687     }
688 }
689 
690 array_view<const RenderDataDefaultMaterial::AllMaterialUniforms>
GetMaterialUniforms() const691 RenderDataStoreDefaultMaterial::GetMaterialUniforms() const
692 {
693     return materialAllUniforms_;
694 }
695 
GetMaterialHandles() const696 array_view<const RenderDataDefaultMaterial::MaterialHandles> RenderDataStoreDefaultMaterial::GetMaterialHandles() const
697 {
698     return materialHandles_;
699 }
700 
GetMaterialCustomPropertyData(const uint32_t materialIndex) const701 array_view<const uint8_t> RenderDataStoreDefaultMaterial::GetMaterialCustomPropertyData(
702     const uint32_t materialIndex) const
703 {
704     if ((!materialCustomPropertyData_.empty()) &&
705         (materialIndex < static_cast<uint32_t>(materialCustomPropertyOffsets_.size()))) {
706         const auto& offsets = materialCustomPropertyOffsets_[materialIndex];
707         CORE_ASSERT((offsets.offset + offsets.byteSize) <= materialCustomPropertyData_.size_in_bytes());
708         if ((offsets.offset + offsets.byteSize) <= materialCustomPropertyData_.size_in_bytes()) {
709             return { &materialCustomPropertyData_[offsets.offset], offsets.byteSize };
710         } else {
711             return {};
712         }
713     } else {
714         return {};
715     }
716 }
717 
718 array_view<const RenderDataDefaultMaterial::SubmeshMaterialFlags>
GetSubmeshMaterialFlags() const719 RenderDataStoreDefaultMaterial::GetSubmeshMaterialFlags() const
720 {
721     return submeshMaterialFlags_;
722 }
723 
724 array_view<const RenderDataDefaultMaterial::CustomResourceData>
GetCustomResourceHandles() const725 RenderDataStoreDefaultMaterial::GetCustomResourceHandles() const
726 {
727     return customResourceData_;
728 }
729 
GenerateRenderHash(const RenderDataDefaultMaterial::SubmeshMaterialFlags & flags) const730 uint32_t RenderDataStoreDefaultMaterial::GenerateRenderHash(
731     const RenderDataDefaultMaterial::SubmeshMaterialFlags& flags) const
732 {
733     return HashSubmeshMaterials(flags.materialType, flags.renderMaterialFlags, flags.submeshFlags);
734 }
735 
GetMaterialIndex(const uint64_t id) const736 uint32_t RenderDataStoreDefaultMaterial::GetMaterialIndex(const uint64_t id) const
737 {
738     auto searchId = HashMaterialId(id, static_cast<uint32_t>(1U));
739     if (const auto iter = materialIdToIndices_.find(searchId); iter != materialIdToIndices_.cend()) {
740         return iter->second.materialIndex;
741     } else {
742         return RenderSceneDataConstants::INVALID_INDEX;
743     }
744 }
745 
GetMaterialCustomResourceIndex(const uint64_t id) const746 uint32_t RenderDataStoreDefaultMaterial::GetMaterialCustomResourceIndex(const uint64_t id) const
747 {
748     auto searchId = HashMaterialId(id, static_cast<uint32_t>(1U));
749     if (const auto iter = materialIdToIndices_.find(searchId); iter != materialIdToIndices_.cend()) {
750         return iter->second.materialCustomResourceIndex;
751     } else {
752         return RenderSceneDataConstants::INVALID_INDEX;
753     }
754 }
755 
GetMaterialIndices(const uint64_t id) const756 RenderDataDefaultMaterial::MaterialIndices RenderDataStoreDefaultMaterial::GetMaterialIndices(const uint64_t id) const
757 {
758     auto searchId = HashMaterialId(id, static_cast<uint32_t>(1U));
759     if (const auto iter = materialIdToIndices_.find(searchId); iter != materialIdToIndices_.cend()) {
760         return iter->second;
761     } else {
762         return {};
763     }
764 }
765 
GetMaterialIndices(const uint64_t id,const uint32_t instanceCount) const766 RenderDataDefaultMaterial::MaterialIndices RenderDataStoreDefaultMaterial::GetMaterialIndices(
767     const uint64_t id, const uint32_t instanceCount) const
768 {
769     auto searchId = HashMaterialId(id, instanceCount);
770     if (const auto iter = materialIdToIndices_.find(searchId); iter != materialIdToIndices_.cend()) {
771         return iter->second;
772     } else {
773         return {};
774     }
775 }
776 
777 // for plugin / factory interface
Create(RENDER_NS::IRenderContext & renderContext,char const * name)778 RENDER_NS::IRenderDataStore* RenderDataStoreDefaultMaterial::Create(
779     RENDER_NS::IRenderContext& renderContext, char const* name)
780 {
781     return new RenderDataStoreDefaultMaterial(renderContext, name);
782 }
783 
Destroy(IRenderDataStore * instance)784 void RenderDataStoreDefaultMaterial::Destroy(IRenderDataStore* instance)
785 {
786     delete static_cast<RenderDataStoreDefaultMaterial*>(instance);
787 }
788 CORE3D_END_NAMESPACE()
789