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