Files
face_sdk/vulkan/framework/rendering/subpass.h
T

489 lines
17 KiB
C++

/* 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 <vkb::BindingType bindingType>
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 <vkb::BindingType bindingType>
struct LightingState
{
std::vector<Light> directional_lights;
std::vector<Light> point_lights;
std::vector<Light> spot_lights;
BufferAllocation<bindingType> light_buffer;
};
using LightingStateC = LightingState<vkb::BindingType::C>;
using LightingStateCpp = LightingState<vkb::BindingType::Cpp>;
/**
* @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<std::string> light_type_definitions = {
"DIRECTIONAL_LIGHT " + std::to_string(static_cast<float>(sg::LightType::Directional)),
"POINT_LIGHT " + std::to_string(static_cast<float>(sg::LightType::Point)),
"SPOT_LIGHT " + std::to_string(static_cast<float>(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 <vkb::BindingType bindingType>
class Subpass
{
using ResolveModeFlagBitsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ResolveModeFlagBits, VkResolveModeFlagBits>::type;
using SampleCountflagBitsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SampleCountFlagBits, VkSampleCountFlagBits>::type;
using DepthStencilStateType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPDepthStencilState, vkb::DepthStencilState>::type;
using RenderContextType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPRenderContext, vkb::RenderContext>::type;
using RenderTargetType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPRenderTarget, vkb::RenderTarget>::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<bindingType> &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 <typename T>
void allocate_lights(const std::vector<sg::Light *> &scene_lights,
size_t max_lights_per_type);
const std::vector<uint32_t> &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<uint32_t> &get_input_attachments() const;
LightingState<bindingType> &get_lighting_state();
const std::vector<uint32_t> &get_output_attachments() const;
RenderContextType &get_render_context();
std::unordered_map<std::string, ShaderResourceMode> const &get_resource_mode_map() const;
SampleCountflagBitsType get_sample_count() const;
const ShaderSource &get_vertex_shader() const;
void set_color_resolve_attachments(std::vector<uint32_t> 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<uint32_t> const &input);
void set_output_attachments(std::vector<uint32_t> 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<uint32_t> 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<uint32_t> input_attachments = {};
/// Default to swapchain output attachment
std::vector<uint32_t> output_attachments = {0};
vkb::rendering::HPPRenderContext &render_context;
// A map of shader resource names and the mode of constant data
std::unordered_map<std::string, ShaderResourceMode> resource_mode_map;
vk::SampleCountFlagBits sample_count{vk::SampleCountFlagBits::e1};
ShaderSource vertex_shader;
};
using SubpassC = Subpass<vkb::BindingType::C>;
using SubpassCpp = Subpass<vkb::BindingType::Cpp>;
} // 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 <vkb::BindingType bindingType>
inline Subpass<bindingType>::Subpass(RenderContextType &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source) :
render_context{reinterpret_cast<vkb::rendering::HPPRenderContext &>(render_context)},
vertex_shader{std::move(vertex_source)},
fragment_shader{std::move(fragment_source)}
{
}
template <vkb::BindingType bindingType>
inline const std::vector<uint32_t> &Subpass<bindingType>::get_input_attachments() const
{
return input_attachments;
}
template <vkb::BindingType bindingType>
inline LightingState<bindingType> &Subpass<bindingType>::get_lighting_state()
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return lighting_state;
}
else
{
return reinterpret_cast<LightingState<bindingType> &>(lighting_state);
}
}
template <vkb::BindingType bindingType>
inline const std::vector<uint32_t> &Subpass<bindingType>::get_output_attachments() const
{
return output_attachments;
}
template <vkb::BindingType bindingType>
inline typename vkb::rendering::Subpass<bindingType>::RenderContextType &Subpass<bindingType>::get_render_context()
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return render_context;
}
else
{
return reinterpret_cast<vkb::RenderContext &>(render_context);
}
}
template <vkb::BindingType bindingType>
std::unordered_map<std::string, ShaderResourceMode> const &Subpass<bindingType>::get_resource_mode_map() const
{
return resource_mode_map;
}
template <vkb::BindingType bindingType>
inline typename Subpass<bindingType>::SampleCountflagBitsType Subpass<bindingType>::get_sample_count() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return sample_count;
}
else
{
return static_cast<VkSampleCountFlagBits>(sample_count);
}
}
template <vkb::BindingType bindingType>
inline const ShaderSource &Subpass<bindingType>::get_vertex_shader() const
{
return vertex_shader;
}
template <vkb::BindingType bindingType>
template <typename T>
void Subpass<bindingType>::allocate_lights(const std::vector<sg::Light *> &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<float>(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 <vkb::BindingType bindingType>
inline const std::vector<uint32_t> &Subpass<bindingType>::get_color_resolve_attachments() const
{
return color_resolve_attachments;
}
template <vkb::BindingType bindingType>
inline const std::string &Subpass<bindingType>::get_debug_name() const
{
return debug_name;
}
template <vkb::BindingType bindingType>
inline const uint32_t &Subpass<bindingType>::get_depth_stencil_resolve_attachment() const
{
return depth_stencil_resolve_attachment;
}
template <vkb::BindingType bindingType>
inline typename Subpass<bindingType>::ResolveModeFlagBitsType Subpass<bindingType>::get_depth_stencil_resolve_mode() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return depth_stencil_resolve_mode;
}
else
{
return static_cast<VkResolveModeFlagBits>(depth_stencil_resolve_mode);
}
}
template <vkb::BindingType bindingType>
inline typename Subpass<bindingType>::DepthStencilStateType &Subpass<bindingType>::get_depth_stencil_state()
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return depth_stencil_state;
}
else
{
return reinterpret_cast<vkb::DepthStencilState &>(depth_stencil_state);
}
}
template <vkb::BindingType bindingType>
inline const bool &Subpass<bindingType>::get_disable_depth_stencil_attachment() const
{
return disable_depth_stencil_attachment;
}
template <vkb::BindingType bindingType>
inline const ShaderSource &Subpass<bindingType>::get_fragment_shader() const
{
return fragment_shader;
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::set_color_resolve_attachments(std::vector<uint32_t> const &color_resolve)
{
color_resolve_attachments = color_resolve;
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::set_depth_stencil_resolve_attachment(uint32_t depth_stencil_resolve)
{
depth_stencil_resolve_attachment = depth_stencil_resolve;
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::set_debug_name(const std::string &name)
{
debug_name = name;
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::set_disable_depth_stencil_attachment(bool disable_depth_stencil)
{
disable_depth_stencil_attachment = disable_depth_stencil;
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::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<vk::ResolveModeFlagBits>(mode);
}
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::set_input_attachments(std::vector<uint32_t> const &input)
{
input_attachments = input;
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::set_output_attachments(std::vector<uint32_t> const &output)
{
output_attachments = output;
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::set_sample_count(SampleCountflagBitsType sample_count_)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
sample_count = sample_count_;
}
else
{
sample_count = static_cast<vk::SampleCountFlagBits>(sample_count_);
}
}
template <vkb::BindingType bindingType>
inline void Subpass<bindingType>::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