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 "pipeline_layout_loader.h"
17 
18 #include <base/math/mathf.h>
19 #include <core/io/intf_file_manager.h>
20 #include <render/device/pipeline_layout_desc.h>
21 #include <render/namespace.h>
22 
23 #include "json_format_serialization.h"
24 #include "json_util.h"
25 #include "util/log.h"
26 
27 using namespace BASE_NS;
28 using namespace CORE_NS;
29 
30 RENDER_BEGIN_NAMESPACE()
31 // clang-format off
32 CORE_JSON_SERIALIZE_ENUM(ShaderStageFlagBits,
33     {
34         { (ShaderStageFlagBits)0, nullptr },
35         { ShaderStageFlagBits::CORE_SHADER_STAGE_VERTEX_BIT, "vertex_bit" },
36         { ShaderStageFlagBits::CORE_SHADER_STAGE_FRAGMENT_BIT, "fragment_bit" },
37         { ShaderStageFlagBits::CORE_SHADER_STAGE_COMPUTE_BIT, "compute_bit" },
38     })
39 
40 CORE_JSON_SERIALIZE_ENUM(DescriptorType,
41     {
42         { DescriptorType::CORE_DESCRIPTOR_TYPE_MAX_ENUM, nullptr }, // default
43         { DescriptorType::CORE_DESCRIPTOR_TYPE_SAMPLER, "sampler" },
44         { DescriptorType::CORE_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, "combined_image_sampler" },
45         { DescriptorType::CORE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, "sampled_image" },
46         { DescriptorType::CORE_DESCRIPTOR_TYPE_STORAGE_IMAGE, "storage_image" },
47         { DescriptorType::CORE_DESCRIPTOR_TYPE_UNIFORM_BUFFER, "uniform_texel_buffer" },
48         { DescriptorType::CORE_DESCRIPTOR_TYPE_STORAGE_BUFFER, "storage_texel_buffer" },
49         { DescriptorType::CORE_DESCRIPTOR_TYPE_UNIFORM_BUFFER, "uniform_buffer" },
50         { DescriptorType::CORE_DESCRIPTOR_TYPE_STORAGE_BUFFER, "storage_buffer" },
51         { DescriptorType::CORE_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, "uniform_buffer_dynamic" },
52         { DescriptorType::CORE_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, "storage_buffer_dynamic" },
53         { DescriptorType::CORE_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, "input_attachment" },
54         { DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE, "acceleration_structure" },
55     })
56 // clang-format on
57 void FromJson(const json::value& jsonData, JsonContext<DescriptorSetLayoutBinding>& context)
58 {
59     SafeGetJsonValue(jsonData, "binding", context.error, context.data.binding);
60     SafeGetJsonEnum(jsonData, "descriptorType", context.error, context.data.descriptorType);
61     SafeGetJsonValue(jsonData, "descriptorCount", context.error, context.data.descriptorCount);
62     SafeGetJsonBitfield<ShaderStageFlagBits>(
63         jsonData, "shaderStageFlags", context.error, context.data.shaderStageFlags);
64 }
65 
FromJson(const json::value & jsonData,JsonContext<DescriptorSetLayout> & context)66 void FromJson(const json::value& jsonData, JsonContext<DescriptorSetLayout>& context)
67 {
68     SafeGetJsonValue(jsonData, "set", context.error, context.data.set);
69 
70     PipelineLayoutLoader::LoadResult loadResult;
71     ParseArray<decltype(context.data.bindings)::value_type>(jsonData, "bindings", context.data.bindings, loadResult);
72     context.error = loadResult.error;
73     // NOTE: does not fetch descriptor set arrays
74 }
75 
Load(const json::value & jsonData,const string_view uri,PipelineLayout & pl)76 PipelineLayoutLoader::LoadResult Load(const json::value& jsonData, const string_view uri, PipelineLayout& pl)
77 {
78     PipelineLayoutLoader::LoadResult result;
79     pl = {}; // reset
80 
81     if (const auto pcIter = jsonData.find("pushConstant"); pcIter) {
82         SafeGetJsonValue(*pcIter, "size", result.error, pl.pushConstant.byteSize);
83         SafeGetJsonValue(*pcIter, "byteSize", result.error, pl.pushConstant.byteSize);
84         SafeGetJsonBitfield<ShaderStageFlagBits>(
85             *pcIter, "shaderStageFlags", result.error, pl.pushConstant.shaderStageFlags);
86 #if (RENDER_VALIDATION_ENABLED == 1)
87         if (pl.pushConstant.byteSize > PipelineLayoutConstants::MAX_PUSH_CONSTANT_BYTE_SIZE) {
88             PLUGIN_LOG_W("RENDER_VALIDATION: Invalid push constant size clamped (name:%s). push constant size %u <= %u",
89                 uri.data(), pl.pushConstant.byteSize, PipelineLayoutConstants::MAX_PUSH_CONSTANT_BYTE_SIZE);
90         }
91 #endif
92         pl.pushConstant.byteSize =
93             Math::min(PipelineLayoutConstants::MAX_PUSH_CONSTANT_BYTE_SIZE, pl.pushConstant.byteSize);
94     }
95 
96     vector<DescriptorSetLayout> descriptorSetLayouts;
97     ParseArray<decltype(descriptorSetLayouts)::value_type>(
98         jsonData, "descriptorSetLayouts", descriptorSetLayouts, result);
99     if (!descriptorSetLayouts.empty()) {
100         const uint32_t inputDescriptorSetCount = static_cast<uint32_t>(descriptorSetLayouts.size());
101 #if (RENDER_VALIDATION_ENABLED == 1)
102         if (inputDescriptorSetCount > PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT) {
103             PLUGIN_LOG_W("RENDER_VALIDATION: Invalid pipeline layout sizes clamped (name:%s). Set count %u <= %u",
104                 uri.data(), inputDescriptorSetCount, PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT);
105         }
106         for (const auto& descRef : descriptorSetLayouts) {
107             if (descRef.bindings.size() > PipelineLayoutConstants::MAX_DESCRIPTOR_SET_BINDING_COUNT) {
108                 PLUGIN_LOG_W(
109                     "RENDER_VALIDATION: Binding count exceeds the maximum (name:%s). Binding count count %u <= %u",
110                     uri.data(), static_cast<uint32_t>(descRef.bindings.size()),
111                     PipelineLayoutConstants::MAX_DESCRIPTOR_SET_BINDING_COUNT);
112             }
113             for (const auto& bindingRef : descRef.bindings) {
114                 if (bindingRef.descriptorType == DescriptorType::CORE_DESCRIPTOR_TYPE_MAX_ENUM) {
115                     PLUGIN_LOG_W("RENDER_VALIDATION: Unknown descriptor type (name:%s) (set:%u, binding:%u).",
116                         uri.data(), descRef.set, bindingRef.binding);
117                 }
118             }
119         }
120 #endif
121         // pipeline layout descriptor sets might have gaps and only some sets defined
122         for (uint32_t idx = 0; idx < inputDescriptorSetCount; ++idx) {
123             const uint32_t setIndex = descriptorSetLayouts[idx].set;
124             if (setIndex < PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT) {
125                 pl.descriptorSetLayouts[setIndex] = move(descriptorSetLayouts[idx]);
126                 pl.descriptorSetCount++;
127             }
128         }
129         // reassure
130         pl.descriptorSetCount = Math::min(pl.descriptorSetCount, PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT);
131     } else {
132         result.error += "invalid descriptor set layout count";
133     }
134 
135     result.success = result.error.empty();
136     if (!result.success) {
137         PLUGIN_LOG_E("error loading pipeline layout from json: %s", result.error.c_str());
138     }
139 
140     return result;
141 }
142 
GetUri() const143 string_view PipelineLayoutLoader::GetUri() const
144 {
145     return uri_;
146 }
147 
GetPipelineLayout() const148 const PipelineLayout& PipelineLayoutLoader::GetPipelineLayout() const
149 {
150     return pipelineLayout_;
151 }
152 
Load(const string_view jsonString)153 PipelineLayoutLoader::LoadResult PipelineLayoutLoader::Load(const string_view jsonString)
154 {
155     if (json::value jsonData = json::parse(jsonString.data()); jsonData) {
156         return RENDER_NS::Load(jsonData, uri_, pipelineLayout_);
157     }
158     return LoadResult("Invalid json file.");
159 }
160 
Load(IFileManager & fileManager,const string_view uri)161 PipelineLayoutLoader::LoadResult PipelineLayoutLoader::Load(IFileManager& fileManager, const string_view uri)
162 {
163     uri_ = uri;
164 
165     IFile::Ptr file = fileManager.OpenFile(uri);
166     if (!file) {
167         PLUGIN_LOG_E("Error loading '%s'", string(uri).c_str());
168         return LoadResult("Failed to open file.");
169     }
170 
171     const uint64_t byteLength = file->GetLength();
172 
173     string raw;
174     raw.resize(static_cast<size_t>(byteLength));
175 
176     if (file->Read(raw.data(), byteLength) != byteLength) {
177         PLUGIN_LOG_E("Error loading '%s'", string(uri).c_str());
178         return LoadResult("Failed to read file.");
179     }
180 
181     return Load(string_view(raw));
182 }
183 RENDER_END_NAMESPACE()
184