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_node_default_material_objects.h"
17 
18 #include <3d/render/default_material_constants.h>
19 #include <3d/render/intf_render_data_store_default_material.h>
20 #include <base/containers/allocator.h>
21 #include <base/math/matrix.h>
22 #include <base/math/vector.h>
23 #include <core/log.h>
24 #include <core/namespace.h>
25 #include <render/datastore/intf_render_data_store.h>
26 #include <render/datastore/intf_render_data_store_manager.h>
27 #include <render/device/intf_gpu_resource_manager.h>
28 #include <render/device/pipeline_layout_desc.h>
29 #include <render/nodecontext/intf_render_node_context_manager.h>
30 #include <render/resource_handle.h>
31 
32 namespace {
33 #include <3d/shaders/common/3d_dm_structures_common.h>
34 } // namespace
35 
36 CORE3D_BEGIN_NAMESPACE()
37 using namespace BASE_NS;
38 using namespace RENDER_NS;
39 
40 namespace {
41 constexpr uint32_t UBO_BIND_OFFSET_ALIGNMENT { PipelineLayoutConstants::MIN_UBO_BIND_OFFSET_ALIGNMENT_BYTE_SIZE };
42 constexpr uint32_t MIN_UBO_OBJECT_COUNT { CORE_UNIFORM_BUFFER_MAX_BIND_SIZE / UBO_BIND_OFFSET_ALIGNMENT };
43 
Align(size_t value,size_t align)44 constexpr size_t Align(size_t value, size_t align)
45 {
46     if (value == 0) {
47         return 0;
48     }
49     return ((value + align) / align) * align;
50 }
51 } // namespace
52 
InitNode(IRenderNodeContextManager & renderNodeContextMgr)53 void RenderNodeDefaultMaterialObjects::InitNode(IRenderNodeContextManager& renderNodeContextMgr)
54 {
55     renderNodeContextMgr_ = &renderNodeContextMgr;
56 
57     const auto& renderNodeGraphData = renderNodeContextMgr_->GetRenderNodeGraphData();
58     stores_ = RenderNodeSceneUtil::GetSceneRenderDataStores(
59         renderNodeContextMgr, renderNodeGraphData.renderNodeGraphDataStoreName);
60     ProcessBuffers({ 1u, 1u, 1u, 1u });
61 }
62 
PreExecuteFrame()63 void RenderNodeDefaultMaterialObjects::PreExecuteFrame()
64 {
65     // re-create needed gpu resources
66     const auto& renderDataStoreMgr = renderNodeContextMgr_->GetRenderDataStoreManager();
67     const IRenderDataStoreDefaultMaterial* dataStoreMaterial = static_cast<IRenderDataStoreDefaultMaterial*>(
68         renderDataStoreMgr.GetRenderDataStore(stores_.dataStoreNameMaterial));
69     // we need to have all buffers created (reason to have at least 1u)
70     if (dataStoreMaterial) {
71         const auto dsOc = dataStoreMaterial->GetObjectCounts();
72         const ObjectCounts oc {
73             Math::max(dsOc.meshCount, 1u),
74             Math::max(dsOc.submeshCount, 1u),
75             Math::max(dsOc.skinCount, 1u),
76             Math::max(dsOc.materialCount, 1u),
77         };
78         ProcessBuffers(oc);
79     } else {
80         ProcessBuffers({ 1u, 1u, 1u, 1u });
81     }
82 }
83 
ExecuteFrame(IRenderCommandList & cmdList)84 void RenderNodeDefaultMaterialObjects::ExecuteFrame(IRenderCommandList& cmdList)
85 {
86     const auto& renderDataStoreMgr = renderNodeContextMgr_->GetRenderDataStoreManager();
87     const auto* dataStoreMaterial = static_cast<IRenderDataStoreDefaultMaterial*>(
88         renderDataStoreMgr.GetRenderDataStore(stores_.dataStoreNameMaterial));
89 
90     if (dataStoreMaterial) {
91         UpdateBuffers(*dataStoreMaterial);
92     } else {
93         CORE_LOG_E("invalid render data store in RenderNodeDefaultMaterialObjects");
94     }
95 }
96 
UpdateBuffers(const IRenderDataStoreDefaultMaterial & dataStoreMaterial)97 void RenderNodeDefaultMaterialObjects::UpdateBuffers(const IRenderDataStoreDefaultMaterial& dataStoreMaterial)
98 {
99     UpdateMeshBuffer(dataStoreMaterial);
100     UpdateSkinBuffer(dataStoreMaterial);
101     UpdateMaterialBuffers(dataStoreMaterial);
102 }
103 
UpdateMeshBuffer(const IRenderDataStoreDefaultMaterial & dataStoreMaterial)104 void RenderNodeDefaultMaterialObjects::UpdateMeshBuffer(const IRenderDataStoreDefaultMaterial& dataStoreMaterial)
105 {
106     IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
107     // mesh data is copied to single buffer with UBO_BIND_OFFSET_ALIGNMENT alignments
108     if (auto meshDataPtr = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.mesh.GetHandle())); meshDataPtr) {
109         constexpr uint32_t meshByteSize = sizeof(DefaultMaterialSingleMeshStruct);
110         CORE_STATIC_ASSERT(meshByteSize >= UBO_BIND_OFFSET_ALIGNMENT);
111         CORE_STATIC_ASSERT(sizeof(RenderMeshData) == sizeof(DefaultMaterialSingleMeshStruct));
112         const auto* meshDataPtrEnd = meshDataPtr + meshByteSize * objectCounts_.maxMeshCount;
113         if (const auto meshData = dataStoreMaterial.GetMeshData(); !meshData.empty()) {
114             // clone all at once, they are in order
115             const size_t cloneByteSize = meshData.size_bytes();
116             if (!CloneData(meshDataPtr, size_t(meshDataPtrEnd - meshDataPtr), meshData.data(), cloneByteSize)) {
117                 CORE_LOG_I("meshData ubo copying failed");
118             }
119         }
120 
121         gpuResourceMgr.UnmapBuffer(ubos_.mesh.GetHandle());
122     }
123 }
124 
UpdateSkinBuffer(const IRenderDataStoreDefaultMaterial & dataStoreMaterial)125 void RenderNodeDefaultMaterialObjects::UpdateSkinBuffer(const IRenderDataStoreDefaultMaterial& dataStoreMaterial)
126 {
127     IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
128     // skindata is copied to a single buffer with sizeof(DefaultMaterialSkinStruct) offset
129     // skin offset for submesh is calculated submesh.skinIndex * sizeof(DefaultMaterialSkinStruct)
130     // NOTE: the size could be optimized, but render data store should calculate correct size with alignment
131     if (auto skinData = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.submeshSkin.GetHandle())); skinData) {
132         CORE_STATIC_ASSERT(
133             RenderDataDefaultMaterial::MAX_SKIN_MATRIX_COUNT * 2u == CORE_DEFAULT_MATERIAL_MAX_JOINT_COUNT);
134         const auto* skinDataEnd = skinData + sizeof(DefaultMaterialSkinStruct) * objectCounts_.maxSkinCount;
135         const auto meshJointMatrices = dataStoreMaterial.GetMeshJointMatrices();
136         for (const auto& jointRef : meshJointMatrices) {
137             // we copy first current frame matrices, then previous frame
138             const size_t copyCount = static_cast<size_t>(jointRef.count / 2u);
139             const size_t copySize = copyCount * sizeof(Math::Mat4X4);
140             const size_t ptrOffset = CORE_DEFAULT_MATERIAL_PREV_JOINT_OFFSET * sizeof(Math::Mat4X4);
141             if (!CloneData(skinData, size_t(skinDataEnd - skinData), jointRef.data, copySize)) {
142                 CORE_LOG_I("skinData ubo copying failed");
143             }
144             if (!CloneData(skinData + ptrOffset, size_t(skinDataEnd - (skinData + ptrOffset)),
145                     jointRef.data + copyCount, copySize)) {
146                 CORE_LOG_I("skinData ubo copying failed");
147             }
148             skinData = skinData + sizeof(DefaultMaterialSkinStruct);
149         }
150 
151         gpuResourceMgr.UnmapBuffer(ubos_.submeshSkin.GetHandle());
152     }
153 }
154 
UpdateMaterialBuffers(const IRenderDataStoreDefaultMaterial & dataStoreMaterial)155 void RenderNodeDefaultMaterialObjects::UpdateMaterialBuffers(const IRenderDataStoreDefaultMaterial& dataStoreMaterial)
156 {
157     IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
158     // material data is copied to single buffer with UBO_BIND_OFFSET_ALIGNMENT alignment
159     auto matFactorData = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.mat.GetHandle()));
160     auto matTransformData = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.matTransform.GetHandle()));
161     auto userMaterialData = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.userMat.GetHandle()));
162     if (matFactorData && matTransformData && userMaterialData) {
163         const auto* matFactorDataEnd = matFactorData + UBO_BIND_OFFSET_ALIGNMENT * objectCounts_.maxMaterialCount;
164         const auto* matTransformDataEnd = matTransformData + UBO_BIND_OFFSET_ALIGNMENT * objectCounts_.maxMaterialCount;
165         const auto* userMaterialDataEnd = userMaterialData + UBO_BIND_OFFSET_ALIGNMENT * objectCounts_.maxMaterialCount;
166         const auto materialUniforms = dataStoreMaterial.GetMaterialUniforms();
167         for (uint32_t matIdx = 0; matIdx < materialUniforms.size(); ++matIdx) {
168             const RenderDataDefaultMaterial::AllMaterialUniforms& uniforms = materialUniforms[matIdx];
169             if (!CloneData(matFactorData, size_t(matFactorDataEnd - matFactorData), &uniforms.factors,
170                     sizeof(RenderDataDefaultMaterial::MaterialUniforms))) {
171                 CORE_LOG_I("materialFactorData ubo copying failed");
172             }
173             if (!CloneData(matTransformData, size_t(matTransformDataEnd - matTransformData), &uniforms.transforms,
174                     sizeof(RenderDataDefaultMaterial::MaterialPackedUniforms))) {
175                 CORE_LOG_I("materialTransformData ubo copying failed");
176             }
177             const auto materialCustomProperties = dataStoreMaterial.GetMaterialCustomPropertyData(matIdx);
178             if (!materialCustomProperties.empty()) {
179                 CORE_ASSERT(materialCustomProperties.size_bytes() <= UBO_BIND_OFFSET_ALIGNMENT);
180                 if (!CloneData(userMaterialData, size_t(userMaterialDataEnd - userMaterialData),
181                         materialCustomProperties.data(), materialCustomProperties.size_bytes())) {
182                     CORE_LOG_I("userMaterialData ubo copying failed");
183                 }
184             }
185             matFactorData = matFactorData + UBO_BIND_OFFSET_ALIGNMENT;
186             matTransformData = matTransformData + UBO_BIND_OFFSET_ALIGNMENT;
187             userMaterialData = userMaterialData + UBO_BIND_OFFSET_ALIGNMENT;
188         }
189 
190         gpuResourceMgr.UnmapBuffer(ubos_.mat.GetHandle());
191         gpuResourceMgr.UnmapBuffer(ubos_.matTransform.GetHandle());
192         gpuResourceMgr.UnmapBuffer(ubos_.userMat.GetHandle());
193     }
194 }
195 
ProcessBuffers(const ObjectCounts & objectCounts)196 void RenderNodeDefaultMaterialObjects::ProcessBuffers(const ObjectCounts& objectCounts)
197 {
198     auto& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
199     constexpr uint32_t overEstimate { 16u };
200     constexpr uint32_t baseStructSize = CORE_UNIFORM_BUFFER_MAX_BIND_SIZE;
201     constexpr uint32_t singleComponentStructSize = UBO_BIND_OFFSET_ALIGNMENT;
202     const string_view us = stores_.dataStoreNameScene;
203     // instancing utilization for mesh and materials
204     if (objectCounts_.maxMeshCount < objectCounts.maxMeshCount) {
205         // mesh matrix uses max ubo bind size to utilize gpu instancing
206         CORE_STATIC_ASSERT(sizeof(DefaultMaterialMeshStruct) <= PipelineLayoutConstants::MAX_UBO_BIND_BYTE_SIZE);
207         objectCounts_.maxMeshCount =
208             objectCounts.maxMeshCount + (objectCounts.maxMeshCount / overEstimate) + MIN_UBO_OBJECT_COUNT;
209 
210         const uint32_t byteSize =
211             static_cast<uint32_t>(Align(objectCounts_.maxMeshCount * singleComponentStructSize, baseStructSize));
212         objectCounts_.maxMeshCount = (byteSize / singleComponentStructSize) - MIN_UBO_OBJECT_COUNT;
213         CORE_ASSERT((int32_t(byteSize / singleComponentStructSize) - int32_t(MIN_UBO_OBJECT_COUNT)) > 0);
214 
215         ubos_.mesh = gpuResourceMgr.Create(us + DefaultMaterialMaterialConstants::MESH_DATA_BUFFER_NAME,
216             GpuBufferDesc { CORE_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
217                 (CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT | CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT),
218                 CORE_ENGINE_BUFFER_CREATION_DYNAMIC_RING_BUFFER, byteSize });
219     }
220     if (objectCounts_.maxSkinCount < objectCounts.maxSkinCount) {
221         objectCounts_.maxSkinCount = objectCounts.maxSkinCount + (objectCounts.maxSkinCount / overEstimate);
222 
223         ubos_.submeshSkin = gpuResourceMgr.Create(us + DefaultMaterialMaterialConstants::SKIN_DATA_BUFFER_NAME,
224             GpuBufferDesc { CORE_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
225                 (CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT | CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT),
226                 CORE_ENGINE_BUFFER_CREATION_DYNAMIC_RING_BUFFER,
227                 static_cast<uint32_t>(sizeof(DefaultMaterialSkinStruct)) * objectCounts_.maxSkinCount });
228     }
229     if (objectCounts_.maxMaterialCount < objectCounts.maxMaterialCount) {
230         CORE_STATIC_ASSERT(sizeof(RenderDataDefaultMaterial::MaterialUniforms) <= UBO_BIND_OFFSET_ALIGNMENT);
231         objectCounts_.maxMaterialCount =
232             objectCounts.maxMaterialCount + (objectCounts.maxMaterialCount / overEstimate) + MIN_UBO_OBJECT_COUNT;
233 
234         const uint32_t byteSize =
235             static_cast<uint32_t>(Align(objectCounts_.maxMaterialCount * singleComponentStructSize, baseStructSize));
236         objectCounts_.maxMaterialCount = (byteSize / singleComponentStructSize) - MIN_UBO_OBJECT_COUNT;
237         CORE_ASSERT((int32_t(byteSize / singleComponentStructSize) - int32_t(MIN_UBO_OBJECT_COUNT)) > 0);
238 
239         const GpuBufferDesc bufferDesc { CORE_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
240             (CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT | CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT),
241             CORE_ENGINE_BUFFER_CREATION_DYNAMIC_RING_BUFFER, byteSize };
242 
243         ubos_.mat = gpuResourceMgr.Create(us + DefaultMaterialMaterialConstants::MATERIAL_DATA_BUFFER_NAME, bufferDesc);
244 
245         ubos_.matTransform = gpuResourceMgr.Create(
246             us + DefaultMaterialMaterialConstants::MATERIAL_TRANSFORM_DATA_BUFFER_NAME, bufferDesc);
247 
248         ubos_.userMat =
249             gpuResourceMgr.Create(us + DefaultMaterialMaterialConstants::MATERIAL_USER_DATA_BUFFER_NAME, bufferDesc);
250     }
251 }
252 
253 // for plugin / factory interface
Create()254 RENDER_NS::IRenderNode* RenderNodeDefaultMaterialObjects::Create()
255 {
256     return new RenderNodeDefaultMaterialObjects();
257 }
258 
Destroy(IRenderNode * instance)259 void RenderNodeDefaultMaterialObjects::Destroy(IRenderNode* instance)
260 {
261     delete static_cast<RenderNodeDefaultMaterialObjects*>(instance);
262 }
263 CORE3D_END_NAMESPACE()
264