/* Copyright (c) 2019-2025, Arm Limited and Contributors * Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved. * * 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. */ #pragma once #include "buffer_pool.h" #include "rendering/hpp_pipeline_state.h" #include "rendering/hpp_render_target.h" #include "rendering/pipeline_state.h" #include "rendering/render_frame.h" #include "scene_graph/components/light.h" #include "scene_graph/node.h" namespace vkb { class RenderContext; class RenderTarget; class ShaderSource; namespace core { template class CommandBuffer; } namespace rendering { class HPPRenderContext; struct alignas(16) Light { glm::vec4 position; // position.w represents type of light glm::vec4 color; // color.w represents light intensity glm::vec4 direction; // direction.w represents range glm::vec2 info; // (only used for spot lights) info.x represents light inner cone angle, info.y represents light outer cone angle }; template struct LightingState { std::vector directional_lights; std::vector point_lights; std::vector spot_lights; BufferAllocation light_buffer; }; using LightingStateC = LightingState; using LightingStateCpp = LightingState; /** * @brief Calculates the vulkan style projection matrix * @param proj The projection matrix * @return The vulkan style projection matrix */ glm::mat4 vulkan_style_projection(const glm::mat4 &proj); inline const std::vector light_type_definitions = { "DIRECTIONAL_LIGHT " + std::to_string(static_cast(sg::LightType::Directional)), "POINT_LIGHT " + std::to_string(static_cast(sg::LightType::Point)), "SPOT_LIGHT " + std::to_string(static_cast(sg::LightType::Spot))}; /** * @brief This class defines an interface for subpasses * where they need to implement the draw function. * It is used to construct a RenderPipeline */ template class Subpass { using ResolveModeFlagBitsType = typename std::conditional::type; using SampleCountflagBitsType = typename std::conditional::type; using DepthStencilStateType = typename std::conditional::type; using RenderContextType = typename std::conditional::type; using RenderTargetType = typename std::conditional::type; public: Subpass(RenderContextType &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader); Subpass(const Subpass &) = delete; Subpass(Subpass &&) = default; virtual ~Subpass() = default; Subpass &operator=(const Subpass &) = delete; Subpass &operator=(Subpass &&) = delete; /** * @brief Draw virtual function * @param command_buffer Command buffer to use to record draw commands */ virtual void draw(vkb::core::CommandBuffer &command_buffer) = 0; /** * @brief Prepares the shaders and shader variants for a subpass */ virtual void prepare() = 0; /** * @brief Prepares the lighting state to have its lights * * @tparam A light structure that has 'directional_lights', 'point_lights' and 'spot_light' array fields defined. * @param scene_lights All of the light components from the scene graph * @param max_lights_per_type The maximum amount of lights allowed for any given type of light. */ template void allocate_lights(const std::vector &scene_lights, size_t max_lights_per_type); const std::vector &get_color_resolve_attachments() const; const std::string &get_debug_name() const; const uint32_t &get_depth_stencil_resolve_attachment() const; ResolveModeFlagBitsType get_depth_stencil_resolve_mode() const; DepthStencilStateType &get_depth_stencil_state(); const bool &get_disable_depth_stencil_attachment() const; const ShaderSource &get_fragment_shader() const; const std::vector &get_input_attachments() const; LightingState &get_lighting_state(); const std::vector &get_output_attachments() const; RenderContextType &get_render_context(); std::unordered_map const &get_resource_mode_map() const; SampleCountflagBitsType get_sample_count() const; const ShaderSource &get_vertex_shader() const; void set_color_resolve_attachments(std::vector const &color_resolve); void set_debug_name(const std::string &name); void set_disable_depth_stencil_attachment(bool disable_depth_stencil); void set_depth_stencil_resolve_attachment(uint32_t depth_stencil_resolve); void set_depth_stencil_resolve_mode(ResolveModeFlagBitsType mode); void set_input_attachments(std::vector const &input); void set_output_attachments(std::vector const &output); void set_sample_count(SampleCountflagBitsType sample_count); /** * @brief Updates the render target attachments with the ones stored in this subpass * This function is called by the RenderPipeline before beginning the render * pass and before proceeding with a new subpass. */ void update_render_target_attachments(RenderTargetType &render_target); private: /// Default to no color resolve attachments std::vector color_resolve_attachments = {}; std::string debug_name{}; /** * @brief When creating the renderpass, if not None, the resolve * of the multisampled depth attachment will be enabled, * with this mode, to depth_stencil_resolve_attachment */ vk::ResolveModeFlagBits depth_stencil_resolve_mode{vk::ResolveModeFlagBits::eNone}; vkb::rendering::HPPDepthStencilState depth_stencil_state{}; /** * @brief When creating the renderpass, pDepthStencilAttachment will * be set to nullptr, which disables depth testing */ bool disable_depth_stencil_attachment{false}; /// Default to no depth stencil resolve attachment uint32_t depth_stencil_resolve_attachment{VK_ATTACHMENT_UNUSED}; /// The structure containing all the requested render-ready lights for the scene LightingStateCpp lighting_state{}; ShaderSource fragment_shader; /// Default to no input attachments std::vector input_attachments = {}; /// Default to swapchain output attachment std::vector output_attachments = {0}; vkb::rendering::HPPRenderContext &render_context; // A map of shader resource names and the mode of constant data std::unordered_map resource_mode_map; vk::SampleCountFlagBits sample_count{vk::SampleCountFlagBits::e1}; ShaderSource vertex_shader; }; using SubpassC = Subpass; using SubpassCpp = Subpass; } // namespace rendering } // namespace vkb #include "rendering/hpp_render_context.h" namespace vkb { namespace rendering { inline glm::mat4 vulkan_style_projection(const glm::mat4 &proj) { // Flip Y in clipspace. X = -1, Y = -1 is topLeft in Vulkan. glm::mat4 mat = proj; mat[1][1] *= -1; return mat; } template inline Subpass::Subpass(RenderContextType &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source) : render_context{reinterpret_cast(render_context)}, vertex_shader{std::move(vertex_source)}, fragment_shader{std::move(fragment_source)} { } template inline const std::vector &Subpass::get_input_attachments() const { return input_attachments; } template inline LightingState &Subpass::get_lighting_state() { if constexpr (bindingType == vkb::BindingType::Cpp) { return lighting_state; } else { return reinterpret_cast &>(lighting_state); } } template inline const std::vector &Subpass::get_output_attachments() const { return output_attachments; } template inline typename vkb::rendering::Subpass::RenderContextType &Subpass::get_render_context() { if constexpr (bindingType == vkb::BindingType::Cpp) { return render_context; } else { return reinterpret_cast(render_context); } } template std::unordered_map const &Subpass::get_resource_mode_map() const { return resource_mode_map; } template inline typename Subpass::SampleCountflagBitsType Subpass::get_sample_count() const { if constexpr (bindingType == vkb::BindingType::Cpp) { return sample_count; } else { return static_cast(sample_count); } } template inline const ShaderSource &Subpass::get_vertex_shader() const { return vertex_shader; } template template void Subpass::allocate_lights(const std::vector &scene_lights, size_t max_lights_per_type) { lighting_state.directional_lights.clear(); lighting_state.point_lights.clear(); lighting_state.spot_lights.clear(); for (auto &scene_light : scene_lights) { const auto &properties = scene_light->get_properties(); auto &transform = scene_light->get_node()->get_transform(); Light light{{transform.get_translation(), static_cast(scene_light->get_light_type())}, {properties.color, properties.intensity}, {transform.get_rotation() * properties.direction, properties.range}, {properties.inner_cone_angle, properties.outer_cone_angle}}; switch (scene_light->get_light_type()) { case sg::LightType::Directional: { if (lighting_state.directional_lights.size() < max_lights_per_type) { lighting_state.directional_lights.push_back(light); } else { LOGE("Subpass::allocate_lights: exceeding max_lights_per_type of {} for directional lights", max_lights_per_type); } break; } case sg::LightType::Point: { if (lighting_state.point_lights.size() < max_lights_per_type) { lighting_state.point_lights.push_back(light); } else { LOGE("Subpass::allocate_lights: exceeding max_lights_per_type of {} for point lights", max_lights_per_type); } break; } case sg::LightType::Spot: { if (lighting_state.spot_lights.size() < max_lights_per_type) { lighting_state.spot_lights.push_back(light); } else { LOGE("Subpass::allocate_lights: exceeding max_lights_per_type of {} for spot lights", max_lights_per_type); } break; } default: LOGE("Subpass::allocate_lights: encountered unknown light type {}", to_string(scene_light->get_light_type())); break; } } T light_info; std::copy(lighting_state.directional_lights.begin(), lighting_state.directional_lights.end(), light_info.directional_lights); std::copy(lighting_state.point_lights.begin(), lighting_state.point_lights.end(), light_info.point_lights); std::copy(lighting_state.spot_lights.begin(), lighting_state.spot_lights.end(), light_info.spot_lights); auto &render_frame = render_context.get_active_frame(); lighting_state.light_buffer = render_frame.allocate_buffer(vk::BufferUsageFlagBits::eUniformBuffer, sizeof(T)); lighting_state.light_buffer.update(light_info); } template inline const std::vector &Subpass::get_color_resolve_attachments() const { return color_resolve_attachments; } template inline const std::string &Subpass::get_debug_name() const { return debug_name; } template inline const uint32_t &Subpass::get_depth_stencil_resolve_attachment() const { return depth_stencil_resolve_attachment; } template inline typename Subpass::ResolveModeFlagBitsType Subpass::get_depth_stencil_resolve_mode() const { if constexpr (bindingType == vkb::BindingType::Cpp) { return depth_stencil_resolve_mode; } else { return static_cast(depth_stencil_resolve_mode); } } template inline typename Subpass::DepthStencilStateType &Subpass::get_depth_stencil_state() { if constexpr (bindingType == vkb::BindingType::Cpp) { return depth_stencil_state; } else { return reinterpret_cast(depth_stencil_state); } } template inline const bool &Subpass::get_disable_depth_stencil_attachment() const { return disable_depth_stencil_attachment; } template inline const ShaderSource &Subpass::get_fragment_shader() const { return fragment_shader; } template inline void Subpass::set_color_resolve_attachments(std::vector const &color_resolve) { color_resolve_attachments = color_resolve; } template inline void Subpass::set_depth_stencil_resolve_attachment(uint32_t depth_stencil_resolve) { depth_stencil_resolve_attachment = depth_stencil_resolve; } template inline void Subpass::set_debug_name(const std::string &name) { debug_name = name; } template inline void Subpass::set_disable_depth_stencil_attachment(bool disable_depth_stencil) { disable_depth_stencil_attachment = disable_depth_stencil; } template inline void Subpass::set_depth_stencil_resolve_mode(ResolveModeFlagBitsType mode) { if constexpr (bindingType == vkb::BindingType::Cpp) { depth_stencil_resolve_mode = mode; } else { depth_stencil_resolve_mode = static_cast(mode); } } template inline void Subpass::set_input_attachments(std::vector const &input) { input_attachments = input; } template inline void Subpass::set_output_attachments(std::vector const &output) { output_attachments = output; } template inline void Subpass::set_sample_count(SampleCountflagBitsType sample_count_) { if constexpr (bindingType == vkb::BindingType::Cpp) { sample_count = sample_count_; } else { sample_count = static_cast(sample_count_); } } template inline void Subpass::update_render_target_attachments(RenderTargetType &render_target) { render_target.set_input_attachments(input_attachments); render_target.set_output_attachments(output_attachments); } } // namespace rendering } // namespace vkb