1 /*
2 * Copyright (c) 2022 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,const uint32_t descriptorSetCount,ShaderStageFlags flags)141 bool ReadDescriptorSetsV0(PipelineLayout& pipelineLayout, const uint8_t*& ptr, const uint8_t* const end,
142 const uint32_t descriptorSetCount, ShaderStageFlags flags)
143 {
144 for (auto i = 0u; i < 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 return false;
148 }
149 // write to correct set location
150 const auto set = static_cast<uint32_t>(Read16U(ptr));
151 if (set >= PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT) {
152 return false;
153 }
154 const auto bindings = static_cast<uint32_t>(Read16U(ptr));
155 // each binding needs three 16 bit (binding idx, desc type, desc count).
156 static constexpr auto bindingSize = (3U * sizeof(uint16_t));
157 if ((end - ptr) < static_cast<ptrdiff_t>(bindings * bindingSize)) {
158 return false;
159 }
160
161 auto& layout = pipelineLayout.descriptorSetLayouts[set];
162 layout.set = set;
163 layout.bindings.reserve(bindings);
164 for (auto j = 0u; j < bindings; ++j) {
165 DescriptorSetLayoutBinding& binding = layout.bindings.emplace_back();
166 binding.binding = static_cast<uint32_t>(Read16U(ptr));
167 binding.descriptorType = static_cast<DescriptorType>(Read16U(ptr));
168 if ((binding.descriptorType > DescriptorType::CORE_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) &&
169 (binding.descriptorType == (DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE & 0xffff))) {
170 binding.descriptorType = DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE;
171 }
172 binding.descriptorCount = static_cast<uint32_t>(Read16U(ptr));
173 binding.shaderStageFlags = flags;
174 }
175 }
176 return true;
177 }
178
ReadDescriptorSetsV1(PipelineLayout & pipelineLayout,const uint8_t * & ptr,const uint8_t * const end,const uint32_t descriptorSetCount,ShaderStageFlags flags)179 bool ReadDescriptorSetsV1(PipelineLayout& pipelineLayout, const uint8_t*& ptr, const uint8_t* const end,
180 const uint32_t descriptorSetCount, ShaderStageFlags flags)
181 {
182 for (auto i = 0u; i < descriptorSetCount; ++i) {
183 // there must be at least 2 uint16 per each descriptor set (set and number of bindings)
184 if ((end - ptr) < static_cast<ptrdiff_t>(2U * sizeof(uint16_t))) {
185 return false;
186 }
187 // write to correct set location
188 const auto set = static_cast<uint32_t>(Read16U(ptr));
189 if (set >= PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT) {
190 return false;
191 }
192 const auto bindings = static_cast<uint32_t>(Read16U(ptr));
193 // each binding needs three 16 bit (binding idx, desc type, desc count) and two 8 bit values (image dims and
194 // flags).
195 static constexpr auto bindingSize = (3U * sizeof(uint16_t) + 2U * sizeof(uint8_t));
196 if ((end - ptr) < static_cast<ptrdiff_t>(bindings * bindingSize)) {
197 return false;
198 }
199
200 auto& layout = pipelineLayout.descriptorSetLayouts[set];
201 layout.set = set;
202 layout.bindings.reserve(bindings);
203 for (auto j = 0u; j < bindings; ++j) {
204 DescriptorSetLayoutBinding& binding = layout.bindings.emplace_back();
205 binding.binding = static_cast<uint32_t>(Read16U(ptr));
206 binding.descriptorType = static_cast<DescriptorType>(Read16U(ptr));
207 if ((binding.descriptorType > DescriptorType::CORE_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) &&
208 (binding.descriptorType == (DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE & 0xffff))) {
209 binding.descriptorType = DescriptorType::CORE_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE;
210 }
211 binding.descriptorCount = static_cast<uint32_t>(Read16U(ptr));
212 const auto imageDimension = Read8U(ptr);
213 if (imageDimension > uint8_t(ImageDimension::DIMENSION_SUBPASS)) {
214 return false;
215 }
216 const auto imageFlags = Read8U(ptr);
217 constexpr auto validFlags =
218 uint8_t(ImageFlags::IMAGE_LOAD_STORE | ImageFlags::IMAGE_SAMPLED | ImageFlags::IMAGE_MULTISAMPLE |
219 ImageFlags::IMAGE_ARRAY | ImageFlags::IMAGE_DEPTH);
220 if (imageFlags & ~validFlags) {
221 return false;
222 }
223 binding.shaderStageFlags = flags;
224 binding.additionalDescriptorTypeFlags =
225 GetPackedDescriptorTypeFlags(ImageFlags(imageFlags), ImageDimension(imageDimension));
226 }
227 }
228 return true;
229 }
230 } // namespace
231
ShaderReflectionData(BASE_NS::array_view<const uint8_t> reflectionData)232 ShaderReflectionData::ShaderReflectionData(BASE_NS::array_view<const uint8_t> reflectionData)
233 : reflectionData_(reflectionData)
234 {
235 if (IsValid()) {
236 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
237 const auto dataSizes = CalculateDataSizes(header, uint32_t(reflectionData_.size()));
238 std::transform(dataSizes.cbegin(), dataSizes.cend(), size_, [](const Data& data) { return data.size; });
239 }
240 }
241
IsValid() const242 bool ShaderReflectionData::IsValid() const
243 {
244 if (reflectionData_.size() < sizeof(ReflectionHeader)) {
245 return false;
246 }
247 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
248 // the begining should match and then check the version next
249 auto isSame = memcmp(header.tag, REFLECTION_TAG, sizeof(REFLECTION_TAG) - 1U) == 0;
250 return isSame && (header.tag[sizeof(REFLECTION_TAG) - 1U] == 0U || header.tag[sizeof(REFLECTION_TAG) - 1U] == 1U);
251 }
252
GetStageFlags() const253 ShaderStageFlags ShaderReflectionData::GetStageFlags() const
254 {
255 ShaderStageFlags flags = 0U;
256 if (reflectionData_.size() < sizeof(ReflectionHeader)) {
257 return flags;
258 }
259 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
260 flags = header.type;
261 return flags;
262 }
263
GetPipelineLayout() const264 PipelineLayout ShaderReflectionData::GetPipelineLayout() const
265 {
266 PipelineLayout pipelineLayout;
267 if (reflectionData_.size() < sizeof(ReflectionHeader)) {
268 return pipelineLayout;
269 }
270
271 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
272 if (header.offsetPushConstants && (size_[INDEX_PUSH_CONSTANTS] >= sizeof(uint8_t)) &&
273 (header.offsetPushConstants + size_t(size_[INDEX_PUSH_CONSTANTS])) <= reflectionData_.size()) {
274 auto ptr = reflectionData_.data() + header.offsetPushConstants;
275 const auto constants = Read8U(ptr);
276 if (constants && (size_[INDEX_PUSH_CONSTANTS] >= (sizeof(uint8_t) + sizeof(uint16_t)))) {
277 pipelineLayout.pushConstant.shaderStageFlags = header.type;
278 pipelineLayout.pushConstant.byteSize = static_cast<uint32_t>(Read16U(ptr));
279 }
280 }
281 if (!header.offsetDescriptorSets || (size_[INDEX_DESCRIPTOR_SETS] < sizeof(uint16_t)) ||
282 (header.offsetDescriptorSets + size_t(size_[INDEX_DESCRIPTOR_SETS])) > reflectionData_.size()) {
283 return pipelineLayout;
284 }
285 auto ptr = reflectionData_.data() + header.offsetDescriptorSets;
286 auto endSets = ptr + size_[INDEX_DESCRIPTOR_SETS];
287 const auto descriptorSetCount = static_cast<uint32_t>(Read16U(ptr));
288 if (descriptorSetCount) {
289 bool descriptorSetsOk = false;
290 switch (header.tag[sizeof(REFLECTION_TAG) - 1U]) {
291 case 0U:
292 descriptorSetsOk = ReadDescriptorSetsV0(pipelineLayout, ptr, endSets, descriptorSetCount, header.type);
293 break;
294
295 case 1U:
296 descriptorSetsOk = ReadDescriptorSetsV1(pipelineLayout, ptr, endSets, descriptorSetCount, header.type);
297 break;
298
299 default:
300 break;
301 }
302 if (!descriptorSetsOk) {
303 pipelineLayout = {};
304 }
305 }
306 return pipelineLayout;
307 }
308
GetSpecializationConstants() const309 BASE_NS::vector<ShaderSpecialization::Constant> ShaderReflectionData::GetSpecializationConstants() const
310 {
311 BASE_NS::vector<ShaderSpecialization::Constant> constants;
312 if (reflectionData_.size() < sizeof(ReflectionHeader)) {
313 return constants;
314 }
315 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
316 if (!header.offsetSpecializationConstants || (size_[INDEX_SPECIALIZATION_CONSTANTS] < sizeof(uint32_t)) ||
317 (header.offsetSpecializationConstants + size_t(size_[INDEX_SPECIALIZATION_CONSTANTS])) >
318 reflectionData_.size()) {
319 return constants;
320 }
321 auto ptr = reflectionData_.data() + header.offsetSpecializationConstants;
322 const auto size = Read32U(ptr);
323 // make sure constant count + id and type for each constant fits in reflection data
324 if ((sizeof(uint32_t) + size * (2LLU * sizeof(uint32_t))) > size_t(size_[INDEX_SPECIALIZATION_CONSTANTS])) {
325 return constants;
326 }
327 constants.reserve(size);
328 for (auto i = 0U; i < size; ++i) {
329 ShaderSpecialization::Constant& constant = constants.emplace_back();
330 constant.shaderStage = header.type;
331 constant.id = Read32U(ptr);
332 constant.type = static_cast<ShaderSpecialization::Constant::Type>(Read32U(ptr));
333 constant.offset = 0;
334 }
335
336 return constants;
337 }
338
339 BASE_NS::vector<VertexInputDeclaration::VertexInputAttributeDescription>
GetInputDescriptions() const340 ShaderReflectionData::GetInputDescriptions() const
341 {
342 BASE_NS::vector<VertexInputDeclaration::VertexInputAttributeDescription> inputs;
343 if (reflectionData_.size() < sizeof(ReflectionHeader)) {
344 return inputs;
345 }
346 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
347 if (!header.offsetInputs || (size_[INDEX_INPUTS] < sizeof(uint16_t)) ||
348 (header.offsetInputs + size_t(size_[INDEX_INPUTS])) > reflectionData_.size()) {
349 return inputs;
350 }
351 auto ptr = reflectionData_.data() + header.offsetInputs;
352 const auto size = Read16U(ptr);
353 // make sure input count + location and format for each input fits in reflection data
354 if ((sizeof(uint16_t) + size * (sizeof(uint16_t) * 2LLU)) > size_t(size_[INDEX_INPUTS])) {
355 return inputs;
356 }
357 inputs.reserve(size);
358 for (auto i = 0U; i < size; ++i) {
359 VertexInputDeclaration::VertexInputAttributeDescription& desc = inputs.emplace_back();
360 desc.location = static_cast<uint32_t>(Read16U(ptr));
361 desc.binding = desc.location;
362 desc.format = static_cast<BASE_NS::Format>(Read16U(ptr));
363 desc.offset = 0;
364 }
365
366 return inputs;
367 }
368
GetLocalSize() const369 BASE_NS::Math::UVec3 ShaderReflectionData::GetLocalSize() const
370 {
371 BASE_NS::Math::UVec3 sizes;
372 if (reflectionData_.size() < sizeof(ReflectionHeader)) {
373 return sizes;
374 }
375 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
376 if (header.offsetLocalSize && (size_[INDEX_LOCAL_SIZE] >= sizeof(BASE_NS::Math::UVec3)) &&
377 (header.offsetLocalSize + size_t(size_[INDEX_LOCAL_SIZE])) <= reflectionData_.size()) {
378 auto ptr = reflectionData_.data() + header.offsetLocalSize;
379 sizes.x = Read32U(ptr);
380 sizes.y = Read32U(ptr);
381 sizes.z = Read32U(ptr);
382 }
383 return sizes;
384 }
385
GetPushConstants() const386 const uint8_t* ShaderReflectionData::GetPushConstants() const
387 {
388 const uint8_t* ptr = nullptr;
389 if (reflectionData_.size() < sizeof(ReflectionHeader)) {
390 return ptr;
391 }
392 const ReflectionHeader& header = *reinterpret_cast<const ReflectionHeader*>(reflectionData_.data());
393 // constants on/off is uint8 and byte size of constants is uint16
394 if (header.offsetPushConstants && (size_[INDEX_PUSH_CONSTANTS] >= sizeof(uint8_t) + sizeof(uint16_t)) &&
395 (header.offsetPushConstants + size_t(size_[INDEX_PUSH_CONSTANTS])) <= reflectionData_.size()) {
396 const auto constants = *(reflectionData_.data() + header.offsetPushConstants);
397 if (constants) {
398 ptr = reflectionData_.data() + header.offsetPushConstants + sizeof(uint8_t) + sizeof(uint16_t);
399 }
400 }
401 return ptr;
402 }
403 RENDER_END_NAMESPACE()
404