/* Copyright (c) 2019-2025, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 the "License"; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "shader_module.h" #include "core/util/logging.hpp" #include "device.h" #include "filesystem/legacy.h" #include "spirv_reflection.h" namespace vkb { ShaderModule::ShaderModule(vkb::core::DeviceC &device, VkShaderStageFlagBits stage, const ShaderSource &shader_source, const std::string &entry_point, const ShaderVariant &shader_variant) : device{device}, stage{stage}, entry_point{entry_point} { debug_name = fmt::format("{} [variant {:X}] [entrypoint {}]", shader_source.get_filename(), shader_variant.get_id(), entry_point); // Shaders in binary SPIR-V format can be loaded directly spirv = vkb::fs::read_shader_binary_u32(shader_source.get_filename()); // Reflection is used to dynamically create descriptor bindings SPIRVReflection spirv_reflection; // Reflect all shader resources if (!spirv_reflection.reflect_shader_resources(stage, spirv, resources, shader_variant)) { throw VulkanException{VK_ERROR_INITIALIZATION_FAILED}; } // Generate a unique id, determined by source and variant std::hash hasher{}; id = hasher(std::string{reinterpret_cast(spirv.data()), reinterpret_cast(spirv.data() + spirv.size())}); } ShaderModule::ShaderModule(ShaderModule &&other) : device{other.device}, id{other.id}, stage{other.stage}, entry_point{other.entry_point}, debug_name{other.debug_name}, spirv{other.spirv}, resources{other.resources} { other.stage = {}; } size_t ShaderModule::get_id() const { return id; } VkShaderStageFlagBits ShaderModule::get_stage() const { return stage; } const std::string &ShaderModule::get_entry_point() const { return entry_point; } const std::vector &ShaderModule::get_resources() const { return resources; } const std::vector &ShaderModule::get_binary() const { return spirv; } void ShaderModule::set_resource_mode(const std::string &resource_name, const ShaderResourceMode &resource_mode) { auto it = std::ranges::find_if(resources, [&resource_name](const ShaderResource &resource) { return resource.name == resource_name; }); if (it != resources.end()) { if (resource_mode == ShaderResourceMode::Dynamic) { if (it->type == ShaderResourceType::BufferUniform || it->type == ShaderResourceType::BufferStorage) { it->mode = resource_mode; } else { LOGW("Resource `{}` does not support dynamic.", resource_name); } } else { it->mode = resource_mode; } } else { LOGW("Resource `{}` not found for shader.", resource_name); } } size_t ShaderVariant::get_id() const { return id; } void ShaderVariant::add_runtime_array_size(const std::string &runtime_array_name, size_t size) { if (runtime_array_sizes.find(runtime_array_name) == runtime_array_sizes.end()) { runtime_array_sizes.insert({runtime_array_name, size}); } else { runtime_array_sizes[runtime_array_name] = size; } } void ShaderVariant::set_runtime_array_sizes(const std::unordered_map &sizes) { this->runtime_array_sizes = sizes; } const std::unordered_map &ShaderVariant::get_runtime_array_sizes() const { return runtime_array_sizes; } void ShaderVariant::clear() { runtime_array_sizes.clear(); id = 0; } ShaderSource::ShaderSource(const std::string &filename) : filename{filename}, source{fs::read_text_file(filename)} { std::hash hasher{}; id = hasher(std::string{this->source.cbegin(), this->source.cend()}); } size_t ShaderSource::get_id() const { return id; } const std::string &ShaderSource::get_filename() const { return filename; } void ShaderSource::set_source(const std::string &source_) { source = source_; std::hash hasher{}; id = hasher(std::string{this->source.cbegin(), this->source.cend()}); } const std::string &ShaderSource::get_source() const { return source; } } // namespace vkb