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 "shader_data_loader.h"
17
18 #include <charconv>
19
20 #include <base/containers/string.h>
21 #include <core/io/intf_file_manager.h>
22 #include <core/namespace.h>
23
24 #include "json_util.h"
25 #include "shader_state_loader_util.h"
26 #include "util/log.h"
27
28 using namespace BASE_NS;
29 using namespace CORE_NS;
30
31 RENDER_BEGIN_NAMESPACE()
32 namespace {
33 constexpr size_t VERSION_SIZE { 5u };
34 constexpr uint32_t VERSION_MAJOR { 22u };
35
LoadState(const json::value & jsonData,GraphicsState & graphicsState,GraphicsStateFlags & stateFlags,ShaderDataLoader::LoadResult & result)36 void LoadState(const json::value& jsonData, GraphicsState& graphicsState, GraphicsStateFlags& stateFlags,
37 ShaderDataLoader::LoadResult& result)
38 {
39 {
40 ShaderStateLoaderUtil::ShaderStateResult ssr;
41 ShaderStateLoaderUtil::ParseSingleState(jsonData, ssr);
42 ShaderStateLoaderUtil::ParseStateFlags(jsonData, stateFlags, ssr);
43 if (ssr.res.success && (ssr.states.states.size() == 1u)) {
44 graphicsState = move(ssr.states.states[0u]);
45 } else {
46 result.error += ssr.res.error;
47 }
48 }
49 }
50
LoadSingleShaderVariant(const json::value & jsonData,const string & materialMetaData,IShaderManager::ShaderVariant & data,ShaderDataLoader::LoadResult & result)51 void LoadSingleShaderVariant(const json::value& jsonData, const string& materialMetaData,
52 IShaderManager::ShaderVariant& data, ShaderDataLoader::LoadResult& result)
53 {
54 SafeGetJsonValue(jsonData, "renderSlotDefaultShader", result.error, data.renderSlotDefaultShader);
55 SafeGetJsonValue(jsonData, "variantName", result.error, data.variantName);
56 SafeGetJsonValue(jsonData, "displayName", result.error, data.displayName);
57 SafeGetJsonValue(jsonData, "baseShader", result.error, data.addBaseShader);
58
59 string vertexShader { "" };
60 string fragmentShader { "" };
61 string computeShader { "" };
62
63 SafeGetJsonValue(jsonData, "vert", result.error, vertexShader);
64 SafeGetJsonValue(jsonData, "frag", result.error, fragmentShader);
65 SafeGetJsonValue(jsonData, "compute", result.error, computeShader);
66 if (computeShader.empty()) {
67 SafeGetJsonValue(jsonData, "comp", result.error, computeShader);
68 }
69
70 if (!vertexShader.empty()) {
71 data.shaders.push_back({ vertexShader, CORE_SHADER_STAGE_VERTEX_BIT });
72 }
73 if (!fragmentShader.empty()) {
74 data.shaders.push_back({ fragmentShader, CORE_SHADER_STAGE_FRAGMENT_BIT });
75 }
76 if (!computeShader.empty()) {
77 data.shaders.push_back({ computeShader, CORE_SHADER_STAGE_COMPUTE_BIT });
78 }
79
80 {
81 SafeGetJsonValue(jsonData, "slot", result.error, data.renderSlot);
82 SafeGetJsonValue(jsonData, "renderSlot", result.error, data.renderSlot);
83 }
84 SafeGetJsonValue(jsonData, "vertexInputDeclaration", result.error, data.vertexInputDeclaration);
85 SafeGetJsonValue(jsonData, "pipelineLayout", result.error, data.pipelineLayout);
86
87 if (result.success) {
88 data.shaderFileStr = json::to_string(jsonData);
89 if (const json::value* iter = jsonData.find("state"); iter) {
90 LoadState(*iter, data.graphicsState, data.stateFlags, result);
91 }
92 if (const json::value* iter = jsonData.find("materialMetadata"); iter) {
93 data.materialMetadata = json::to_string(*iter);
94 } else if (!materialMetaData.empty()) {
95 data.materialMetadata = materialMetaData;
96 }
97 }
98 }
99
LoadFunc(const string_view uri,const json::value & jsonData,string & baseCategory,vector<IShaderManager::ShaderVariant> & shaderVariants)100 ShaderDataLoader::LoadResult LoadFunc(const string_view uri, const json::value& jsonData, string& baseCategory,
101 vector<IShaderManager::ShaderVariant>& shaderVariants)
102 {
103 ShaderDataLoader::LoadResult result;
104 // compatibility check with early out
105 {
106 string ver;
107 string type;
108 uint32_t verMajor { ~0u };
109 uint32_t verMinor { ~0u };
110 if (const json::value* iter = jsonData.find("compatibility_info"); iter) {
111 SafeGetJsonValue(*iter, "type", result.error, type);
112 SafeGetJsonValue(*iter, "version", result.error, ver);
113 if (ver.size() == VERSION_SIZE) {
114 if (const auto delim = ver.find('.'); delim != string::npos) {
115 std::from_chars(ver.data(), ver.data() + delim, verMajor);
116 std::from_chars(ver.data() + delim + 1, ver.data() + ver.size(), verMinor);
117 }
118 }
119 }
120 if ((type != "shader") || (verMajor != VERSION_MAJOR)) {
121 result.error += "invalid shader type (" + type + ") and/or version (" + ver + ").";
122 result.success = false;
123 return result;
124 }
125 }
126
127 #if (RENDER_VALIDATION_ENABLED == 1)
128 {
129 string name;
130 SafeGetJsonValue(jsonData, "name", result.error, name);
131 if (!name.empty()) {
132 PLUGIN_LOG_W("RENDER_VALIDATION: name (%s) not supported in shader json", name.c_str());
133 }
134 }
135 // base shader
136 {
137 string baseShader;
138 SafeGetJsonValue(jsonData, "baseShader", result.error, baseShader);
139 if (!(baseShader.empty())) {
140 PLUGIN_LOG_W("RENDER_VALIDATION: baseShader supported only for variants (%s)", baseShader.c_str());
141 }
142 }
143 #endif
144 // category
145 SafeGetJsonValue(jsonData, "category", result.error, baseCategory);
146 // base materialMetaData
147 string materialMetaData;
148 if (const json::value* iter = jsonData.find("materialMetadata"); iter) {
149 materialMetaData = json::to_string(*iter);
150 }
151
152 // check all variants or use (older) single variant style
153 if (const json::value* iter = jsonData.find("shaders"); iter) {
154 if (iter->is_array()) {
155 for (const auto& variantRef : iter->array_) {
156 IShaderManager::ShaderVariant sv;
157 // add own base shader
158 sv.ownBaseShader = uri;
159 LoadSingleShaderVariant(variantRef, materialMetaData, sv, result);
160 if (result.error.empty()) {
161 shaderVariants.push_back(move(sv));
162 }
163 }
164 }
165 } else {
166 IShaderManager::ShaderVariant sv;
167 LoadSingleShaderVariant(jsonData, materialMetaData, sv, result);
168 if (result.error.empty()) {
169 shaderVariants.push_back(move(sv));
170 }
171 }
172
173 result.success = result.error.empty();
174 return result;
175 }
176 } // namespace
177
GetUri() const178 string_view ShaderDataLoader::GetUri() const
179 {
180 return uri_;
181 }
182
GetBaseCategory() const183 BASE_NS::string_view ShaderDataLoader::GetBaseCategory() const
184 {
185 return baseCategory_;
186 }
187
GetShaderVariants() const188 array_view<const IShaderManager::ShaderVariant> ShaderDataLoader::GetShaderVariants() const
189 {
190 return shaderVariants_;
191 }
192
Load(IFileManager & fileManager,const string_view uri)193 ShaderDataLoader::LoadResult ShaderDataLoader::Load(IFileManager& fileManager, const string_view uri)
194 {
195 uri_ = uri;
196 IFile::Ptr file = fileManager.OpenFile(uri);
197 if (!file) {
198 PLUGIN_LOG_D("Error loading '%s'", string(uri).c_str());
199 return LoadResult("Failed to open file.");
200 }
201
202 const uint64_t byteLength = file->GetLength();
203
204 string raw;
205 raw.resize(static_cast<size_t>(byteLength));
206
207 if (file->Read(raw.data(), byteLength) != byteLength) {
208 PLUGIN_LOG_D("Error loading '%s'", string(uri).c_str());
209 return LoadResult("Failed to read file.");
210 }
211
212 return Load(uri, move(raw));
213 }
214
Load(const string_view uri,string && jsonData)215 ShaderDataLoader::LoadResult ShaderDataLoader::Load(const string_view uri, string&& jsonData)
216 {
217 LoadResult result;
218 const auto json = json::parse(jsonData.data());
219 if (json) {
220 result = RENDER_NS::LoadFunc(uri, json, baseCategory_, shaderVariants_);
221 } else {
222 result.success = false;
223 result.error = "Invalid json file.";
224 }
225
226 return result;
227 }
228 RENDER_END_NAMESPACE()
229