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