• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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