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_graph_loader.h"
17 
18 #include <cctype>
19 #include <charconv>
20 #include <cstring>
21 
22 #include <base/containers/fixed_string.h>
23 #include <base/containers/vector.h>
24 #include <base/math/matrix_util.h>
25 #include <core/io/intf_file.h>
26 #include <core/io/intf_file_manager.h>
27 #include <render/namespace.h>
28 #include <render/nodecontext/intf_render_node_graph_manager.h>
29 #include <render/render_data_structures.h>
30 
31 #include "json_format_serialization.h"
32 #include "json_util.h"
33 #include "util/log.h"
34 
35 using namespace BASE_NS;
36 using namespace CORE_NS;
37 
38 RENDER_BEGIN_NAMESPACE()
39 CORE_JSON_SERIALIZE_ENUM(GpuQueue::QueueType,
40     { { GpuQueue::QueueType::UNDEFINED, nullptr }, { GpuQueue::QueueType::GRAPHICS, "graphics" },
41         { GpuQueue::QueueType::COMPUTE, "compute" }, { GpuQueue::QueueType::TRANSFER, "transfer" } })
42 
43 namespace {
44 constexpr size_t VERSION_SIZE { 5u };
45 constexpr uint32_t VERSION_MAJOR { 22u };
46 
ParseQueueWaitSignals(const json::value & node,RenderNodeDesc & data,IRenderNodeGraphLoader::LoadResult & nodeResult)47 void ParseQueueWaitSignals(
48     const json::value& node, RenderNodeDesc& data, IRenderNodeGraphLoader::LoadResult& nodeResult)
49 {
50     if (auto const queueSignals = node.find("gpuQueueWaitSignals"); queueSignals) {
51         if (auto const typeNames = queueSignals->find("typeNames"); typeNames) {
52             if (typeNames->is_array()) {
53                 FromJson(*typeNames, data.description.gpuQueueWaitForSignals.typeNames);
54             }
55         }
56         if (auto const nodeNames = queueSignals->find("nodeNames"); nodeNames) {
57             if (nodeNames->is_array()) {
58                 FromJson(*nodeNames, data.description.gpuQueueWaitForSignals.nodeNames);
59             }
60         }
61 #if (RENDER_VALIDATION_ENABLED == 1)
62         if (data.description.gpuQueueWaitForSignals.nodeNames.size() >
63             PipelineStateConstants::MAX_RENDER_NODE_GPU_WAIT_SIGNALS) {
64             nodeResult.error += "gpuQueueWaitSignal count must be smaller than" +
65                                 to_string(PipelineStateConstants::MAX_RENDER_NODE_GPU_WAIT_SIGNALS) + ")\n";
66         }
67         if (data.description.gpuQueueWaitForSignals.typeNames.size() >
68             PipelineStateConstants::MAX_RENDER_NODE_GPU_WAIT_SIGNALS) {
69             nodeResult.error += "gpuQueueWaitSignal count must be smaller than" +
70                                 to_string(PipelineStateConstants::MAX_RENDER_NODE_GPU_WAIT_SIGNALS) + ")\n";
71         }
72 #endif
73     }
74 }
75 
ParseRenderNode(const json::value & node,RenderNodeDesc & data)76 IRenderNodeGraphLoader::LoadResult ParseRenderNode(const json::value& node, RenderNodeDesc& data)
77 {
78     IRenderNodeGraphLoader::LoadResult nodeResult;
79 
80     SafeGetJsonValue(node, "typeName", nodeResult.error, data.typeName);
81     SafeGetJsonValue(node, "nodeName", nodeResult.error, data.nodeName);
82     // render node specific json
83     data.nodeJson = json::to_string(node);
84     SafeGetJsonValue(node, "nodeDataStoreName", nodeResult.error, data.description.nodeDataStoreName);
85 
86     if (auto const queue = node.find("queue"); queue) {
87         SafeGetJsonEnum(*queue, "type", nodeResult.error, data.description.queue.type);
88         SafeGetJsonValue(*queue, "index", nodeResult.error, data.description.queue.index);
89     }
90 
91     if (auto const cpuDependencies = node.find("cpuDependencies"); cpuDependencies) {
92         if (auto const typeNames = cpuDependencies->find("typeNames"); typeNames) {
93             if (typeNames->is_array()) {
94                 FromJson(*typeNames, data.description.cpuDependencies.typeNames);
95             }
96         }
97         if (auto const nodeNames = cpuDependencies->find("nodeNames"); nodeNames) {
98             if (nodeNames->is_array()) {
99                 FromJson(*nodeNames, data.description.cpuDependencies.nodeNames);
100             }
101         }
102     }
103     ParseQueueWaitSignals(node, data, nodeResult);
104 
105     return nodeResult;
106 }
107 
ParseOutputResources(const json::value & node,RenderNodeGraphOutputResource & data)108 IRenderNodeGraphLoader::LoadResult ParseOutputResources(const json::value& node, RenderNodeGraphOutputResource& data)
109 {
110     IRenderNodeGraphLoader::LoadResult nodeResult;
111 
112     SafeGetJsonValue(node, "nodeName", nodeResult.error, data.nodeName);
113     SafeGetJsonValue(node, "name", nodeResult.error, data.name);
114 
115     return nodeResult;
116 }
117 
CompatibilityCheck(const json::value & json,RenderNodeGraphLoader::LoadResult & result)118 void CompatibilityCheck(const json::value& json, RenderNodeGraphLoader::LoadResult& result)
119 {
120     string ver;
121     string type;
122     uint32_t verMajor { ~0u };
123     uint32_t verMinor { ~0u };
124     if (const json::value* iter = json.find("compatibility_info"); iter) {
125         SafeGetJsonValue(*iter, "version", result.error, ver);
126         SafeGetJsonValue(*iter, "type", result.error, type);
127         if (ver.size() == VERSION_SIZE) {
128             if (const auto delim = ver.find('.'); delim != string::npos) {
129                 std::from_chars(ver.data(), ver.data() + delim, verMajor);
130                 std::from_chars(ver.data() + delim + 1, ver.data() + ver.size(), verMinor);
131             }
132         }
133     }
134     if ((type != "rendernodegraph") || (verMajor != VERSION_MAJOR)) {
135         result.error += "invalid render node graph type (" + type + ") and/or version (" + ver + ").";
136         result.success = false;
137     }
138 }
139 } // namespace
140 
RenderNodeGraphLoader(IFileManager & fileManager)141 RenderNodeGraphLoader::RenderNodeGraphLoader(IFileManager& fileManager) : fileManager_(fileManager) {}
142 
Load(const string_view uri)143 RenderNodeGraphLoader::LoadResult RenderNodeGraphLoader::Load(const string_view uri)
144 {
145     IFile::Ptr file = fileManager_.OpenFile(uri);
146     if (!file) {
147         PLUGIN_LOG_D("Error loading '%s'", string(uri).c_str());
148         return LoadResult("Failed to open file.");
149     }
150 
151     const uint64_t byteLength = file->GetLength();
152 
153     string raw(static_cast<size_t>(byteLength), string::value_type());
154     if (file->Read(raw.data(), byteLength) != byteLength) {
155         PLUGIN_LOG_D("Error loading '%s'", string(uri).c_str());
156         return LoadResult("Failed to read file.");
157     }
158 
159     return LoadString(uri, raw);
160 }
161 
LoadString(const string_view jsonString)162 RenderNodeGraphLoader::LoadResult RenderNodeGraphLoader::LoadString(const string_view jsonString)
163 {
164     return LoadString("", jsonString);
165 }
166 
LoadString(const string_view uri,const string_view jsonString)167 RenderNodeGraphLoader::LoadResult RenderNodeGraphLoader::LoadString(const string_view uri, const string_view jsonString)
168 {
169     if (const auto json = json::parse(jsonString.data()); json) {
170         LoadResult finalResult;
171         CompatibilityCheck(json, finalResult);
172         if (!finalResult.success) {
173             return finalResult; // compatibility check failed
174         }
175 
176         string renderNodeGraphName;
177         string renderNodeGraphDataStoreName;
178         SafeGetJsonValue(json, "renderNodeGraphName", finalResult.error, renderNodeGraphName);
179         SafeGetJsonValue(json, "renderNodeGraphDataStoreName", finalResult.error, renderNodeGraphDataStoreName);
180 
181         vector<RenderNodeDesc> nodeDescriptors;
182         if (const auto nodes = json.find("nodes"); nodes) {
183             if (nodes->is_array()) {
184                 nodeDescriptors.reserve(nodes->array_.size());
185                 for (auto const& node : nodes->array_) {
186                     RenderNodeDesc data;
187                     LoadResult nodeResult = ParseRenderNode(node, data);
188                     if (nodeResult.error.empty()) {
189                         nodeDescriptors.push_back(move(data));
190                     } else {
191                         finalResult.error += nodeResult.error;
192                     }
193                 }
194             } else {
195                 finalResult.error += "\"nodes\" must to be an array.";
196             }
197         }
198         vector<RenderNodeGraphOutputResource> outputResources;
199         if (const auto nodes = json.find("renderNodeGraphOutputResources"); nodes) {
200             outputResources.reserve(nodes->array_.size());
201             for (auto const& node : nodes->array_) {
202                 RenderNodeGraphOutputResource data;
203                 LoadResult nodeResult = ParseOutputResources(node, data);
204                 if (nodeResult.error.empty()) {
205                     outputResources.push_back(move(data));
206                 } else {
207                     finalResult.error += nodeResult.error;
208                 }
209             }
210         }
211 
212         finalResult.success = finalResult.error.empty();
213         if (finalResult.error.empty()) {
214             finalResult.desc.renderNodeGraphName = renderNodeGraphName;
215             finalResult.desc.renderNodeGraphDataStoreName = renderNodeGraphDataStoreName;
216             finalResult.desc.renderNodeGraphUri = uri;
217             finalResult.desc.nodes = move(nodeDescriptors);
218             finalResult.desc.outputResources = move(outputResources);
219         }
220 
221         return finalResult;
222     } else {
223         return LoadResult("Invalid render node graph json file.");
224     }
225 }
226 RENDER_END_NAMESPACE()
227