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 "device/shader_reflection_data.h"
17
18 #include <algorithm>
19 #include <array>
20
21 #include <base/util/algorithm.h>
22
23 RENDER_BEGIN_NAMESPACE()
24 namespace {
25 constexpr uint8_t REFLECTION_TAG[] = { 'r', 'f', 'l', 1 };
26 struct ReflectionHeader {
27 uint8_t tag[sizeof(REFLECTION_TAG)];
28 uint16_t type;
29 uint16_t offsetPushConstants;
30 uint16_t offsetSpecializationConstants;
31 uint16_t offsetDescriptorSets;
32 uint16_t offsetInputs;
33 uint16_t offsetLocalSize;
34 };
35 constexpr size_t INDEX_PUSH_CONSTANTS = 0U;
36 constexpr size_t INDEX_SPECIALIZATION_CONSTANTS = 1U;
37 constexpr size_t INDEX_DESCRIPTOR_SETS = 2U;
38 constexpr size_t INDEX_INPUTS = 3U;
39 constexpr size_t INDEX_LOCAL_SIZE = 4U;
40 struct Data {
41 uint32_t index;
42 uint16_t offset;
43 uint16_t size;
44 };
45
CalculateDataSizes(const ReflectionHeader & header,uint32_t totalSize)46 std::array<Data, 5U> CalculateDataSizes(const ReflectionHeader& header, uint32_t totalSize)
47 {
48 std::array<Data, 5U> data {
49 Data { INDEX_PUSH_CONSTANTS, header.offsetPushConstants, 0U },
50 Data { INDEX_SPECIALIZATION_CONSTANTS, header.offsetSpecializationConstants, 0U },
51 Data { INDEX_DESCRIPTOR_SETS, header.offsetDescriptorSets, 0U },
52 Data { INDEX_INPUTS, header.offsetInputs, 0U },
53 Data { INDEX_LOCAL_SIZE, header.offsetLocalSize, 0U },
54 };
55 // first sort by offsets
56 BASE_NS::InsertionSort(
57 data.begin(), data.end(), [](const auto& lhs, const auto& rhs) { return lhs.offset < rhs.offset; });
58
59 // size of each data segment is what fits between it's offset and the next. if offset + size doesn't fit size is
60 // left zero.
61 for (auto i = 0LLU, end = data.size() - 1LLU; i < end; ++i) {
62 const auto size = data[i + 1LLU].offset - data[i].offset;
63 if ((size > 0) && ((data[i].offset + size_t(size)) < totalSize)) {
64 data[i].size = uint16_t(size);
65 }
66 }
67 if (data.back().offset < totalSize) {
68 data.back().size = uint16_t(totalSize - data.back().offset);
69 }
70 // sort be index so that the data is easily usable.
71 BASE_NS::InsertionSort(
72 data.begin(), data.end(), [](const auto& lhs, const auto& rhs) { return lhs.index < rhs.index; });
73 return data;
74 }
75
Read8U(const uint8_t * & ptr)76 inline uint8_t Read8U(const uint8_t*& ptr)
77 {
78 uint8_t ret = *(ptr);
79 ptr += sizeof(ret);
80 return ret;
81 }
82
Read16U(const uint8_t * & ptr)83 inline uint16_t Read16U(const uint8_t*& ptr)
84 {
85 auto ret = static_cast<uint16_t>(*(ptr) | (*(ptr + 1) << 8U));
86 ptr += sizeof(ret);
87 return ret;
88 }
Read32U(const uint8_t * & ptr)89 inline uint32_t Read32U(const uint8_t*& ptr)
90 {
91 auto ret = static_cast<uint32_t>(*ptr | *(ptr + 1) << 8U | *(ptr + 2) << 16U | *(ptr + 3) << 24U);
92 ptr += sizeof(ret);
93 return ret;
94 }
95
GetPackedDescriptorTypeFlags(const ImageFlags imageFlags,const ImageDimension imageDimension)96 constexpr AdditionalDescriptorTypeFlags GetPackedDescriptorTypeFlags(
97 const ImageFlags imageFlags, const ImageDimension imageDimension)
98 {
99 AdditionalDescriptorFlags flags = 0U;
100 if (imageDimension == ImageDimension::DIMENSION_1D) {
101 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_DIMENSION_1D_BIT;
102 }
103 if (imageDimension == ImageDimension::DIMENSION_2D) {
104 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_DIMENSION_2D_BIT;
105 }
106 if (imageDimension == ImageDimension::DIMENSION_3D) {
107 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_DIMENSION_3D_BIT;
108 }
109 if (imageDimension == ImageDimension::DIMENSION_CUBE) {
110 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_DIMENSION_CUBE_BIT;
111 }
112 if (imageDimension == ImageDimension::DIMENSION_RECT) {
113 flags |= 0U;
114 }
115 if (imageDimension == ImageDimension::DIMENSION_BUFFER) {
116 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_DIMENSION_BUFFER_BIT;
117 }
118 if (imageDimension == ImageDimension::DIMENSION_SUBPASS) {
119 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_DIMENSION_SUBPASS_BIT;
120 }
121
122 if (imageFlags & ImageFlags::IMAGE_DEPTH) {
123 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_DEPTH_BIT;
124 }
125 if (imageFlags & ImageFlags::IMAGE_ARRAY) {
126 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_ARRAY_BIT;
127 }
128 if (imageFlags & ImageFlags::IMAGE_MULTISAMPLE) {
129 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_MULTISAMPLE_BIT;
130 }
131 if (imageFlags & ImageFlags::IMAGE_SAMPLED) {
132 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_SAMPLED_BIT;
133 }
134 if (imageFlags & ImageFlags::IMAGE_LOAD_STORE) {
135 flags |= AdditionalDescriptorTypeImageFlagBits::CORE_DESCRIPTOR_TYPE_IMAGE_LOAD_STORE_BIT;
136 }
137
138 return flags;
139 }
140
ReadDescriptorSetsV0(PipelineLayout & pipelineLayout,const uint8_t * & ptr,const uint8_t * const end,ShaderStageFlags flags)141 void ReadDescriptorSetsV0(
142 PipelineLayout& pipelineLayout, const uint8_t*& ptr, const uint8_t* const end, ShaderStageFlags flags)
143 {
144 for (auto i = 0u; i < pipelineLayout.descriptorSetCount; ++i) {
145 // there must be at least 2 uint16 per each descriptor set (set and number of bindings)
146 if ((end - ptr) < static_cast<ptrdiff_t>(2U * sizeof(uint16_t))) {
147 pipelineLayout.descriptorSetCount = 0U;
148 return;
149 }
150 // write to correct set location
151 const auto set = static_cast<uint32_t>(Read16U(ptr));
152 if (set >= PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT) {
153 pipelineLayout.descriptorSetCount = 0U;
154 return;
155 }
156 const auto bindings = static_cast<uint32_t>(Read16U(ptr));
157 // each binding needs three 16 bit (binding idx, desc type, desc count).
158 static constexpr auto bindingSize = (3U * sizeof(uint16_t));
159 if ((end - ptr) < static_cast<ptrdiff_t>(bindings * bindingSize)) {
160 pipelineLayout.descriptorSetCount = 0U;
161 return;
162 }
163
164 auto& layout = pipelineLayout.descriptorSetLayouts[set];
165 layout.set = set;
166 layout.bindings.reserve(bindings);
167 for (auto j = 0u; j < bindings; ++j) {
168 DescriptorSetLayoutBinding& binding = layout.bindings.emplace_back();
169 binding.binding = static_cast<uint32_t>(Read16U(ptr));
170 binding.descriptorType = static_cast<DescriptorType>(Read16U(ptr));
171 if ((binding.descriptorType > DescriptorType::CORE_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) &&
172 (binding.descriptorType == (DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE & 0xffff))) {
173 binding.descriptorType = DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE;
174 }
175 binding.descriptorCount = static_cast<uint32_t>(Read16U(ptr));
176 binding.shaderStageFlags = flags;
177 }
178 }
179 }
180
ReadDescriptorSetsV1(PipelineLayout & pipelineLayout,const uint8_t * & ptr,const uint8_t * const end,ShaderStageFlags flags)181 void ReadDescriptorSetsV1(
182 PipelineLayout& pipelineLayout, const uint8_t*& ptr, const uint8_t* const end, ShaderStageFlags flags)
183 {
184 for (auto i = 0u; i < pipelineLayout.descriptorSetCount; ++i) {
185 // there must be at least 2 uint16 per each descriptor set (set and number of bindings)
186 if ((end - ptr) < static_cast<ptrdiff_t>(2U * sizeof(uint16_t))) {
187 pipelineLayout.descriptorSetCount = 0U;
188 return;
189 }
190 // write to correct set location
191 const auto set = static_cast<uint32_t>(Read16U(ptr));
192 if (set >= PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT) {
193 pipelineLayout.descriptorSetCount = 0U;
194 return;
195 }
196 const auto bindings = static_cast<uint32_t>(Read16U(ptr));
197 // each binding needs three 16 bit (binding idx, desc type, desc count) and two 8 bit values (image dims and
198 // flags).
199 static constexpr auto bindingSize = (3U * sizeof(uint16_t) + 2U * sizeof(uint8_t));
200 if ((end - ptr) < static_cast<ptrdiff_t>(bindings * bindingSize)) {
201 pipelineLayout.descriptorSetCount = 0U;
202 return;
203 }
204
205 auto& layout = pipelineLayout.descriptorSetLayouts[set];
206 layout.set = set;
207 layout.bindings.reserve(bindings);
208 for (auto j = 0u; j < bindings; ++j) {
209 DescriptorSetLayoutBinding& binding = layout.bindings.emplace_back();
210 binding.binding = static_cast<uint32_t>(Read16U(ptr));
211 binding.descriptorType = static_cast<DescriptorType>(Read16U(ptr));
212 if ((binding.descriptorType > DescriptorType::CORE_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) &&
213 (binding.descriptorType == (DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE & 0xffff))) {
214 binding.descriptorType = DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE;
215 }
216 binding.descriptorCount = static_cast<uint32_t>(Read16U(ptr));
217 const auto imageDimension = Read8U(ptr);
218 if (imageDimension > uint8_t(ImageDimension::DIMENSION_SUBPASS)) {
219 pipelineLayout.descriptorSetCount = 0U;
220 return;
221 }
222 const auto imageFlags = Read8U(ptr);
223 if (imageFlags >
224 uint8_t(ImageFlags::IMAGE_LOAD_STORE | ImageFlags::IMAGE_SAMPLED | ImageFlags::IMAGE_MULTISAMPLE |
225 ImageFlags::IMAGE_ARRAY | ImageFlags::IMAGE_DEPTH)) {
226 pipelineLayout.descriptorSetCount = 0U;
227 return;
228 }
229 binding.shaderStageFlags = flags;
230 binding.additionalDescriptorTypeFlags =
231 GetPackedDescriptorTypeFlags(ImageFlags(imageFlags), ImageDimension(imageDimension));
232 }
233 }
234 }
235 } // namespace
236
ShaderReflectionData(BASE_NS::array_view<const uint8_t> reflectionData)237 ShaderReflectionData::ShaderReflectionData(BASE_NS::array_view<const uint8_t> reflectionData)
238 : reflectionData_(reflectionData)
239 {
240 if (IsValid()) {
241 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
242 const auto dataSizes = CalculateDataSizes(header, uint32_t(reflectionData_.size()));
243 std::transform(dataSizes.cbegin(), dataSizes.cend(), size_, [](const Data& data) { return data.size; });
244 }
245 }
246
IsValid() const247 bool ShaderReflectionData::IsValid() const
248 {
249 if (reflectionData_.size() < sizeof(ReflectionHeader)) {
250 return false;
251 }
252 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
253 // the begining should match and then check the version next
254 auto isSame = memcmp(header.tag, REFLECTION_TAG, sizeof(REFLECTION_TAG) - 1U) == 0;
255 return isSame && (header.tag[sizeof(REFLECTION_TAG) - 1U] == 0U || header.tag[sizeof(REFLECTION_TAG) - 1U] == 1U);
256 }
257
GetStageFlags() const258 ShaderStageFlags ShaderReflectionData::GetStageFlags() const
259 {
260 ShaderStageFlags flags;
261 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
262 flags = header.type;
263 return flags;
264 }
265
GetPipelineLayout() const266 PipelineLayout ShaderReflectionData::GetPipelineLayout() const
267 {
268 PipelineLayout pipelineLayout;
269 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
270 if (header.offsetPushConstants && (size_[INDEX_PUSH_CONSTANTS] >= sizeof(uint8_t)) &&
271 (header.offsetPushConstants + size_t(size_[INDEX_PUSH_CONSTANTS])) <= reflectionData_.size()) {
272 auto ptr = reflectionData_.data() + header.offsetPushConstants;
273 const auto constants = Read8U(ptr);
274 if (constants && (size_[INDEX_PUSH_CONSTANTS] >= (sizeof(uint8_t) + sizeof(uint16_t)))) {
275 pipelineLayout.pushConstant.shaderStageFlags = header.type;
276 pipelineLayout.pushConstant.byteSize = static_cast<uint32_t>(Read16U(ptr));
277 }
278 }
279 if (!header.offsetDescriptorSets || (size_[INDEX_DESCRIPTOR_SETS] < sizeof(uint16_t)) ||
280 (header.offsetDescriptorSets + size_t(size_[INDEX_DESCRIPTOR_SETS])) > reflectionData_.size()) {
281 return pipelineLayout;
282 }
283 auto ptr = reflectionData_.data() + header.offsetDescriptorSets;
284 auto endSets = ptr + size_[INDEX_DESCRIPTOR_SETS];
285 pipelineLayout.descriptorSetCount = static_cast<uint32_t>(Read16U(ptr));
286 if (pipelineLayout.descriptorSetCount) {
287 switch (header.tag[sizeof(REFLECTION_TAG) - 1U]) {
288 case 0U:
289 ReadDescriptorSetsV0(pipelineLayout, ptr, endSets, header.type);
290 break;
291
292 case 1U:
293 ReadDescriptorSetsV1(pipelineLayout, ptr, endSets, header.type);
294 break;
295
296 default:
297 pipelineLayout.descriptorSetCount = 0U;
298 break;
299 }
300 }
301 return pipelineLayout;
302 }
303
GetSpecializationConstants() const304 BASE_NS::vector<ShaderSpecialization::Constant> ShaderReflectionData::GetSpecializationConstants() const
305 {
306 BASE_NS::vector<ShaderSpecialization::Constant> constants;
307 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
308 if (!header.offsetSpecializationConstants || (size_[INDEX_SPECIALIZATION_CONSTANTS] < sizeof(uint32_t)) ||
309 (header.offsetSpecializationConstants + size_t(size_[INDEX_SPECIALIZATION_CONSTANTS])) >
310 reflectionData_.size()) {
311 return constants;
312 }
313 auto ptr = reflectionData_.data() + header.offsetSpecializationConstants;
314 const auto size = Read32U(ptr);
315 // make sure constant count + id and type for each constant fits in reflection data
316 if ((sizeof(uint32_t) + size * (2LLU * sizeof(uint32_t))) > size_t(size_[INDEX_SPECIALIZATION_CONSTANTS])) {
317 return constants;
318 }
319 constants.reserve(size);
320 for (auto i = 0U; i < size; ++i) {
321 ShaderSpecialization::Constant& constant = constants.emplace_back();
322 constant.shaderStage = header.type;
323 constant.id = Read32U(ptr);
324 constant.type = static_cast<ShaderSpecialization::Constant::Type>(Read32U(ptr));
325 constant.offset = 0;
326 }
327
328 return constants;
329 }
330
331 BASE_NS::vector<VertexInputDeclaration::VertexInputAttributeDescription>
GetInputDescriptions() const332 ShaderReflectionData::GetInputDescriptions() const
333 {
334 BASE_NS::vector<VertexInputDeclaration::VertexInputAttributeDescription> inputs;
335 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
336 if (!header.offsetInputs || (size_[INDEX_INPUTS] < sizeof(uint16_t)) ||
337 (header.offsetInputs + size_t(size_[INDEX_INPUTS])) > reflectionData_.size()) {
338 return inputs;
339 }
340 auto ptr = reflectionData_.data() + header.offsetInputs;
341 const auto size = Read16U(ptr);
342 // make sure input count + location and format for each input fits in reflection data
343 if ((sizeof(uint16_t) + size * (sizeof(uint16_t) * 2LLU)) > size_t(size_[INDEX_INPUTS])) {
344 return inputs;
345 }
346 inputs.reserve(size);
347 for (auto i = 0U; i < size; ++i) {
348 VertexInputDeclaration::VertexInputAttributeDescription& desc = inputs.emplace_back();
349 desc.location = static_cast<uint32_t>(Read16U(ptr));
350 desc.binding = desc.location;
351 desc.format = static_cast<BASE_NS::Format>(Read16U(ptr));
352 desc.offset = 0;
353 }
354
355 return inputs;
356 }
357
GetLocalSize() const358 BASE_NS::Math::UVec3 ShaderReflectionData::GetLocalSize() const
359 {
360 BASE_NS::Math::UVec3 sizes;
361 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
362 if (header.offsetLocalSize && (size_[INDEX_LOCAL_SIZE] >= sizeof(BASE_NS::Math::UVec3)) &&
363 (header.offsetLocalSize + size_t(size_[INDEX_LOCAL_SIZE])) <= reflectionData_.size()) {
364 auto ptr = reflectionData_.data() + header.offsetLocalSize;
365 sizes.x = Read32U(ptr);
366 sizes.y = Read32U(ptr);
367 sizes.z = Read32U(ptr);
368 }
369 return sizes;
370 }
371
GetPushConstants() const372 const uint8_t* ShaderReflectionData::GetPushConstants() const
373 {
374 const uint8_t* ptr = nullptr;
375 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
376 // constants on/off is uint8 and byte size of constants is uint16
377 if (header.offsetPushConstants && (size_[INDEX_PUSH_CONSTANTS] >= sizeof(uint8_t) + sizeof(uint16_t)) &&
378 (header.offsetPushConstants + size_t(size_[INDEX_PUSH_CONSTANTS])) <= reflectionData_.size()) {
379 const auto constants = *(reflectionData_.data() + header.offsetPushConstants);
380 if (constants) {
381 ptr = reflectionData_.data() + header.offsetPushConstants + sizeof(uint8_t) + sizeof(uint16_t);
382 }
383 }
384 return ptr;
385 }
386 RENDER_END_NAMESPACE()
387