| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "impeller/compiler/shader_bundle_data.h" | |
| #include <optional> | |
| #include "impeller/shader_bundle/shader_bundle_flatbuffers.h" | |
| #include "impeller/base/validation.h" | |
| namespace impeller { | |
| namespace compiler { | |
| ShaderBundleData::ShaderBundleData(std::string entrypoint, | |
| spv::ExecutionModel stage, | |
| TargetPlatform target_platform) | |
| : entrypoint_(std::move(entrypoint)), | |
| stage_(stage), | |
| target_platform_(target_platform) {} | |
| ShaderBundleData::~ShaderBundleData() = default; | |
| void ShaderBundleData::AddUniformStruct(ShaderUniformStruct uniform_struct) { | |
| uniform_structs_.emplace_back(std::move(uniform_struct)); | |
| } | |
| void ShaderBundleData::AddUniformTexture(ShaderUniformTexture uniform_texture) { | |
| uniform_textures_.emplace_back(std::move(uniform_texture)); | |
| } | |
| void ShaderBundleData::AddInputDescription(InputDescription input) { | |
| inputs_.emplace_back(std::move(input)); | |
| } | |
| void ShaderBundleData::SetShaderData(std::shared_ptr<fml::Mapping> shader) { | |
| shader_ = std::move(shader); | |
| } | |
| static std::optional<fb::shaderbundle::ShaderStage> ToStage( | |
| spv::ExecutionModel stage) { | |
| switch (stage) { | |
| case spv::ExecutionModel::ExecutionModelVertex: | |
| return fb::shaderbundle::ShaderStage::kVertex; | |
| case spv::ExecutionModel::ExecutionModelFragment: | |
| return fb::shaderbundle::ShaderStage::kFragment; | |
| case spv::ExecutionModel::ExecutionModelGLCompute: | |
| return fb::shaderbundle::ShaderStage::kCompute; | |
| default: | |
| return std::nullopt; | |
| } | |
| FML_UNREACHABLE(); | |
| } | |
| static std::optional<fb::shaderbundle::UniformDataType> ToUniformType( | |
| spirv_cross::SPIRType::BaseType type) { | |
| switch (type) { | |
| case spirv_cross::SPIRType::Boolean: | |
| return fb::shaderbundle::UniformDataType::kBoolean; | |
| case spirv_cross::SPIRType::SByte: | |
| return fb::shaderbundle::UniformDataType::kSignedByte; | |
| case spirv_cross::SPIRType::UByte: | |
| return fb::shaderbundle::UniformDataType::kUnsignedByte; | |
| case spirv_cross::SPIRType::Short: | |
| return fb::shaderbundle::UniformDataType::kSignedShort; | |
| case spirv_cross::SPIRType::UShort: | |
| return fb::shaderbundle::UniformDataType::kUnsignedShort; | |
| case spirv_cross::SPIRType::Int: | |
| return fb::shaderbundle::UniformDataType::kSignedInt; | |
| case spirv_cross::SPIRType::UInt: | |
| return fb::shaderbundle::UniformDataType::kUnsignedInt; | |
| case spirv_cross::SPIRType::Int64: | |
| return fb::shaderbundle::UniformDataType::kSignedInt64; | |
| case spirv_cross::SPIRType::UInt64: | |
| return fb::shaderbundle::UniformDataType::kUnsignedInt64; | |
| case spirv_cross::SPIRType::Half: | |
| return fb::shaderbundle::UniformDataType::kHalfFloat; | |
| case spirv_cross::SPIRType::Float: | |
| return fb::shaderbundle::UniformDataType::kFloat; | |
| case spirv_cross::SPIRType::Double: | |
| return fb::shaderbundle::UniformDataType::kDouble; | |
| case spirv_cross::SPIRType::SampledImage: | |
| return fb::shaderbundle::UniformDataType::kSampledImage; | |
| case spirv_cross::SPIRType::AccelerationStructure: | |
| case spirv_cross::SPIRType::AtomicCounter: | |
| case spirv_cross::SPIRType::Char: | |
| case spirv_cross::SPIRType::ControlPointArray: | |
| case spirv_cross::SPIRType::Image: | |
| case spirv_cross::SPIRType::Interpolant: | |
| case spirv_cross::SPIRType::RayQuery: | |
| case spirv_cross::SPIRType::Sampler: | |
| case spirv_cross::SPIRType::Struct: | |
| case spirv_cross::SPIRType::Unknown: | |
| case spirv_cross::SPIRType::Void: | |
| return std::nullopt; | |
| } | |
| FML_UNREACHABLE(); | |
| } | |
| static std::optional<fb::shaderbundle::InputDataType> ToInputType( | |
| spirv_cross::SPIRType::BaseType type) { | |
| switch (type) { | |
| case spirv_cross::SPIRType::Boolean: | |
| return fb::shaderbundle::InputDataType::kBoolean; | |
| case spirv_cross::SPIRType::SByte: | |
| return fb::shaderbundle::InputDataType::kSignedByte; | |
| case spirv_cross::SPIRType::UByte: | |
| return fb::shaderbundle::InputDataType::kUnsignedByte; | |
| case spirv_cross::SPIRType::Short: | |
| return fb::shaderbundle::InputDataType::kSignedShort; | |
| case spirv_cross::SPIRType::UShort: | |
| return fb::shaderbundle::InputDataType::kUnsignedShort; | |
| case spirv_cross::SPIRType::Int: | |
| return fb::shaderbundle::InputDataType::kSignedInt; | |
| case spirv_cross::SPIRType::UInt: | |
| return fb::shaderbundle::InputDataType::kUnsignedInt; | |
| case spirv_cross::SPIRType::Int64: | |
| return fb::shaderbundle::InputDataType::kSignedInt64; | |
| case spirv_cross::SPIRType::UInt64: | |
| return fb::shaderbundle::InputDataType::kUnsignedInt64; | |
| case spirv_cross::SPIRType::Float: | |
| return fb::shaderbundle::InputDataType::kFloat; | |
| case spirv_cross::SPIRType::Double: | |
| return fb::shaderbundle::InputDataType::kDouble; | |
| case spirv_cross::SPIRType::Unknown: | |
| case spirv_cross::SPIRType::Void: | |
| case spirv_cross::SPIRType::Half: | |
| case spirv_cross::SPIRType::AtomicCounter: | |
| case spirv_cross::SPIRType::Struct: | |
| case spirv_cross::SPIRType::Image: | |
| case spirv_cross::SPIRType::SampledImage: | |
| case spirv_cross::SPIRType::Sampler: | |
| case spirv_cross::SPIRType::AccelerationStructure: | |
| case spirv_cross::SPIRType::RayQuery: | |
| case spirv_cross::SPIRType::ControlPointArray: | |
| case spirv_cross::SPIRType::Interpolant: | |
| case spirv_cross::SPIRType::Char: | |
| return std::nullopt; | |
| } | |
| FML_UNREACHABLE(); | |
| } | |
| std::unique_ptr<fb::shaderbundle::BackendShaderT> | |
| ShaderBundleData::CreateFlatbuffer() const { | |
| auto shader_bundle = std::make_unique<fb::shaderbundle::BackendShaderT>(); | |
| // The high level object API is used here for writing to the buffer. This is | |
| // just a convenience. | |
| shader_bundle->entrypoint = entrypoint_; | |
| const auto stage = ToStage(stage_); | |
| if (!stage.has_value()) { | |
| VALIDATION_LOG << "Invalid shader bundle."; | |
| return nullptr; | |
| } | |
| shader_bundle->stage = stage.value(); | |
| // This field is ignored, so just set it to anything. | |
| if (!shader_) { | |
| VALIDATION_LOG << "No shader specified for shader bundle."; | |
| return nullptr; | |
| } | |
| if (shader_->GetSize() > 0u) { | |
| shader_bundle->shader = {shader_->GetMapping(), | |
| shader_->GetMapping() + shader_->GetSize()}; | |
| } | |
| for (const auto& uniform : uniform_structs_) { | |
| auto desc = std::make_unique<fb::shaderbundle::ShaderUniformStructT>(); | |
| desc->name = uniform.name; | |
| if (desc->name.empty()) { | |
| VALIDATION_LOG << "Uniform name cannot be empty."; | |
| return nullptr; | |
| } | |
| desc->ext_res_0 = uniform.ext_res_0; | |
| desc->set = uniform.set; | |
| desc->binding = uniform.binding; | |
| desc->size_in_bytes = uniform.size_in_bytes; | |
| for (const auto& field : uniform.fields) { | |
| auto field_desc = | |
| std::make_unique<fb::shaderbundle::ShaderUniformStructFieldT>(); | |
| field_desc->name = field.name; | |
| auto type = ToUniformType(field.type); | |
| if (!type.has_value()) { | |
| VALIDATION_LOG << " Invalid shader type " << field.type << "."; | |
| return nullptr; | |
| } | |
| field_desc->type = type.value(); | |
| field_desc->offset_in_bytes = field.offset_in_bytes; | |
| field_desc->element_size_in_bytes = field.element_size_in_bytes; | |
| field_desc->total_size_in_bytes = field.total_size_in_bytes; | |
| field_desc->array_elements = field.array_elements.value_or(0); | |
| desc->fields.push_back(std::move(field_desc)); | |
| } | |
| shader_bundle->uniform_structs.emplace_back(std::move(desc)); | |
| } | |
| for (const auto& texture : uniform_textures_) { | |
| auto desc = std::make_unique<fb::shaderbundle::ShaderUniformTextureT>(); | |
| desc->name = texture.name; | |
| if (desc->name.empty()) { | |
| VALIDATION_LOG << "Uniform name cannot be empty."; | |
| return nullptr; | |
| } | |
| desc->ext_res_0 = texture.ext_res_0; | |
| desc->set = texture.set; | |
| desc->binding = texture.binding; | |
| shader_bundle->uniform_textures.emplace_back(std::move(desc)); | |
| } | |
| for (const auto& input : inputs_) { | |
| auto desc = std::make_unique<fb::shaderbundle::ShaderInputT>(); | |
| desc->name = input.name; | |
| if (desc->name.empty()) { | |
| VALIDATION_LOG << "Stage input name cannot be empty."; | |
| return nullptr; | |
| } | |
| desc->location = input.location; | |
| desc->set = input.set; | |
| desc->binding = input.binding; | |
| auto input_type = ToInputType(input.type); | |
| if (!input_type.has_value()) { | |
| VALIDATION_LOG << "Invalid uniform type for runtime stage."; | |
| return nullptr; | |
| } | |
| desc->type = input_type.value(); | |
| desc->bit_width = input.bit_width; | |
| desc->vec_size = input.vec_size; | |
| desc->columns = input.columns; | |
| desc->offset = input.offset; | |
| shader_bundle->inputs.emplace_back(std::move(desc)); | |
| } | |
| return shader_bundle; | |
| } | |
| } // namespace compiler | |
| } // namespace impeller |