diff --git a/app/plugins/screenshot/screenshot.cpp b/app/plugins/screenshot/screenshot.cpp index 14f5648539..0c677d2b8b 100644 --- a/app/plugins/screenshot/screenshot.cpp +++ b/app/plugins/screenshot/screenshot.cpp @@ -21,6 +21,7 @@ #include #include +#include "common/utils.h" #include "rendering/render_context.h" namespace plugins @@ -79,7 +80,7 @@ void Screenshot::on_app_start(const std::string &name) current_frame = 0; } -void Screenshot::on_post_draw(vkb::RenderContext &context) +void Screenshot::on_post_draw(vkb::rendering::RenderContextC &context) { if (current_frame == frame_number) { @@ -99,7 +100,7 @@ void Screenshot::on_post_draw(vkb::RenderContext &context) output_path = stream.str(); } - screenshot(context, output_path); + vkb::screenshot(context, output_path); } } } // namespace plugins \ No newline at end of file diff --git a/app/plugins/screenshot/screenshot.h b/app/plugins/screenshot/screenshot.h index f4735261f5..6b42d89658 100644 --- a/app/plugins/screenshot/screenshot.h +++ b/app/plugins/screenshot/screenshot.h @@ -44,7 +44,7 @@ class Screenshot : public ScreenshotTags void on_update(float delta_time) override; void on_app_start(const std::string &app_info) override; - void on_post_draw(vkb::RenderContext &context) override; + void on_post_draw(vkb::rendering::RenderContextC &context) override; bool handle_option(std::deque &arguments) override; diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index 6f926dbe49..bbe596e660 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -110,7 +110,6 @@ set(RENDERING_FILES rendering/render_target.h rendering/subpass.h rendering/hpp_pipeline_state.h - rendering/hpp_render_context.h rendering/hpp_render_pipeline.h rendering/hpp_render_target.h # Source files @@ -119,10 +118,8 @@ set(RENDERING_FILES rendering/postprocessing_pass.cpp rendering/postprocessing_renderpass.cpp rendering/postprocessing_computepass.cpp - rendering/render_context.cpp rendering/render_pipeline.cpp rendering/render_target.cpp - rendering/hpp_render_context.cpp rendering/hpp_render_target.cpp) set(RENDERING_SUBPASSES_FILES diff --git a/framework/common/hpp_utils.h b/framework/common/hpp_utils.h index b8ec44d8c0..1cb07834df 100644 --- a/framework/common/hpp_utils.h +++ b/framework/common/hpp_utils.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -17,10 +17,8 @@ #pragma once -#include - -#include -#include +#include "common/utils.h" +#include "scene_graph/hpp_scene.h" /** * @brief facade helper functions around the functions in common/utils.h, providing a vulkan.hpp-based interface @@ -34,9 +32,9 @@ inline sg::Node &add_free_camera(vkb::scene_graph::HPPScene &scene, const std::s return vkb::add_free_camera(reinterpret_cast(scene), node_name, static_cast(extent)); } -inline void screenshot(vkb::rendering::HPPRenderContext &render_context, const std::string &filename) +inline void screenshot(vkb::rendering::RenderContextCpp &render_context, const std::string &filename) { - vkb::screenshot(reinterpret_cast(render_context), filename); + vkb::screenshot(reinterpret_cast(render_context), filename); } } // namespace common } // namespace vkb diff --git a/framework/common/hpp_vk_common.h b/framework/common/hpp_vk_common.h index dd9c4fc8b1..23db9ad819 100644 --- a/framework/common/hpp_vk_common.h +++ b/framework/common/hpp_vk_common.h @@ -203,6 +203,16 @@ inline vk::Format choose_blendable_format(vk::PhysicalDevice gpu, const std::vec throw std::runtime_error("No suitable blendable format could be determined"); } +inline vk::ImageCompressionPropertiesEXT query_applied_compression(vk::Device device, vk::Image image) +{ + vk::ImageSubresource2EXT image_subresource{ + .imageSubresource = {.aspectMask = vk::ImageAspectFlagBits::eColor, .mipLevel = 0, .arrayLayer = 0}}; + + auto imageSubresourceLayout = device.getImageSubresourceLayout2EXT(image, image_subresource); + + return imageSubresourceLayout.get(); +} + // helper functions not backed by vk_common.h inline vk::CommandBuffer allocate_command_buffer(vk::Device device, vk::CommandPool command_pool, vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary) diff --git a/framework/common/utils.cpp b/framework/common/utils.cpp index 8d3878b10c..d03e2abeae 100644 --- a/framework/common/utils.cpp +++ b/framework/common/utils.cpp @@ -20,6 +20,8 @@ #include #include +#include "core/command_buffer.h" +#include "rendering/render_frame.h" #include "scene_graph/components/material.h" #include "scene_graph/components/perspective_camera.h" #include "scene_graph/components/sub_mesh.h" @@ -40,7 +42,7 @@ std::string get_extension(const std::string &uri) return uri.substr(dot_pos + 1); } -void screenshot(RenderContext &render_context, const std::string &filename) +void screenshot(vkb::rendering::RenderContextC &render_context, const std::string &filename) { assert(render_context.get_format() == VK_FORMAT_R8G8B8A8_UNORM || render_context.get_format() == VK_FORMAT_B8G8R8A8_UNORM || diff --git a/framework/common/utils.h b/framework/common/utils.h index 5faeb1b64a..4d7fed4ce7 100644 --- a/framework/common/utils.h +++ b/framework/common/utils.h @@ -54,7 +54,7 @@ std::string to_snake_case(const std::string &name); * @param render_context The RenderContext to use * @param filename The name of the file to save the output to */ -void screenshot(RenderContext &render_context, const std::string &filename); +void screenshot(vkb::rendering::RenderContextC &render_context, const std::string &filename); /** * @brief Adds a light to the scene with the specified parameters diff --git a/framework/core/command_buffer.h b/framework/core/command_buffer.h index c6fe83e724..de14177596 100644 --- a/framework/core/command_buffer.h +++ b/framework/core/command_buffer.h @@ -30,6 +30,7 @@ #include "rendering/hpp_pipeline_state.h" #include "rendering/hpp_render_target.h" #include "rendering/subpass.h" +#include "resource_cache.h" namespace vkb { diff --git a/framework/core/hpp_swapchain.cpp b/framework/core/hpp_swapchain.cpp index ee2e3951ff..5b77c55f28 100644 --- a/framework/core/hpp_swapchain.cpp +++ b/framework/core/hpp_swapchain.cpp @@ -29,6 +29,11 @@ constexpr const T &clamp(const T &v, const T &lo, const T &hi) return (v < lo) ? lo : ((hi < v) ? hi : v); } +inline uint32_t choose_image_count(uint32_t request_image_count, uint32_t min_image_count, uint32_t max_image_count) +{ + return clamp(request_image_count, min_image_count, (max_image_count != 0) ? max_image_count : request_image_count); +} + vk::Extent2D choose_extent(vk::Extent2D request_extent, const vk::Extent2D &min_image_extent, const vk::Extent2D &max_image_extent, @@ -111,6 +116,11 @@ vk::SurfaceFormatKHR choose_surface_format(const vk::SurfaceFormatKHR } } +inline uint32_t choose_image_array_layers(uint32_t request_image_array_layers, uint32_t max_image_array_layers) +{ + return clamp(request_image_array_layers, 1u, max_image_array_layers); +} + vk::SurfaceTransformFlagBitsKHR choose_transform(vk::SurfaceTransformFlagBitsKHR request_transform, vk::SurfaceTransformFlagsKHR supported_transform, vk::SurfaceTransformFlagBitsKHR current_transform) @@ -221,7 +231,8 @@ vk::ImageUsageFlags composite_image_flags(std::set &imag namespace core { HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const vk::Extent2D &extent) : - HPPSwapchain{old_swapchain.device, + HPPSwapchain{old_swapchain, + old_swapchain.device, old_swapchain.surface, old_swapchain.properties.present_mode, old_swapchain.present_mode_priority_list, @@ -230,11 +241,13 @@ HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const vk::Extent2D &exte old_swapchain.properties.image_count, old_swapchain.properties.pre_transform, old_swapchain.image_usage_flags, - old_swapchain.get_handle()} + old_swapchain.requested_compression, + old_swapchain.requested_compression_fixed_rate} {} HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const uint32_t image_count) : - HPPSwapchain{old_swapchain.device, + HPPSwapchain{old_swapchain, + old_swapchain.device, old_swapchain.surface, old_swapchain.properties.present_mode, old_swapchain.present_mode_priority_list, @@ -243,11 +256,13 @@ HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const uint32_t image_cou image_count, old_swapchain.properties.pre_transform, old_swapchain.image_usage_flags, - old_swapchain.get_handle()} + old_swapchain.requested_compression, + old_swapchain.requested_compression_fixed_rate} {} HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const std::set &image_usage_flags) : - HPPSwapchain{old_swapchain.device, + HPPSwapchain{old_swapchain, + old_swapchain.device, old_swapchain.surface, old_swapchain.properties.present_mode, old_swapchain.present_mode_priority_list, @@ -256,11 +271,13 @@ HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const std::set &present_mode_priority_list, + const std::vector &surface_format_priority_list, + const vk::Extent2D &extent, + const uint32_t image_count, + const vk::SurfaceTransformFlagBitsKHR transform, + const std::set &image_usage_flags, + const vk::ImageCompressionFlagsEXT requested_compression, + const vk::ImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate) : + HPPSwapchain{*this, device, surface, present_mode, present_mode_priority_list, surface_format_priority_list, extent, image_count, transform, image_usage_flags} {} -HPPSwapchain::HPPSwapchain(vkb::core::DeviceCpp &device, - vk::SurfaceKHR surface, - const vk::PresentModeKHR present_mode, - const std::vector &present_mode_priority_list, - const std::vector &surface_format_priority_list, - const vk::Extent2D &extent, - const uint32_t image_count, - const vk::SurfaceTransformFlagBitsKHR transform, - const std::set &image_usage_flags, - vk::SwapchainKHR old_swapchain) : +HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, + vkb::core::DeviceCpp &device, + vk::SurfaceKHR surface, + const vk::PresentModeKHR present_mode, + std::vector const &present_mode_priority_list, + const std::vector &surface_format_priority_list, + const vk::Extent2D &extent, + const uint32_t image_count, + const vk::SurfaceTransformFlagBitsKHR transform, + const std::set &image_usage_flags, + const vk::ImageCompressionFlagsEXT requested_compression, + const vk::ImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate) : device{device}, - surface{surface} + surface{surface}, + requested_compression{requested_compression}, + requested_compression_fixed_rate{requested_compression_fixed_rate} { this->present_mode_priority_list = present_mode_priority_list; this->surface_format_priority_list = surface_format_priority_list; @@ -305,13 +359,11 @@ HPPSwapchain::HPPSwapchain(vkb::core::DeviceCpp &device, // Choose best properties based on surface capabilities vk::SurfaceCapabilitiesKHR const surface_capabilities = device.get_gpu().get_handle().getSurfaceCapabilitiesKHR(surface); - properties.old_swapchain = old_swapchain; - properties.image_count = clamp(image_count, - surface_capabilities.minImageCount, - surface_capabilities.maxImageCount ? surface_capabilities.maxImageCount : std::numeric_limits::max()); + properties.old_swapchain = old_swapchain.get_handle(); + properties.image_count = choose_image_count(image_count, surface_capabilities.minImageCount, surface_capabilities.maxImageCount); properties.extent = choose_extent(extent, surface_capabilities.minImageExtent, surface_capabilities.maxImageExtent, surface_capabilities.currentExtent); properties.surface_format = choose_surface_format(properties.surface_format, surface_formats, surface_format_priority_list); - properties.array_layers = 1; + properties.array_layers = choose_image_array_layers(1U, surface_capabilities.maxImageArrayLayers); vk::FormatProperties const format_properties = device.get_gpu().get_handle().getFormatProperties(properties.surface_format.format); this->image_usage_flags = choose_image_usage(image_usage_flags, surface_capabilities.supportedUsageFlags, format_properties.optimalTilingFeatures); @@ -321,21 +373,75 @@ HPPSwapchain::HPPSwapchain(vkb::core::DeviceCpp &device, properties.composite_alpha = choose_composite_alpha(vk::CompositeAlphaFlagBitsKHR::eInherit, surface_capabilities.supportedCompositeAlpha); properties.present_mode = choose_present_mode(present_mode, present_modes, present_mode_priority_list); - vk::SwapchainCreateInfoKHR const create_info{.surface = surface, - .minImageCount = properties.image_count, - .imageFormat = properties.surface_format.format, - .imageColorSpace = properties.surface_format.colorSpace, - .imageExtent = properties.extent, - .imageArrayLayers = properties.array_layers, - .imageUsage = properties.image_usage, - .preTransform = properties.pre_transform, - .compositeAlpha = properties.composite_alpha, - .presentMode = properties.present_mode, - .oldSwapchain = properties.old_swapchain}; + vk::SwapchainCreateInfoKHR create_info{.surface = surface, + .minImageCount = properties.image_count, + .imageFormat = properties.surface_format.format, + .imageColorSpace = properties.surface_format.colorSpace, + .imageExtent = properties.extent, + .imageArrayLayers = properties.array_layers, + .imageUsage = properties.image_usage, + .preTransform = properties.pre_transform, + .compositeAlpha = properties.composite_alpha, + .presentMode = properties.present_mode, + .oldSwapchain = properties.old_swapchain}; + + auto fixed_rate_flags = requested_compression_fixed_rate; + vk::ImageCompressionControlEXT compression_control; + compression_control.flags = requested_compression; + if (device.is_extension_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME)) + { + create_info.pNext = &compression_control; + + if (vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit == requested_compression) + { + // Do not support compression for multi-planar formats + compression_control.compressionControlPlaneCount = 1; + compression_control.pFixedRateFlags = &fixed_rate_flags; + } + else if (vk::ImageCompressionFlagBitsEXT::eDisabled == requested_compression) + { + LOGW("(Swapchain) Disabling default (lossless) compression, which can negatively impact performance") + } + } + else + { + if (vk::ImageCompressionFlagBitsEXT::eDefault != requested_compression) + { + LOGW("(Swapchain) Compression cannot be controlled because VK_EXT_image_compression_control_swapchain is not enabled") + + this->requested_compression = vk::ImageCompressionFlagBitsEXT::eDefault; + this->requested_compression_fixed_rate = vk::ImageCompressionFixedRateFlagBitsEXT::eNone; + } + } handle = device.get_handle().createSwapchainKHR(create_info); images = device.get_handle().getSwapchainImagesKHR(handle); + + if (device.is_extension_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME) && + vk::ImageCompressionFlagBitsEXT::eFixedRateDefault == requested_compression) + { + // Check if fixed-rate compression was applied + const auto applied_compression_fixed_rate = vkb::common::query_applied_compression(device.get_handle(), images[0]).imageCompressionFixedRateFlags; + + if (applied_compression_fixed_rate != requested_compression_fixed_rate) + { + LOGW("(Swapchain) Requested fixed-rate compression ({}) was not applied, instead images use {}", + vk::to_string(requested_compression_fixed_rate), + vk::to_string(applied_compression_fixed_rate)); + + this->requested_compression_fixed_rate = applied_compression_fixed_rate; + + if (vk::ImageCompressionFixedRateFlagBitsEXT::eNone == applied_compression_fixed_rate) + { + this->requested_compression = vk::ImageCompressionFlagBitsEXT::eDefault; + } + } + else + { + LOGI("(Swapchain) Applied fixed-rate compression: {}", vk::to_string(applied_compression_fixed_rate)); + } + } } HPPSwapchain::~HPPSwapchain() diff --git a/framework/core/hpp_swapchain.h b/framework/core/hpp_swapchain.h index 8de77106c0..a5505bd206 100644 --- a/framework/core/hpp_swapchain.h +++ b/framework/core/hpp_swapchain.h @@ -69,20 +69,47 @@ class HPPSwapchain */ HPPSwapchain(HPPSwapchain &swapchain, const vk::Extent2D &extent, const vk::SurfaceTransformFlagBitsKHR transform); + /** + * @brief Constructor to create a swapchain by changing the compression settings + * only and preserving the configuration from the old swapchain. + */ + HPPSwapchain(HPPSwapchain &swapchain, + const vk::ImageCompressionFlagsEXT requested_compression, + const vk::ImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate); + /** * @brief Constructor to create a swapchain. */ - HPPSwapchain(vkb::core::DeviceCpp &device, - vk::SurfaceKHR surface, - const vk::PresentModeKHR present_mode, - const std::vector &present_mode_priority_list = {vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox}, - const std::vector &surface_format_priority_list = {{vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}, - {vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}}, - const vk::Extent2D &extent = {}, - const uint32_t image_count = 3, - const vk::SurfaceTransformFlagBitsKHR transform = vk::SurfaceTransformFlagBitsKHR::eIdentity, - const std::set &image_usage_flags = {vk::ImageUsageFlagBits::eColorAttachment, vk::ImageUsageFlagBits::eTransferSrc}, - vk::SwapchainKHR old_swapchain = nullptr); + HPPSwapchain(vkb::core::DeviceCpp &device, + vk::SurfaceKHR surface, + const vk::PresentModeKHR present_mode, + const std::vector &present_mode_priority_list = {vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox}, + const std::vector &surface_format_priority_list = {{vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}, + {vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}}, + const vk::Extent2D &extent = {}, + const uint32_t image_count = 3, + const vk::SurfaceTransformFlagBitsKHR transform = vk::SurfaceTransformFlagBitsKHR::eIdentity, + const std::set &image_usage_flags = {vk::ImageUsageFlagBits::eColorAttachment, vk::ImageUsageFlagBits::eTransferSrc}, + const vk::ImageCompressionFlagsEXT requested_compression = vk::ImageCompressionFlagBitsEXT::eDefault, + const vk::ImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate = vk::ImageCompressionFixedRateFlagBitsEXT::eNone); + + /** + * @brief Constructor to create a swapchain from the old swapchain + * by configuring all parameters. + */ + HPPSwapchain(HPPSwapchain &old_swapchain, + vkb::core::DeviceCpp &device, + vk::SurfaceKHR surface, + const vk::PresentModeKHR present_mode, + const std::vector &present_mode_priority_list = {vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox}, + const std::vector &surface_format_priority_list = {{vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}, + {vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}}, + const vk::Extent2D &extent = {}, + const uint32_t image_count = 3, + const vk::SurfaceTransformFlagBitsKHR transform = vk::SurfaceTransformFlagBitsKHR::eIdentity, + const std::set &image_usage_flags = {vk::ImageUsageFlagBits::eColorAttachment, vk::ImageUsageFlagBits::eTransferSrc}, + const vk::ImageCompressionFlagsEXT requested_compression = vk::ImageCompressionFlagBitsEXT::eDefault, + const vk::ImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate = vk::ImageCompressionFixedRateFlagBitsEXT::eNone); HPPSwapchain(const HPPSwapchain &) = delete; @@ -134,6 +161,10 @@ class HPPSwapchain std::vector surface_format_priority_list; std::set image_usage_flags; + + vk::ImageCompressionFlagsEXT requested_compression{vk::ImageCompressionFlagBitsEXT::eDefault}; + + vk::ImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate{vk::ImageCompressionFixedRateFlagBitsEXT::eNone}; }; } // namespace core } // namespace vkb diff --git a/framework/gui.h b/framework/gui.h index 449a078eca..fef35a08fa 100644 --- a/framework/gui.h +++ b/framework/gui.h @@ -103,7 +103,6 @@ class Gui using PipelineCacheType = typename std::conditional::type; using PipelineLayoutType = typename std::conditional::type; using PipelineShaderStageCreateInfoType = typename std::conditional::type; - using RenderContextType = typename std::conditional::type; using RenderPassType = typename std::conditional::type; using StatsType = typename std::conditional::type; @@ -162,7 +161,11 @@ class Gui * @param font_size The font size * @param explicit_update If true, update buffers every frame */ - Gui(RenderContextType &render_context, Window const &window, StatsType const *stats = nullptr, float font_size = 21.0f, bool explicit_update = false); + Gui(vkb::rendering::RenderContext &render_context, + Window const &window, + StatsType const *stats = nullptr, + float font_size = 21.0f, + bool explicit_update = false); /** * @brief Destroys the Gui @@ -310,7 +313,7 @@ class Gui vk::Pipeline pipeline; vkb::core::HPPPipelineLayout *pipeline_layout = nullptr; bool prev_visible = true; - vkb::rendering::HPPRenderContext &render_context; + vkb::rendering::RenderContextCpp &render_context; std::unique_ptr sampler; StatsView stats_view; uint32_t subpass = 0; @@ -323,7 +326,8 @@ using GuiC = Gui; using GuiCpp = Gui; template -inline Gui::Gui(RenderContextType &render_context_, Window const &window, StatsType const *stats, float font_size, bool explicit_update) : +inline Gui::Gui( + vkb::rendering::RenderContext &render_context_, Window const &window, StatsType const *stats, float font_size, bool explicit_update) : render_context{render_context_}, content_scale_factor{window.get_content_scale_factor()}, dpi_factor{window.get_dpi_factor() * content_scale_factor}, diff --git a/framework/platform/platform.cpp b/framework/platform/platform.cpp index 2ec821aae1..6bc1d97df0 100644 --- a/framework/platform/platform.cpp +++ b/framework/platform/platform.cpp @@ -280,7 +280,7 @@ void Platform::update() { if (app->has_render_context()) { - on_post_draw(reinterpret_cast(app->get_render_context())); + on_post_draw(reinterpret_cast(app->get_render_context())); } } else if (auto *app = dynamic_cast(active_app.get())) @@ -531,7 +531,7 @@ void Platform::resize(uint32_t width, uint32_t height) } \ } -void Platform::on_post_draw(RenderContext &context) +void Platform::on_post_draw(vkb::rendering::RenderContextC &context) { HOOK(Hook::PostDraw, on_post_draw(context)); } diff --git a/framework/platform/platform.h b/framework/platform/platform.h index fec06b45e7..72195669d1 100644 --- a/framework/platform/platform.h +++ b/framework/platform/platform.h @@ -130,7 +130,7 @@ class Platform void set_window_properties(const Window::OptionalProperties &properties); - void on_post_draw(RenderContext &context); + void on_post_draw(vkb::rendering::RenderContextC &context); static const uint32_t MIN_WINDOW_WIDTH; static const uint32_t MIN_WINDOW_HEIGHT; diff --git a/framework/platform/plugins/plugin.h b/framework/platform/plugins/plugin.h index 31a180028b..433e5a6326 100644 --- a/framework/platform/plugins/plugin.h +++ b/framework/platform/plugins/plugin.h @@ -30,9 +30,15 @@ namespace vkb { class Platform; -class RenderContext; class Plugin; +namespace rendering +{ +template +class RenderContext; +using RenderContextC = RenderContext; +} // namespace rendering + /** * @brief Tags are used to define a plugins behaviour. This is useful to dictate which plugins will work together * and which will not without directly specifying an exclusion or inclusion list. Tags are struct types so that they can @@ -140,7 +146,7 @@ class Plugin /** * @brief Post Draw */ - virtual void on_post_draw(RenderContext &context) = 0; + virtual void on_post_draw(vkb::rendering::RenderContextC &context) = 0; /** * @brief Allows to add a UI to a sample diff --git a/framework/platform/plugins/plugin_base.h b/framework/platform/plugins/plugin_base.h index 62b94670cb..41f910a7be 100644 --- a/framework/platform/plugins/plugin_base.h +++ b/framework/platform/plugins/plugin_base.h @@ -26,6 +26,13 @@ namespace vkb { +namespace rendering +{ +template +class RenderContext; +using RenderContextC = RenderContext; +} // namespace rendering + /** * @brief PluginBase is the base class that plugins inherit from. The class enforces the use of tags when creating new plugins. * For method information see Plugin @@ -46,7 +53,7 @@ class PluginBase : public Plugin, public Tag void on_app_start(const std::string &app_id) override{}; void on_app_close(const std::string &app_id) override{}; void on_platform_close() override{}; - void on_post_draw(RenderContext &context) override{}; + void on_post_draw(vkb::rendering::RenderContextC &context) override{}; void on_app_error(const std::string &app_id) override{}; void on_update_ui_overlay(vkb::Drawer &drawer) override{}; diff --git a/framework/rendering/hpp_render_context.cpp b/framework/rendering/hpp_render_context.cpp deleted file mode 100644 index 92da7ebd5a..0000000000 --- a/framework/rendering/hpp_render_context.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved. - * Copyright (c) 2024-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 "rendering/hpp_render_context.h" -#include "buffer_pool.h" -#include "core/command_buffer.h" -#include "core/hpp_queue.h" -#include "core/physical_device.h" -#include "platform/window.h" - -namespace vkb -{ -namespace rendering -{ -vk::Format HPPRenderContext::DEFAULT_VK_FORMAT = vk::Format::eR8G8B8A8Srgb; - -HPPRenderContext::HPPRenderContext(vkb::core::DeviceCpp &device, - vk::SurfaceKHR surface, - const vkb::Window &window, - vk::PresentModeKHR present_mode, - std::vector const &present_mode_priority_list, - std::vector const &surface_format_priority_list) : - device{device}, window{window}, queue{device.get_queue_by_flags(vk::QueueFlagBits::eGraphics, 0)}, surface_extent{window.get_extent().width, window.get_extent().height} -{ - if (surface) - { - vk::SurfaceCapabilitiesKHR surface_properties = device.get_gpu().get_handle().getSurfaceCapabilitiesKHR(surface); - - if (surface_properties.currentExtent.width == 0xFFFFFFFF) - { - swapchain = - std::make_unique(device, surface, present_mode, present_mode_priority_list, surface_format_priority_list, surface_extent); - } - else - { - swapchain = std::make_unique(device, surface, present_mode, present_mode_priority_list, surface_format_priority_list); - } - } -} - -void HPPRenderContext::prepare(size_t thread_count, vkb::rendering::HPPRenderTarget::CreateFunc create_render_target_func) -{ - device.get_handle().waitIdle(); - - if (swapchain) - { - surface_extent = swapchain->get_extent(); - - vk::Extent3D extent{surface_extent.width, surface_extent.height, 1}; - - for (auto &image_handle : swapchain->get_images()) - { - auto swapchain_image = core::HPPImage{device, image_handle, extent, swapchain->get_format(), swapchain->get_usage()}; - auto render_target = create_render_target_func(std::move(swapchain_image)); - frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); - } - } - else - { - // Otherwise, create a single RenderFrame - swapchain = nullptr; - - auto color_image = vkb::core::HPPImage{device, - vk::Extent3D{surface_extent.width, surface_extent.height, 1}, - DEFAULT_VK_FORMAT, // We can use any format here that we like - vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc, - VMA_MEMORY_USAGE_GPU_ONLY}; - - auto render_target = create_render_target_func(std::move(color_image)); - frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); - } - - this->create_render_target_func = create_render_target_func; - this->thread_count = thread_count; - this->prepared = true; -} - -vk::Format HPPRenderContext::get_format() const -{ - return swapchain ? swapchain->get_format() : DEFAULT_VK_FORMAT; -} - -void HPPRenderContext::update_swapchain(const vk::Extent2D &extent) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains extent. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - swapchain = std::make_unique(*swapchain, extent); - - recreate(); -} - -void HPPRenderContext::update_swapchain(const uint32_t image_count) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains image count. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - device.get_handle().waitIdle(); - - swapchain = std::make_unique(*swapchain, image_count); - - recreate(); -} - -void HPPRenderContext::update_swapchain(const std::set &image_usage_flags) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains image usage. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - swapchain = std::make_unique(*swapchain, image_usage_flags); - - recreate(); -} - -void HPPRenderContext::update_swapchain(const vk::Extent2D &extent, const vk::SurfaceTransformFlagBitsKHR transform) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains extent and surface transform. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - auto width = extent.width; - auto height = extent.height; - if (transform == vk::SurfaceTransformFlagBitsKHR::eRotate90 || transform == vk::SurfaceTransformFlagBitsKHR::eRotate270) - { - // Pre-rotation: always use native orientation i.e. if rotated, use width and height of identity transform - std::swap(width, height); - } - - swapchain = std::make_unique(*swapchain, vk::Extent2D{width, height}, transform); - - // Save the preTransform attribute for future rotations - pre_transform = transform; - - recreate(); -} - -void HPPRenderContext::recreate() -{ - LOGI("Recreated swapchain"); - - vk::Extent2D swapchain_extent = swapchain->get_extent(); - vk::Extent3D extent{swapchain_extent.width, swapchain_extent.height, 1}; - - auto frame_it = frames.begin(); - - for (auto &image_handle : swapchain->get_images()) - { - vkb::core::HPPImage swapchain_image{device, image_handle, extent, swapchain->get_format(), swapchain->get_usage()}; - - auto render_target = create_render_target_func(std::move(swapchain_image)); - - if (frame_it != frames.end()) - { - (*frame_it)->update_render_target(std::move(render_target)); - } - else - { - // Create a new frame if the new swapchain has more images than current frames - frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); - } - - ++frame_it; - } - - device.get_resource_cache().clear_framebuffers(); -} - -bool HPPRenderContext::handle_surface_changes(bool force_update) -{ - if (!swapchain) - { - LOGW("Can't handle surface changes. No swapchain, offscreen rendering detected, skipping."); - return false; - } - - vk::SurfaceCapabilitiesKHR surface_properties = device.get_gpu().get_handle().getSurfaceCapabilitiesKHR(swapchain->get_surface()); - - if (surface_properties.currentExtent.width == 0xFFFFFFFF) - { - return false; - } - - // Only recreate the swapchain if the dimensions have changed; - // handle_surface_changes() is called on VK_SUBOPTIMAL_KHR, - // which might not be due to a surface resize - if (surface_properties.currentExtent.width != surface_extent.width || - surface_properties.currentExtent.height != surface_extent.height || - force_update) - { - // Recreate swapchain - device.get_handle().waitIdle(); - - update_swapchain(surface_properties.currentExtent, pre_transform); - - surface_extent = surface_properties.currentExtent; - - return true; - } - - return false; -} - -std::shared_ptr HPPRenderContext::begin(vkb::CommandBufferResetMode reset_mode) -{ - assert(prepared && "HPPRenderContext not prepared for rendering, call prepare()"); - - if (!frame_active) - { - begin_frame(); - } - - if (!acquired_semaphore) - { - throw std::runtime_error("Couldn't begin frame"); - } - - const auto &queue = device.get_queue_by_flags(vk::QueueFlagBits::eGraphics, 0); - return get_active_frame().get_command_pool(queue, reset_mode).request_command_buffer(); -} - -void HPPRenderContext::submit(vkb::core::CommandBufferCpp &command_buffer) -{ - submit({&command_buffer}); -} - -void HPPRenderContext::submit(const std::vector &command_buffers) -{ - assert(frame_active && "HPPRenderContext is inactive, cannot submit command buffer. Please call begin()"); - - vk::Semaphore render_semaphore; - - if (swapchain) - { - assert(acquired_semaphore && "We do not have acquired_semaphore, it was probably consumed?\n"); - render_semaphore = submit(queue, command_buffers, acquired_semaphore, vk::PipelineStageFlagBits::eColorAttachmentOutput); - } - else - { - submit(queue, command_buffers); - } - - end_frame(render_semaphore); -} - -void HPPRenderContext::begin_frame() -{ - // Only handle surface changes if a swapchain exists - if (swapchain) - { - handle_surface_changes(); - } - - assert(!frame_active && "Frame is still active, please call end_frame"); - - auto &prev_frame = *frames[active_frame_index]; - - // We will use the acquired semaphore in a different frame context, - // so we need to hold ownership. - acquired_semaphore = prev_frame.get_semaphore_pool().request_semaphore_with_ownership(); - - if (swapchain) - { - vk::Result result; - try - { - std::tie(result, active_frame_index) = swapchain->acquire_next_image(acquired_semaphore); - } - catch (vk::OutOfDateKHRError & /*err*/) - { - result = vk::Result::eErrorOutOfDateKHR; - } - - if (result == vk::Result::eSuboptimalKHR || result == vk::Result::eErrorOutOfDateKHR) - { -#if defined(PLATFORM__MACOS) - // On Apple platforms, force swapchain update on both eSuboptimalKHR and eErrorOutOfDateKHR - // eSuboptimalKHR may occur on macOS/iOS following changes to swapchain other than extent/size - bool swapchain_updated = handle_surface_changes(true); -#else - bool swapchain_updated = handle_surface_changes(result == vk::Result::eErrorOutOfDateKHR); -#endif - - if (swapchain_updated) - { - // Need to destroy and reallocate acquired_semaphore since it may have already been signaled - device.get_handle().destroySemaphore(acquired_semaphore); - acquired_semaphore = prev_frame.get_semaphore_pool().request_semaphore_with_ownership(); - std::tie(result, active_frame_index) = swapchain->acquire_next_image(acquired_semaphore); - } - } - - if (result != vk::Result::eSuccess) - { - prev_frame.reset(); - return; - } - } - - // Now the frame is active again - frame_active = true; - - // Wait on all resource to be freed from the previous render to this frame - wait_frame(); -} - -vk::Semaphore HPPRenderContext::submit(const vkb::core::HPPQueue &queue, - const std::vector &command_buffers, - vk::Semaphore wait_semaphore, - vk::PipelineStageFlags wait_pipeline_stage) -{ - std::vector cmd_buf_handles(command_buffers.size(), nullptr); - std::ranges::transform(command_buffers, cmd_buf_handles.begin(), [](const vkb::core::CommandBufferCpp *cmd_buf) { return cmd_buf->get_handle(); }); - - vkb::rendering::RenderFrameCpp &frame = get_active_frame(); - - vk::Semaphore signal_semaphore = frame.get_semaphore_pool().request_semaphore(); - - vk::SubmitInfo submit_info{.commandBufferCount = static_cast(cmd_buf_handles.size()), - .pCommandBuffers = cmd_buf_handles.data(), - .signalSemaphoreCount = 1, - .pSignalSemaphores = &signal_semaphore}; - if (wait_semaphore) - { - submit_info.setWaitSemaphores(wait_semaphore); - submit_info.pWaitDstStageMask = &wait_pipeline_stage; - } - - vk::Fence fence = frame.get_fence_pool().request_fence(); - - queue.get_handle().submit(submit_info, fence); - - return signal_semaphore; -} - -void HPPRenderContext::submit(const vkb::core::HPPQueue &queue, const std::vector &command_buffers) -{ - std::vector cmd_buf_handles(command_buffers.size(), nullptr); - std::ranges::transform(command_buffers, cmd_buf_handles.begin(), [](const vkb::core::CommandBufferCpp *cmd_buf) { return cmd_buf->get_handle(); }); - - vkb::rendering::RenderFrameCpp &frame = get_active_frame(); - - vk::SubmitInfo submit_info{.commandBufferCount = static_cast(cmd_buf_handles.size()), .pCommandBuffers = cmd_buf_handles.data()}; - - vk::Fence fence = frame.get_fence_pool().request_fence(); - - queue.get_handle().submit(submit_info, fence); -} - -void HPPRenderContext::wait_frame() -{ - get_active_frame().reset(); -} - -void HPPRenderContext::end_frame(vk::Semaphore semaphore) -{ - assert(frame_active && "Frame is not active, please call begin_frame"); - - if (swapchain) - { - vk::SwapchainKHR vk_swapchain = swapchain->get_handle(); - vk::PresentInfoKHR present_info{ - .waitSemaphoreCount = 1, .pWaitSemaphores = &semaphore, .swapchainCount = 1, .pSwapchains = &vk_swapchain, .pImageIndices = &active_frame_index}; - - vk::DisplayPresentInfoKHR disp_present_info; - if (device.get_gpu().is_extension_supported(VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME) && - window.get_display_present_info(reinterpret_cast(&disp_present_info), surface_extent.width, surface_extent.height)) - { - // Add display present info if supported and wanted - present_info.pNext = &disp_present_info; - } - - vk::Result result; - try - { - result = queue.present(present_info); - } - catch (vk::OutOfDateKHRError & /*err*/) - { - result = vk::Result::eErrorOutOfDateKHR; - } - - if (result == vk::Result::eSuboptimalKHR || result == vk::Result::eErrorOutOfDateKHR) - { - handle_surface_changes(); - } - } - - // Frame is not active anymore - if (acquired_semaphore) - { - release_owned_semaphore(acquired_semaphore); - acquired_semaphore = nullptr; - } - frame_active = false; -} - -vk::Semaphore HPPRenderContext::consume_acquired_semaphore() -{ - assert(frame_active && "Frame is not active, please call begin_frame"); - return std::exchange(acquired_semaphore, nullptr); -} - -vkb::rendering::RenderFrameCpp &HPPRenderContext::get_active_frame() -{ - assert(frame_active && "Frame is not active, please call begin_frame"); - return *frames[active_frame_index]; -} - -uint32_t HPPRenderContext::get_active_frame_index() -{ - assert(frame_active && "Frame is not active, please call begin_frame"); - return active_frame_index; -} - -vkb::rendering::RenderFrameCpp &HPPRenderContext::get_last_rendered_frame() -{ - assert(!frame_active && "Frame is still active, please call end_frame"); - return *frames[active_frame_index]; -} - -vk::Semaphore HPPRenderContext::request_semaphore() -{ - return get_active_frame().get_semaphore_pool().request_semaphore(); -} - -vk::Semaphore HPPRenderContext::request_semaphore_with_ownership() -{ - return get_active_frame().get_semaphore_pool().request_semaphore_with_ownership(); -} - -void HPPRenderContext::release_owned_semaphore(vk::Semaphore semaphore) -{ - get_active_frame().get_semaphore_pool().release_owned_semaphore(semaphore); -} - -vkb::core::DeviceCpp &HPPRenderContext::get_device() -{ - return device; -} - -void HPPRenderContext::recreate_swapchain() -{ - device.get_handle().waitIdle(); - device.get_resource_cache().clear_framebuffers(); - - vk::Extent2D swapchain_extent = swapchain->get_extent(); - vk::Extent3D extent{swapchain_extent.width, swapchain_extent.height, 1}; - - auto frame_it = frames.begin(); - - for (auto &image_handle : swapchain->get_images()) - { - vkb::core::HPPImage swapchain_image{device, image_handle, extent, swapchain->get_format(), swapchain->get_usage()}; - auto render_target = create_render_target_func(std::move(swapchain_image)); - (*frame_it)->update_render_target(std::move(render_target)); - - ++frame_it; - } -} - -bool HPPRenderContext::has_swapchain() -{ - return swapchain != nullptr; -} - -vkb::core::HPPSwapchain const &HPPRenderContext::get_swapchain() const -{ - assert(swapchain && "Swapchain is not valid"); - return *swapchain; -} - -vk::Extent2D const &HPPRenderContext::get_surface_extent() const -{ - return surface_extent; -} - -uint32_t HPPRenderContext::get_active_frame_index() const -{ - return active_frame_index; -} - -std::vector> &HPPRenderContext::get_render_frames() -{ - return frames; -} - -} // namespace rendering -} // namespace vkb diff --git a/framework/rendering/hpp_render_context.h b/framework/rendering/hpp_render_context.h deleted file mode 100644 index b0849d4e23..0000000000 --- a/framework/rendering/hpp_render_context.h +++ /dev/null @@ -1,254 +0,0 @@ -/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. - * Copyright (c) 2024-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. - */ - -#pragma once - -#include "common/vk_common.h" -#include "core/hpp_swapchain.h" -#include "rendering/hpp_render_target.h" -#include "rendering/render_frame.h" - -namespace vkb -{ -class Window; - -namespace core -{ -template -class CommandBuffer; -using CommandBufferCpp = CommandBuffer; - -class HPPQueue; -} // namespace core - -namespace rendering -{ -/** - * @brief HPPRenderContext is a transcoded version of vkb::RenderContext from vulkan to vulkan-hpp. - * - * See vkb::RenderContext for documentation - */ -class HPPRenderContext -{ - public: - // The format to use for the RenderTargets if a swapchain isn't created - static vk::Format DEFAULT_VK_FORMAT; - - /** - * @brief Constructor - * @param device A valid device - * @param surface A surface, nullptr if in offscreen mode - * @param window The window where the surface was created - * @param present_mode Requests to set the present mode of the swapchain - * @param present_mode_priority_list The order in which the swapchain prioritizes selecting its present mode - * @param surface_format_priority_list The order in which the swapchain prioritizes selecting its surface format - */ - HPPRenderContext(vkb::core::DeviceCpp &device, - vk::SurfaceKHR surface, - const vkb::Window &window, - vk::PresentModeKHR present_mode = vk::PresentModeKHR::eFifo, - std::vector const &present_mode_priority_list = {vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox}, - std::vector const &surface_format_priority_list = { - {vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}, {vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}}); - - HPPRenderContext(const HPPRenderContext &) = delete; - - HPPRenderContext(HPPRenderContext &&) = delete; - - virtual ~HPPRenderContext() = default; - - HPPRenderContext &operator=(const HPPRenderContext &) = delete; - - HPPRenderContext &operator=(HPPRenderContext &&) = delete; - - /** - * @brief Prepares the RenderFrames for rendering - * @param thread_count The number of threads in the application, necessary to allocate this many resource pools for each RenderFrame - * @param create_render_target_func A function delegate, used to create a RenderTarget - */ - void prepare(size_t thread_count = 1, HPPRenderTarget::CreateFunc create_render_target_func = HPPRenderTarget::DEFAULT_CREATE_FUNC); - - /** - * @brief Updates the swapchains extent, if a swapchain exists - * @param extent The width and height of the new swapchain images - */ - void update_swapchain(const vk::Extent2D &extent); - - /** - * @brief Updates the swapchains image count, if a swapchain exists - * @param image_count The amount of images in the new swapchain - */ - void update_swapchain(const uint32_t image_count); - - /** - * @brief Updates the swapchains image usage, if a swapchain exists - * @param image_usage_flags The usage flags the new swapchain images will have - */ - void update_swapchain(const std::set &image_usage_flags); - - /** - * @brief Updates the swapchains extent and surface transform, if a swapchain exists - * @param extent The width and height of the new swapchain images - * @param transform The surface transform flags - */ - void update_swapchain(const vk::Extent2D &extent, const vk::SurfaceTransformFlagBitsKHR transform); - - /** - * @returns True if a valid swapchain exists in the HPPRenderContext - */ - bool has_swapchain(); - - /** - * @brief Recreates the RenderFrames, called after every update - */ - void recreate(); - - /** - * @brief Recreates the swapchain - */ - void recreate_swapchain(); - - /** - * @brief Prepares the next available frame for rendering - * @param reset_mode How to reset the command buffer - * @returns A valid command buffer to record commands to be submitted - * Also ensures that there is an active frame if there is no existing active frame already - */ - std::shared_ptr begin(vkb::CommandBufferResetMode reset_mode = vkb::CommandBufferResetMode::ResetPool); - - /** - * @brief Submits the command buffer to the right queue - * @param command_buffer A command buffer containing recorded commands - */ - void submit(vkb::core::CommandBufferCpp &command_buffer); - - /** - * @brief Submits multiple command buffers to the right queue - * @param command_buffers Command buffers containing recorded commands - */ - void submit(const std::vector &command_buffers); - - /** - * @brief begin_frame - */ - void begin_frame(); - - vk::Semaphore submit(const vkb::core::HPPQueue &queue, - const std::vector &command_buffers, - vk::Semaphore wait_semaphore, - vk::PipelineStageFlags wait_pipeline_stage); - - /** - * @brief Submits a command buffer related to a frame to a queue - */ - void submit(const vkb::core::HPPQueue &queue, const std::vector &command_buffers); - - /** - * @brief Waits a frame to finish its rendering - */ - virtual void wait_frame(); - - void end_frame(vk::Semaphore semaphore); - - /** - * @brief An error should be raised if the frame is not active. - * A frame is active after @ref begin_frame has been called. - * @return The current active frame - */ - vkb::rendering::RenderFrameCpp &get_active_frame(); - - /** - * @brief An error should be raised if the frame is not active. - * A frame is active after @ref begin_frame has been called. - * @return The current active frame index - */ - uint32_t get_active_frame_index(); - - /** - * @brief An error should be raised if a frame is active. - * A frame is active after @ref begin_frame has been called. - * @return The previous frame - */ - vkb::rendering::RenderFrameCpp &get_last_rendered_frame(); - - vk::Semaphore request_semaphore(); - vk::Semaphore request_semaphore_with_ownership(); - void release_owned_semaphore(vk::Semaphore semaphore); - - vkb::core::DeviceCpp &get_device(); - - /** - * @brief Returns the format that the RenderTargets are created with within the HPPRenderContext - */ - vk::Format get_format() const; - - vkb::core::HPPSwapchain const &get_swapchain() const; - - vk::Extent2D const &get_surface_extent() const; - - uint32_t get_active_frame_index() const; - - std::vector> &get_render_frames(); - - /** - * @brief Handles surface changes, only applicable if the render_context makes use of a swapchain - */ - virtual bool handle_surface_changes(bool force_update = false); - - /** - * @brief Returns the WSI acquire semaphore. Only to be used in very special circumstances. - * @return The WSI acquire semaphore. - */ - vk::Semaphore consume_acquired_semaphore(); - - protected: - vk::Extent2D surface_extent; - - private: - vkb::core::DeviceCpp &device; - - const vkb::Window &window; - - /// If swapchain exists, then this will be a present supported queue, else a graphics queue - const vkb::core::HPPQueue &queue; - - std::unique_ptr swapchain; - - vkb::core::HPPSwapchainProperties swapchain_properties; - - std::vector> frames; - - vk::Semaphore acquired_semaphore; - - bool prepared{false}; - - /// Current active frame index - uint32_t active_frame_index{0}; - - /// Whether a frame is active or not - bool frame_active{false}; - - HPPRenderTarget::CreateFunc create_render_target_func = HPPRenderTarget::DEFAULT_CREATE_FUNC; - - vk::SurfaceTransformFlagBitsKHR pre_transform{vk::SurfaceTransformFlagBitsKHR::eIdentity}; - - size_t thread_count{1}; -}; - -} // namespace rendering -} // namespace vkb diff --git a/framework/rendering/postprocessing_pass.cpp b/framework/rendering/postprocessing_pass.cpp index dd546c8608..385e0ad6ce 100644 --- a/framework/rendering/postprocessing_pass.cpp +++ b/framework/rendering/postprocessing_pass.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2020, Arm Limited and Contributors +/* Copyright (c) 2020-2025, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -25,7 +25,7 @@ PostProcessingPassBase::PostProcessingPassBase(PostProcessingPipeline *parent) : parent{parent} {} -RenderContext &PostProcessingPassBase::get_render_context() const +vkb::rendering::RenderContextC &PostProcessingPassBase::get_render_context() const { return *parent->render_context; } diff --git a/framework/rendering/postprocessing_pass.h b/framework/rendering/postprocessing_pass.h index 24f76dfecd..7466985f39 100644 --- a/framework/rendering/postprocessing_pass.h +++ b/framework/rendering/postprocessing_pass.h @@ -84,7 +84,7 @@ class PostProcessingPassBase /** * @brief Returns the parent's render context. */ - RenderContext &get_render_context() const; + vkb::rendering::RenderContextC &get_render_context() const; /** * @brief Returns the parent's fullscreen triangle vertex shader source. diff --git a/framework/rendering/postprocessing_pipeline.cpp b/framework/rendering/postprocessing_pipeline.cpp index 46911f571d..1f6ec9fb14 100644 --- a/framework/rendering/postprocessing_pipeline.cpp +++ b/framework/rendering/postprocessing_pipeline.cpp @@ -21,7 +21,7 @@ namespace vkb { -PostProcessingPipeline::PostProcessingPipeline(RenderContext &render_context, ShaderSource triangle_vs) : +PostProcessingPipeline::PostProcessingPipeline(vkb::rendering::RenderContextC &render_context, ShaderSource triangle_vs) : render_context{&render_context}, triangle_vs{std::move(triangle_vs)} {} diff --git a/framework/rendering/postprocessing_pipeline.h b/framework/rendering/postprocessing_pipeline.h index caae70fe37..d380fe3d46 100644 --- a/framework/rendering/postprocessing_pipeline.h +++ b/framework/rendering/postprocessing_pipeline.h @@ -34,7 +34,7 @@ class PostProcessingPipeline /** * @brief Creates a rendering pipeline entirely made of fullscreen post-processing subpasses. */ - PostProcessingPipeline(RenderContext &render_context, ShaderSource triangle_vs); + PostProcessingPipeline(vkb::rendering::RenderContextC &render_context, ShaderSource triangle_vs); PostProcessingPipeline(const PostProcessingPipeline &to_copy) = delete; PostProcessingPipeline &operator=(const PostProcessingPipeline &to_copy) = delete; @@ -82,7 +82,7 @@ class PostProcessingPipeline /** * @brief Returns the current render context. */ - inline RenderContext &get_render_context() const + inline vkb::rendering::RenderContextC &get_render_context() const { return *render_context; } @@ -96,7 +96,7 @@ class PostProcessingPipeline } private: - RenderContext *render_context{nullptr}; + vkb::rendering::RenderContextC *render_context{nullptr}; ShaderSource triangle_vs; std::vector> passes{}; size_t current_pass_index{0}; diff --git a/framework/rendering/postprocessing_renderpass.cpp b/framework/rendering/postprocessing_renderpass.cpp index 182a4d82e5..8582c3d164 100644 --- a/framework/rendering/postprocessing_renderpass.cpp +++ b/framework/rendering/postprocessing_renderpass.cpp @@ -24,8 +24,11 @@ namespace vkb constexpr uint32_t DEPTH_RESOLVE_BITMASK = 0x80000000; constexpr uint32_t ATTACHMENT_BITMASK = 0x7FFFFFFF; -PostProcessingSubpass::PostProcessingSubpass(PostProcessingRenderPass *parent, RenderContext &render_context, ShaderSource &&triangle_vs, - ShaderSource &&fs, ShaderVariant &&fs_variant) : +PostProcessingSubpass::PostProcessingSubpass(PostProcessingRenderPass *parent, + vkb::rendering::RenderContextC &render_context, + ShaderSource &&triangle_vs, + ShaderSource &&fs, + ShaderVariant &&fs_variant) : Subpass(render_context, std::move(triangle_vs), std::move(fs)), parent{parent}, fs_variant{std::move(fs_variant)} diff --git a/framework/rendering/postprocessing_renderpass.h b/framework/rendering/postprocessing_renderpass.h index f3de9fe12f..f7230c5ace 100644 --- a/framework/rendering/postprocessing_renderpass.h +++ b/framework/rendering/postprocessing_renderpass.h @@ -71,8 +71,11 @@ class PostProcessingRenderPass; class PostProcessingSubpass : public vkb::rendering::SubpassC { public: - PostProcessingSubpass(PostProcessingRenderPass *parent, RenderContext &render_context, ShaderSource &&triangle_vs, - ShaderSource &&fs, ShaderVariant &&fs_variant = {}); + PostProcessingSubpass(PostProcessingRenderPass *parent, + vkb::rendering::RenderContextC &render_context, + ShaderSource &&triangle_vs, + ShaderSource &&fs, + ShaderVariant &&fs_variant = {}); PostProcessingSubpass(const PostProcessingSubpass &to_copy) = delete; PostProcessingSubpass &operator=(const PostProcessingSubpass &to_copy) = delete; diff --git a/framework/rendering/render_context.cpp b/framework/rendering/render_context.cpp deleted file mode 100644 index 8c98f180be..0000000000 --- a/framework/rendering/render_context.cpp +++ /dev/null @@ -1,561 +0,0 @@ -/* 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 "render_context.h" - -#include "platform/window.h" - -namespace vkb -{ -VkFormat RenderContext::DEFAULT_VK_FORMAT = VK_FORMAT_R8G8B8A8_SRGB; - -RenderContext::RenderContext(vkb::core::DeviceC &device, - VkSurfaceKHR surface, - const Window &window, - VkPresentModeKHR present_mode, - const std::vector &present_mode_priority_list, - const std::vector &surface_format_priority_list) : - device{device}, window{window}, queue{device.get_queue_by_flags(VK_QUEUE_GRAPHICS_BIT, 0)}, surface_extent{window.get_extent().width, window.get_extent().height} -{ - if (surface != VK_NULL_HANDLE) - { - VkSurfaceCapabilitiesKHR surface_properties; - VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device.get_gpu().get_handle(), - surface, - &surface_properties)); - - if (surface_properties.currentExtent.width == 0xFFFFFFFF) - { - swapchain = std::make_unique(device, surface, present_mode, present_mode_priority_list, surface_format_priority_list, surface_extent); - } - else - { - swapchain = std::make_unique(device, surface, present_mode, present_mode_priority_list, surface_format_priority_list); - } - } -} - -void RenderContext::prepare(size_t thread_count, RenderTarget::CreateFunc create_render_target_func) -{ - device.wait_idle(); - - if (swapchain) - { - surface_extent = swapchain->get_extent(); - - VkExtent3D extent{surface_extent.width, surface_extent.height, 1}; - - for (auto &image_handle : swapchain->get_images()) - { - auto swapchain_image = core::Image{ - device, image_handle, - extent, - swapchain->get_format(), - swapchain->get_usage()}; - auto render_target = create_render_target_func(std::move(swapchain_image)); - frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); - } - } - else - { - // Otherwise, create a single RenderFrame - swapchain = nullptr; - - auto color_image = core::Image{device, - VkExtent3D{surface_extent.width, surface_extent.height, 1}, - DEFAULT_VK_FORMAT, // We can use any format here that we like - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, - VMA_MEMORY_USAGE_GPU_ONLY}; - - auto render_target = create_render_target_func(std::move(color_image)); - frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); - } - - this->create_render_target_func = create_render_target_func; - this->thread_count = thread_count; - this->prepared = true; -} - -VkFormat RenderContext::get_format() const -{ - VkFormat format = DEFAULT_VK_FORMAT; - - if (swapchain) - { - format = swapchain->get_format(); - } - - return format; -} - -void RenderContext::update_swapchain(const VkExtent2D &extent) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains extent. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - swapchain = std::make_unique(*swapchain, extent); - - recreate(); -} - -void RenderContext::update_swapchain(const uint32_t image_count) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains image count. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - device.wait_idle(); - - swapchain = std::make_unique(*swapchain, image_count); - - recreate(); -} - -void RenderContext::update_swapchain(const std::set &image_usage_flags) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains image usage. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - swapchain = std::make_unique(*swapchain, image_usage_flags); - - recreate(); -} - -void RenderContext::update_swapchain(const VkExtent2D &extent, const VkSurfaceTransformFlagBitsKHR transform) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains extent and surface transform. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - auto width = extent.width; - auto height = extent.height; - if (transform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) - { - // Pre-rotation: always use native orientation i.e. if rotated, use width and height of identity transform - std::swap(width, height); - } - - swapchain = std::make_unique(*swapchain, VkExtent2D{width, height}, transform); - - // Save the preTransform attribute for future rotations - pre_transform = transform; - - recreate(); -} - -void RenderContext::update_swapchain(const VkImageCompressionFlagsEXT compression, const VkImageCompressionFixedRateFlagsEXT compression_fixed_rate) -{ - if (!swapchain) - { - LOGW("Can't update the swapchains compression. No swapchain, offscreen rendering detected, skipping."); - return; - } - - device.get_resource_cache().clear_framebuffers(); - - swapchain = std::make_unique(*swapchain, compression, compression_fixed_rate); - - recreate(); -} - -void RenderContext::recreate() -{ - LOGI("Recreated swapchain"); - - VkExtent2D swapchain_extent = swapchain->get_extent(); - VkExtent3D extent{swapchain_extent.width, swapchain_extent.height, 1}; - - auto frame_it = frames.begin(); - - for (auto &image_handle : swapchain->get_images()) - { - core::Image swapchain_image{device, image_handle, - extent, - swapchain->get_format(), - swapchain->get_usage()}; - - auto render_target = create_render_target_func(std::move(swapchain_image)); - - if (frame_it != frames.end()) - { - (*frame_it)->update_render_target(std::move(render_target)); - } - else - { - // Create a new frame if the new swapchain has more images than current frames - frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); - } - - ++frame_it; - } - - device.get_resource_cache().clear_framebuffers(); -} - -bool RenderContext::handle_surface_changes(bool force_update) -{ - if (!swapchain) - { - LOGW("Can't handle surface changes. No swapchain, offscreen rendering detected, skipping."); - return false; - } - - VkSurfaceCapabilitiesKHR surface_properties; - VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device.get_gpu().get_handle(), - swapchain->get_surface(), - &surface_properties)); - - if (surface_properties.currentExtent.width == 0xFFFFFFFF) - { - return false; - } - - // Only recreate the swapchain if the dimensions have changed; - // handle_surface_changes() is called on VK_SUBOPTIMAL_KHR, - // which might not be due to a surface resize - if (surface_properties.currentExtent.width != surface_extent.width || - surface_properties.currentExtent.height != surface_extent.height || - force_update) - { - // Recreate swapchain - device.wait_idle(); - - update_swapchain(surface_properties.currentExtent, pre_transform); - - surface_extent = surface_properties.currentExtent; - - return true; - } - - return false; -} - -std::shared_ptr RenderContext::begin(vkb::CommandBufferResetMode reset_mode) -{ - assert(prepared && "RenderContext not prepared for rendering, call prepare()"); - - if (!frame_active) - { - begin_frame(); - } - - if (acquired_semaphore == VK_NULL_HANDLE) - { - throw std::runtime_error("Couldn't begin frame"); - } - - const auto &queue = device.get_queue_by_flags(VK_QUEUE_GRAPHICS_BIT, 0); - return get_active_frame().get_command_pool(queue, reset_mode).request_command_buffer(); -} - -void RenderContext::submit(std::shared_ptr command_buffer) -{ - std::vector> command_buffers(1, command_buffer); - submit(command_buffers); -} - -void RenderContext::submit(const std::vector> &command_buffers) -{ - assert(frame_active && "RenderContext is inactive, cannot submit command buffer. Please call begin()"); - - VkSemaphore render_semaphore = VK_NULL_HANDLE; - - if (swapchain) - { - assert(acquired_semaphore && "We do not have acquired_semaphore, it was probably consumed?\n"); - render_semaphore = submit(queue, command_buffers, acquired_semaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); - } - else - { - submit(queue, command_buffers); - } - - end_frame(render_semaphore); -} - -void RenderContext::begin_frame() -{ - // Only handle surface changes if a swapchain exists - if (swapchain) - { - handle_surface_changes(); - } - - assert(!frame_active && "Frame is still active, please call end_frame"); - - assert(active_frame_index < frames.size()); - auto &prev_frame = *frames[active_frame_index]; - - // We will use the acquired semaphore in a different frame context, - // so we need to hold ownership. - acquired_semaphore = prev_frame.get_semaphore_pool().request_semaphore_with_ownership(); - - if (swapchain) - { - auto result = swapchain->acquire_next_image(active_frame_index, acquired_semaphore, VK_NULL_HANDLE); - - if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) - { -#if defined(PLATFORM__MACOS) - // On Apple platforms, force swapchain update on both VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR - // VK_SUBOPTIMAL_KHR may occur on macOS/iOS following changes to swapchain other than extent/size - bool swapchain_updated = handle_surface_changes(true); -#else - bool swapchain_updated = handle_surface_changes(result == VK_ERROR_OUT_OF_DATE_KHR); -#endif - - if (swapchain_updated) - { - // Need to destroy and reallocate acquired_semaphore since it may have already been signaled - vkDestroySemaphore(device.get_handle(), acquired_semaphore, nullptr); - acquired_semaphore = prev_frame.get_semaphore_pool().request_semaphore_with_ownership(); - result = swapchain->acquire_next_image(active_frame_index, acquired_semaphore, VK_NULL_HANDLE); - } - } - - if (result != VK_SUCCESS) - { - prev_frame.reset(); - return; - } - } - - // Now the frame is active again - frame_active = true; - - // Wait on all resource to be freed from the previous render to this frame - wait_frame(); -} - -VkSemaphore RenderContext::submit(const Queue &queue, - const std::vector> &command_buffers, - VkSemaphore wait_semaphore, - VkPipelineStageFlags wait_pipeline_stage) -{ - std::vector cmd_buf_handles(command_buffers.size(), VK_NULL_HANDLE); - std::ranges::transform(command_buffers, cmd_buf_handles.begin(), [](auto const &cmd_buf) { return cmd_buf->get_handle(); }); - - vkb::rendering::RenderFrameC &frame = get_active_frame(); - - VkSemaphore signal_semaphore = frame.get_semaphore_pool().request_semaphore(); - - VkSubmitInfo submit_info{VK_STRUCTURE_TYPE_SUBMIT_INFO}; - - submit_info.commandBufferCount = to_u32(cmd_buf_handles.size()); - submit_info.pCommandBuffers = cmd_buf_handles.data(); - - if (wait_semaphore != VK_NULL_HANDLE) - { - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &wait_semaphore; - submit_info.pWaitDstStageMask = &wait_pipeline_stage; - } - - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &signal_semaphore; - - VkFence fence = frame.get_fence_pool().request_fence(); - - VK_CHECK(queue.submit({submit_info}, fence)); - - return signal_semaphore; -} - -void RenderContext::submit(const Queue &queue, const std::vector> &command_buffers) -{ - std::vector cmd_buf_handles(command_buffers.size(), VK_NULL_HANDLE); - std::ranges::transform(command_buffers, cmd_buf_handles.begin(), [](auto const &cmd_buf) { return cmd_buf->get_handle(); }); - - vkb::rendering::RenderFrameC &frame = get_active_frame(); - - VkSubmitInfo submit_info{VK_STRUCTURE_TYPE_SUBMIT_INFO}; - - submit_info.commandBufferCount = to_u32(cmd_buf_handles.size()); - submit_info.pCommandBuffers = cmd_buf_handles.data(); - - VkFence fence = frame.get_fence_pool().request_fence(); - - VK_CHECK(queue.submit({submit_info}, fence)); -} - -void RenderContext::wait_frame() -{ - vkb::rendering::RenderFrameC &frame = get_active_frame(); - frame.reset(); -} - -void RenderContext::end_frame(VkSemaphore semaphore) -{ - assert(frame_active && "Frame is not active, please call begin_frame"); - - if (swapchain) - { - VkSwapchainKHR vk_swapchain = swapchain->get_handle(); - - VkPresentInfoKHR present_info{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR}; - - present_info.waitSemaphoreCount = 1; - present_info.pWaitSemaphores = &semaphore; - present_info.swapchainCount = 1; - present_info.pSwapchains = &vk_swapchain; - present_info.pImageIndices = &active_frame_index; - - VkDisplayPresentInfoKHR disp_present_info{}; - if (device.get_gpu().is_extension_supported(VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME) && - window.get_display_present_info(&disp_present_info, surface_extent.width, surface_extent.height)) - { - // Add display present info if supported and wanted - present_info.pNext = &disp_present_info; - } - - VkResult result = queue.present(present_info); - - if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) - { - handle_surface_changes(); - } - } - - // Frame is not active anymore - if (acquired_semaphore) - { - release_owned_semaphore(acquired_semaphore); - acquired_semaphore = VK_NULL_HANDLE; - } - frame_active = false; -} - -VkSemaphore RenderContext::consume_acquired_semaphore() -{ - assert(frame_active && "Frame is not active, please call begin_frame"); - auto sem = acquired_semaphore; - acquired_semaphore = VK_NULL_HANDLE; - return sem; -} - -vkb::rendering::RenderFrameC &RenderContext::get_active_frame() -{ - assert(frame_active && "Frame is not active, please call begin_frame"); - assert(active_frame_index < frames.size()); - return *frames[active_frame_index]; -} - -uint32_t RenderContext::get_active_frame_index() -{ - assert(frame_active && "Frame is not active, please call begin_frame"); - return active_frame_index; -} - -vkb::rendering::RenderFrameC &RenderContext::get_last_rendered_frame() -{ - assert(!frame_active && "Frame is still active, please call end_frame"); - assert(active_frame_index < frames.size()); - return *frames[active_frame_index]; -} - -VkSemaphore RenderContext::request_semaphore() -{ - vkb::rendering::RenderFrameC &frame = get_active_frame(); - return frame.get_semaphore_pool().request_semaphore(); -} - -VkSemaphore RenderContext::request_semaphore_with_ownership() -{ - vkb::rendering::RenderFrameC &frame = get_active_frame(); - return frame.get_semaphore_pool().request_semaphore_with_ownership(); -} - -void RenderContext::release_owned_semaphore(VkSemaphore semaphore) -{ - vkb::rendering::RenderFrameC &frame = get_active_frame(); - frame.get_semaphore_pool().release_owned_semaphore(semaphore); -} - -vkb::core::DeviceC &RenderContext::get_device() -{ - return device; -} - -void RenderContext::recreate_swapchain() -{ - device.wait_idle(); - device.get_resource_cache().clear_framebuffers(); - - VkExtent2D swapchain_extent = swapchain->get_extent(); - VkExtent3D extent{swapchain_extent.width, swapchain_extent.height, 1}; - - auto frame_it = frames.begin(); - - for (auto &image_handle : swapchain->get_images()) - { - core::Image swapchain_image{device, image_handle, - extent, - swapchain->get_format(), - swapchain->get_usage()}; - - auto render_target = create_render_target_func(std::move(swapchain_image)); - (*frame_it)->update_render_target(std::move(render_target)); - - ++frame_it; - } -} - -bool RenderContext::has_swapchain() -{ - return swapchain != nullptr; -} - -Swapchain const &RenderContext::get_swapchain() const -{ - assert(swapchain && "Swapchain is not valid"); - return *swapchain; -} - -VkExtent2D const &RenderContext::get_surface_extent() const -{ - return surface_extent; -} - -uint32_t RenderContext::get_active_frame_index() const -{ - return active_frame_index; -} - -std::vector> &RenderContext::get_render_frames() -{ - return frames; -} - -} // namespace vkb diff --git a/framework/rendering/render_context.h b/framework/rendering/render_context.h index 9f648354aa..648a720b05 100644 --- a/framework/rendering/render_context.h +++ b/framework/rendering/render_context.h @@ -17,27 +17,33 @@ #pragma once -#include "common/helpers.h" #include "common/vk_common.h" -#include "core/command_buffer.h" -#include "core/command_pool.h" -#include "core/descriptor_set.h" -#include "core/descriptor_set_layout.h" -#include "core/framebuffer.h" -#include "core/pipeline.h" -#include "core/pipeline_layout.h" -#include "core/queue.h" -#include "core/render_pass.h" -#include "core/shader_module.h" -#include "core/swapchain.h" -#include "rendering/pipeline_state.h" +#include "core/device.h" +#include "core/hpp_swapchain.h" +#include "platform/window.h" +#include "rendering/hpp_render_target.h" #include "rendering/render_frame.h" -#include "rendering/render_target.h" -#include "resource_cache.h" +#include namespace vkb { -class Window; +class Queue; +class RenderTarget; +class Swapchain; + +namespace core +{ +template +class CommandBuffer; + +class HPPQueue; +} // namespace core + +namespace rendering +{ +template +class RenderFrame; +using RenderFrameCpp = RenderFrame; /** * @brief RenderContext acts as a frame manager for the sample, with a lifetime that is the @@ -54,11 +60,29 @@ class Window; * For offscreen rendering (no swapchain), the RenderContext can be given a valid Device, and * a width and height. A single RenderFrame will then be created. */ +template class RenderContext { public: // The format to use for the RenderTargets if a swapchain isn't created - static VkFormat DEFAULT_VK_FORMAT; + static inline vk::Format DEFAULT_VK_FORMAT = vk::Format::eR8G8B8A8Srgb; + + public: + using QueueType = typename std::conditional::type; + using RenderTargetType = typename std::conditional::type; + using SwapchainType = typename std::conditional::type; + + using Extent2DType = typename std::conditional::type; + using FormatType = typename std::conditional::type; + using ImageCompressionFixedRateFlagsType = typename std::conditional::type; + using ImageCompressionFlagsType = typename std::conditional::type; + using ImageUsageFlagBitsType = typename std::conditional::type; + using PipelineStageFlagsType = typename std::conditional::type; + using PresentModeType = typename std::conditional::type; + using SemaphoreType = typename std::conditional::type; + using SurfaceFormatType = typename std::conditional::type; + using SurfaceTransformFlagBitsType = typename std::conditional::type; + using SurfaceType = typename std::conditional::type; /** * @brief Constructor @@ -69,69 +93,102 @@ class RenderContext * @param present_mode_priority_list The order in which the swapchain prioritizes selecting its present mode * @param surface_format_priority_list The order in which the swapchain prioritizes selecting its surface format */ + RenderContext(vkb::core::DeviceCpp &device, + vk::SurfaceKHR surface, + const vkb::Window &window, + vk::PresentModeKHR present_mode = vk::PresentModeKHR::eFifo, + const std::vector &present_mode_priority_list = {vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox}, + const std::vector &surface_format_priority_list = {{vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}, + {vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}}); RenderContext(vkb::core::DeviceC &device, VkSurfaceKHR surface, - const Window &window, + const vkb::Window &window, VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR, const std::vector &present_mode_priority_list = {VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_MAILBOX_KHR}, - const std::vector &surface_format_priority_list = { - {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}); + const std::vector &surface_format_priority_list = {{VK_FORMAT_R8G8B8A8_SRGB, VK_COLORSPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_B8G8R8A8_SRGB, VK_COLORSPACE_SRGB_NONLINEAR_KHR}}); RenderContext(const RenderContext &) = delete; - - RenderContext(RenderContext &&) = delete; + RenderContext(RenderContext &&) = delete; virtual ~RenderContext() = default; RenderContext &operator=(const RenderContext &) = delete; + RenderContext &operator=(RenderContext &&) = delete; - RenderContext &operator=(RenderContext &&) = delete; + public: + /** + * @brief Prepares the next available frame for rendering + * @param reset_mode How to reset the command buffer + * @returns A valid command buffer to record commands to be submitted + * Also ensures that there is an active frame if there is no existing active frame already + */ + std::shared_ptr> begin(vkb::CommandBufferResetMode reset_mode = vkb::CommandBufferResetMode::ResetPool); /** - * @brief Prepares the RenderFrames for rendering - * @param thread_count The number of threads in the application, necessary to allocate this many resource pools for each RenderFrame - * @param create_render_target_func A function delegate, used to create a RenderTarget + * @brief begin_frame */ - void prepare(size_t thread_count = 1, RenderTarget::CreateFunc create_render_target_func = RenderTarget::DEFAULT_CREATE_FUNC); + void begin_frame(); /** - * @brief Updates the swapchains extent, if a swapchain exists - * @param extent The width and height of the new swapchain images + * @brief Returns the WSI acquire semaphore. Only to be used in very special circumstances. + * @return The WSI acquire semaphore. */ - void update_swapchain(const VkExtent2D &extent); + SemaphoreType consume_acquired_semaphore(); + + void end_frame(SemaphoreType semaphore); /** - * @brief Updates the swapchains image count, if a swapchain exists - * @param image_count The amount of images in the new swapchain + * @brief An error should be raised if the frame is not active. + * A frame is active after @ref begin_frame has been called. + * @return The current active frame */ - void update_swapchain(const uint32_t image_count); + vkb::rendering::RenderFrame &get_active_frame(); /** - * @brief Updates the swapchains image usage, if a swapchain exists - * @param image_usage_flags The usage flags the new swapchain images will have + * @brief An error should be raised if the frame is not active. + * A frame is active after @ref begin_frame has been called. + * @return The current active frame index */ - void update_swapchain(const std::set &image_usage_flags); + uint32_t get_active_frame_index() const; + + vkb::core::Device &get_device(); /** - * @brief Updates the swapchains extent and surface transform, if a swapchain exists - * @param extent The width and height of the new swapchain images - * @param transform The surface transform flags + * @brief Returns the format that the RenderTargets are created with within the RenderContext */ - void update_swapchain(const VkExtent2D &extent, const VkSurfaceTransformFlagBitsKHR transform); + FormatType get_format() const; /** - * @brief Updates the swapchain's compression settings, if a swapchain exists - * @param compression The compression to use for swapchain images (default, fixed-rate, none) - * @param compression_fixed_rate The rate to use, if compression is fixed-rate + * @brief An error should be raised if a frame is active. + * A frame is active after @ref begin_frame has been called. + * @return The previous frame */ - void update_swapchain(const VkImageCompressionFlagsEXT compression, const VkImageCompressionFixedRateFlagsEXT compression_fixed_rate); + vkb::rendering::RenderFrame &get_last_rendered_frame(); + + std::vector>> &get_render_frames(); + + Extent2DType const &get_surface_extent() const; + + SwapchainType const &get_swapchain() const; + + /** + * @brief Handles surface changes, only applicable if the render_context makes use of a swapchain + */ + virtual bool handle_surface_changes(bool force_update = false); /** * @returns True if a valid swapchain exists in the RenderContext */ bool has_swapchain(); + /** + * @brief Prepares the RenderFrames for rendering + * @param thread_count The number of threads in the application, necessary to allocate this many resource pools for each RenderFrame + * @param create_render_target_func A function delegate, used to create a RenderTarget + */ + void prepare(size_t thread_count = 1, typename RenderTargetType::CreateFunc create_render_target_func = RenderTargetType::DEFAULT_CREATE_FUNC); + /** * @brief Recreates the RenderFrames, called after every update */ @@ -142,131 +199,811 @@ class RenderContext */ void recreate_swapchain(); - /** - * @brief Prepares the next available frame for rendering - * @param reset_mode How to reset the command buffer - * @returns A valid command buffer to record commands to be submitted - * Also ensures that there is an active frame if there is no existing active frame already - */ - std::shared_ptr begin(vkb::CommandBufferResetMode reset_mode = vkb::CommandBufferResetMode::ResetPool); + void release_owned_semaphore(SemaphoreType semaphore); + SemaphoreType request_semaphore(); + SemaphoreType request_semaphore_with_ownership(); /** * @brief Submits the command buffer to the right queue * @param command_buffer A command buffer containing recorded commands */ - void submit(std::shared_ptr command_buffer); + void submit(std::shared_ptr> command_buffer); /** * @brief Submits multiple command buffers to the right queue * @param command_buffers Command buffers containing recorded commands */ - void submit(const std::vector> &command_buffers); + void submit(const std::vector>> &command_buffers); + + SemaphoreType submit(const QueueType &queue, + const std::vector>> &command_buffers, + SemaphoreType wait_semaphore, + PipelineStageFlagsType wait_pipeline_stage); /** - * @brief begin_frame + * @brief Submits a command buffer related to a frame to a queue */ - void begin_frame(); - - VkSemaphore submit(const Queue &queue, - const std::vector> &command_buffers, - VkSemaphore wait_semaphore, - VkPipelineStageFlags wait_pipeline_stage); + void submit(const QueueType &queue, const std::vector>> &command_buffers); /** - * @brief Submits a command buffer related to a frame to a queue + * @brief Updates the swapchains extent, if a swapchain exists + * @param extent The width and height of the new swapchain images */ - void submit(const Queue &queue, const std::vector> &command_buffers); + void update_swapchain(const Extent2DType &extent); /** - * @brief Waits a frame to finish its rendering + * @brief Updates the swapchains image count, if a swapchain exists + * @param image_count The amount of images in the new swapchain */ - virtual void wait_frame(); + void update_swapchain(const uint32_t image_count); - void end_frame(VkSemaphore semaphore); + /** + * @brief Updates the swapchains image usage, if a swapchain exists + * @param image_usage_flags The usage flags the new swapchain images will have + */ + void update_swapchain(const std::set &image_usage_flags); /** - * @brief An error should be raised if the frame is not active. - * A frame is active after @ref begin_frame has been called. - * @return The current active frame + * @brief Updates the swapchains extent and surface transform, if a swapchain exists + * @param extent The width and height of the new swapchain images + * @param transform The surface transform flags */ - vkb::rendering::RenderFrameC &get_active_frame(); + void update_swapchain(const Extent2DType &extent, const SurfaceTransformFlagBitsType transform); /** - * @brief An error should be raised if the frame is not active. - * A frame is active after @ref begin_frame has been called. - * @return The current active frame index + * @brief Updates the swapchain's compression settings, if a swapchain exists + * @param compression The compression to use for swapchain images (default, fixed-rate, none) + * @param compression_fixed_rate The rate to use, if compression is fixed-rate */ - uint32_t get_active_frame_index(); + void update_swapchain(const ImageCompressionFlagsType compression, const ImageCompressionFixedRateFlagsType compression_fixed_rate); /** - * @brief An error should be raised if a frame is active. - * A frame is active after @ref begin_frame has been called. - * @return The previous frame + * @brief Waits a frame to finish its rendering */ - vkb::rendering::RenderFrameC &get_last_rendered_frame(); + virtual void wait_frame(); - VkSemaphore request_semaphore(); - VkSemaphore request_semaphore_with_ownership(); - void release_owned_semaphore(VkSemaphore semaphore); + private: + void initialize_swapchain(vk::SurfaceKHR surface, vk::PresentModeKHR present_movde, std::vector const &present_mode_priority_list, std::vector const &surface_format_priority_list); + void submit_impl(const std::vector> &command_buffers); + vk::Semaphore submit_impl(vkb::core::HPPQueue const &queue, + std::vector> const &command_buffers, + vk::Semaphore wait_semaphore, + vk::PipelineStageFlags wait_pipeline_stage); + void submit_impl(vkb::core::HPPQueue const &queue, std::vector> const &command_buffers); + void update_swapchain_impl(vk::Extent2D const &extent, vk::SurfaceTransformFlagBitsKHR transform); - vkb::core::DeviceC &get_device(); + private: + vk::Semaphore acquired_semaphore; + uint32_t active_frame_index = 0; // Current active frame index + HPPRenderTarget::CreateFunc create_render_target_func = HPPRenderTarget::DEFAULT_CREATE_FUNC; + vkb::core::DeviceCpp &device; + bool frame_active = false; // Whether a frame is active or not + std::vector> frames; + vk::SurfaceTransformFlagBitsKHR pre_transform = vk::SurfaceTransformFlagBitsKHR::eIdentity; + bool prepared = false; + const vkb::core::HPPQueue &queue; // If swapchain exists, then this will be a present supported queue, else a graphics queue + vk::Extent2D surface_extent; + std::unique_ptr swapchain; + vkb::core::HPPSwapchainProperties swapchain_properties; + size_t thread_count = 1; + const vkb::Window &window; +}; - /** - * @brief Returns the format that the RenderTargets are created with within the RenderContext - */ - VkFormat get_format() const; +using RenderContextC = RenderContext; +using RenderContextCpp = RenderContext; + +// Member function definitions + +template <> +inline RenderContextCpp::RenderContext(vkb::core::DeviceCpp &device, + vk::SurfaceKHR surface, + const vkb::Window &window, + vk::PresentModeKHR present_mode, + std::vector const &present_mode_priority_list, + std::vector const &surface_format_priority_list) : + device(device), + window(window), + queue(device.get_queue_by_flags(vk::QueueFlagBits::eGraphics, 0)), + surface_extent{window.get_extent().width, window.get_extent().height} +{ + initialize_swapchain(surface, present_mode, present_mode_priority_list, surface_format_priority_list); +} + +template <> +inline RenderContextC::RenderContext(vkb::core::DeviceC &device, + VkSurfaceKHR surface, + const Window &window, + VkPresentModeKHR present_mode, + const std::vector &present_mode_priority_list, + const std::vector &surface_format_priority_list) : + device(reinterpret_cast(device)), + window(window), + queue(reinterpret_cast(device.get_queue_by_flags(VK_QUEUE_GRAPHICS_BIT, 0))), + surface_extent{window.get_extent().width, window.get_extent().height} +{ + initialize_swapchain(static_cast(surface), + static_cast(present_mode), + reinterpret_cast const &>(present_mode_priority_list), + reinterpret_cast const &>(surface_format_priority_list)); +} + +template +inline void RenderContext::initialize_swapchain(vk::SurfaceKHR surface, + vk::PresentModeKHR present_mode, + std::vector const &present_mode_priority_list, + std::vector const &surface_format_priority_list) +{ + if (surface) + { + vk::SurfaceCapabilitiesKHR surface_properties = device.get_gpu().get_handle().getSurfaceCapabilitiesKHR(surface); + + if (surface_properties.currentExtent.width == 0xFFFFFFFF) + { + swapchain = + std::make_unique(device, surface, present_mode, present_mode_priority_list, surface_format_priority_list, surface_extent); + } + else + { + swapchain = std::make_unique(device, surface, present_mode, present_mode_priority_list, surface_format_priority_list); + } + } +} + +template +inline std::shared_ptr> RenderContext::begin(vkb::CommandBufferResetMode reset_mode) +{ + assert(prepared && "HPPRenderContext not prepared for rendering, call prepare()"); + + if (!frame_active) + { + begin_frame(); + } + + if (!acquired_semaphore) + { + throw std::runtime_error("Couldn't begin frame"); + } + + const auto &queue = device.get_queue_by_flags(vk::QueueFlagBits::eGraphics, 0); + if constexpr (bindingType == vkb::BindingType::Cpp) + { + return get_active_frame().get_command_pool(queue, reset_mode).request_command_buffer(); + } + else + { + return get_active_frame().get_command_pool(reinterpret_cast(queue), reset_mode).request_command_buffer(); + } +} + +template +inline void RenderContext::begin_frame() +{ + // Only handle surface changes if a swapchain exists + if (swapchain) + { + handle_surface_changes(); + } + + assert(!frame_active && "Frame is still active, please call end_frame"); + + auto &prev_frame = *frames[active_frame_index]; + + // We will use the acquired semaphore in a different frame context, + // so we need to hold ownership. + acquired_semaphore = prev_frame.get_semaphore_pool().request_semaphore_with_ownership(); + + if (swapchain) + { + vk::Result result; + try + { + std::tie(result, active_frame_index) = swapchain->acquire_next_image(acquired_semaphore); + } + catch (vk::OutOfDateKHRError & /*err*/) + { + result = vk::Result::eErrorOutOfDateKHR; + } + + if (result == vk::Result::eSuboptimalKHR || result == vk::Result::eErrorOutOfDateKHR) + { +#if defined(PLATFORM__MACOS) + // On Apple platforms, force swapchain update on both eSuboptimalKHR and eErrorOutOfDateKHR + // eSuboptimalKHR may occur on macOS/iOS following changes to swapchain other than extent/size + bool swapchain_updated = handle_surface_changes(true); +#else + bool swapchain_updated = handle_surface_changes(result == vk::Result::eErrorOutOfDateKHR); +#endif + + if (swapchain_updated) + { + // Need to destroy and reallocate acquired_semaphore since it may have already been signaled + device.get_handle().destroySemaphore(acquired_semaphore); + acquired_semaphore = prev_frame.get_semaphore_pool().request_semaphore_with_ownership(); + std::tie(result, active_frame_index) = swapchain->acquire_next_image(acquired_semaphore); + } + } + + if (result != vk::Result::eSuccess) + { + prev_frame.reset(); + return; + } + } + + // Now the frame is active again + frame_active = true; + + // Wait on all resource to be freed from the previous render to this frame + wait_frame(); +} + +template +inline typename RenderContext::SemaphoreType RenderContext::consume_acquired_semaphore() +{ + assert(frame_active && "Frame is not active, please call begin_frame"); + return std::exchange(acquired_semaphore, nullptr); +} + +template +inline void RenderContext::end_frame(SemaphoreType semaphore) +{ + assert(frame_active && "Frame is not active, please call begin_frame"); + + if (swapchain) + { + vk::SwapchainKHR vk_swapchain = swapchain->get_handle(); + vk::PresentInfoKHR present_info{.waitSemaphoreCount = 1, .swapchainCount = 1, .pSwapchains = &vk_swapchain, .pImageIndices = &active_frame_index}; + if constexpr (bindingType == BindingType::Cpp) + { + present_info.pWaitSemaphores = &semaphore; + } + else + { + present_info.pWaitSemaphores = reinterpret_cast(&semaphore); + } + + vk::DisplayPresentInfoKHR disp_present_info; + if (device.get_gpu().is_extension_supported(VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME) && + window.get_display_present_info(reinterpret_cast(&disp_present_info), surface_extent.width, surface_extent.height)) + { + // Add display present info if supported and wanted + present_info.pNext = &disp_present_info; + } + + vk::Result result; + try + { + result = queue.present(present_info); + } + catch (vk::OutOfDateKHRError & /*err*/) + { + result = vk::Result::eErrorOutOfDateKHR; + } + + if (result == vk::Result::eSuboptimalKHR || result == vk::Result::eErrorOutOfDateKHR) + { + handle_surface_changes(); + } + } + + // Frame is not active anymore + if (acquired_semaphore) + { + release_owned_semaphore(acquired_semaphore); + acquired_semaphore = nullptr; + } + frame_active = false; +} + +template +inline vkb::rendering::RenderFrame &RenderContext::get_active_frame() +{ + assert(frame_active && "Frame is not active, please call begin_frame"); + if constexpr (bindingType == BindingType::Cpp) + { + return *frames[active_frame_index]; + } + else + { + return reinterpret_cast(*frames[active_frame_index]); + } +} + +template +inline uint32_t RenderContext::get_active_frame_index() const +{ + assert(frame_active && "Frame is not active, please call begin_frame"); + return active_frame_index; +} + +template +inline vkb::core::Device &RenderContext::get_device() +{ + if constexpr (bindingType == BindingType::Cpp) + { + return device; + } + else + { + return reinterpret_cast(device); + } +} + +template +inline typename RenderContext::FormatType RenderContext::get_format() const +{ + vk::Format format = swapchain ? swapchain->get_format() : DEFAULT_VK_FORMAT; + if constexpr (bindingType == BindingType::Cpp) + { + return format; + } + else + { + return static_cast(format); + } +} + +template +inline vkb::rendering::RenderFrame &RenderContext::get_last_rendered_frame() +{ + assert(!frame_active && "Frame is still active, please call end_frame"); + if constexpr (bindingType == BindingType::Cpp) + { + return *frames[active_frame_index]; + } + else + { + return reinterpret_cast(*frames[active_frame_index]); + } +} + +template +inline std::vector>> &RenderContext::get_render_frames() +{ + if constexpr (bindingType == BindingType::Cpp) + { + return frames; + } + else + { + return reinterpret_cast> &>(frames); + } +} + +template +inline typename RenderContext::Extent2DType const &RenderContext::get_surface_extent() const +{ + if constexpr (bindingType == BindingType::Cpp) + { + return surface_extent; + } + else + { + return *reinterpret_cast(&surface_extent); + } +} + +template +inline typename RenderContext::SwapchainType const &RenderContext::get_swapchain() const +{ + assert(swapchain && "Swapchain is not valid"); + if constexpr (bindingType == vkb::BindingType::Cpp) + { + return *swapchain; + } + else + { + return reinterpret_cast(*swapchain); + } +} + +template +inline bool RenderContext::handle_surface_changes(bool force_update) +{ + if (!swapchain) + { + LOGW("Can't handle surface changes. No swapchain, offscreen rendering detected, skipping."); + return false; + } - Swapchain const &get_swapchain() const; + vk::SurfaceCapabilitiesKHR surface_properties = device.get_gpu().get_handle().getSurfaceCapabilitiesKHR(swapchain->get_surface()); - VkExtent2D const &get_surface_extent() const; + if (surface_properties.currentExtent.width == 0xFFFFFFFF) + { + return false; + } - uint32_t get_active_frame_index() const; + // Only recreate the swapchain if the dimensions have changed; + // handle_surface_changes() is called on VK_SUBOPTIMAL_KHR, + // which might not be due to a surface resize + if (surface_properties.currentExtent.width != surface_extent.width || surface_properties.currentExtent.height != surface_extent.height || force_update) + { + // Recreate swapchain + device.get_handle().waitIdle(); - std::vector> &get_render_frames(); + update_swapchain_impl(surface_properties.currentExtent, pre_transform); - /** - * @brief Handles surface changes, only applicable if the render_context makes use of a swapchain - */ - virtual bool handle_surface_changes(bool force_update = false); + surface_extent = surface_properties.currentExtent; - /** - * @brief Returns the WSI acquire semaphore. Only to be used in very special circumstances. - * @return The WSI acquire semaphore. - */ - VkSemaphore consume_acquired_semaphore(); + return true; + } - protected: - VkExtent2D surface_extent; + return false; +} - private: - vkb::core::DeviceC &device; +template +inline bool RenderContext::has_swapchain() +{ + return swapchain != nullptr; +} + +template +inline void RenderContext::prepare(size_t thread_count, typename RenderTargetType::CreateFunc create_render_target_func_) +{ + if constexpr (bindingType == vkb::BindingType::Cpp) + { + create_render_target_func = create_render_target_func_; + } + else + { + create_render_target_func = *reinterpret_cast(&create_render_target_func_); + } + + device.get_handle().waitIdle(); + + if (swapchain) + { + surface_extent = swapchain->get_extent(); + + vk::Extent3D extent{surface_extent.width, surface_extent.height, 1}; + + for (auto &image_handle : swapchain->get_images()) + { + vkb::core::HPPImage swapchain_image{device, image_handle, extent, swapchain->get_format(), swapchain->get_usage()}; + auto render_target = create_render_target_func(std::move(swapchain_image)); + frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); + } + } + else + { + // Otherwise, create a single RenderFrame + swapchain = nullptr; + + auto color_image = vkb::core::HPPImage{device, + vk::Extent3D{surface_extent.width, surface_extent.height, 1}, + DEFAULT_VK_FORMAT, // We can use any format here that we like + vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc, + VMA_MEMORY_USAGE_GPU_ONLY}; + + std::unique_ptr render_target = create_render_target_func(std::move(color_image)); + frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); + } + + this->thread_count = thread_count; + this->prepared = true; +} + +template +inline void RenderContext::recreate() +{ + LOGI("Recreated swapchain"); + + vk::Extent2D swapchain_extent = swapchain->get_extent(); + vk::Extent3D extent{swapchain_extent.width, swapchain_extent.height, 1}; - const Window &window; + auto frame_it = frames.begin(); - /// If swapchain exists, then this will be a present supported queue, else a graphics queue - const Queue &queue; + for (auto &image_handle : swapchain->get_images()) + { + vkb::core::HPPImage swapchain_image{device, image_handle, extent, swapchain->get_format(), swapchain->get_usage()}; - std::unique_ptr swapchain; + auto render_target = create_render_target_func(std::move(swapchain_image)); - SwapchainProperties swapchain_properties; + if (frame_it != frames.end()) + { + (*frame_it)->update_render_target(std::move(render_target)); + } + else + { + // Create a new frame if the new swapchain has more images than current frames + frames.emplace_back(std::make_unique(device, std::move(render_target), thread_count)); + } - std::vector> frames; + ++frame_it; + } - VkSemaphore acquired_semaphore; + device.get_resource_cache().clear_framebuffers(); +} - bool prepared{false}; +template +inline void RenderContext::recreate_swapchain() +{ + device.get_handle().waitIdle(); + device.get_resource_cache().clear_framebuffers(); - /// Current active frame index - uint32_t active_frame_index{0}; + vk::Extent2D swapchain_extent = swapchain->get_extent(); + vk::Extent3D extent{swapchain_extent.width, swapchain_extent.height, 1}; - /// Whether a frame is active or not - bool frame_active{false}; + auto frame_it = frames.begin(); - RenderTarget::CreateFunc create_render_target_func = RenderTarget::DEFAULT_CREATE_FUNC; + for (auto &image_handle : swapchain->get_images()) + { + vkb::core::HPPImage swapchain_image{device, image_handle, extent, swapchain->get_format(), swapchain->get_usage()}; + auto render_target = create_render_target_func(std::move(swapchain_image)); + (*frame_it)->update_render_target(std::move(render_target)); - VkSurfaceTransformFlagBitsKHR pre_transform{VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR}; + ++frame_it; + } +} - size_t thread_count{1}; -}; +template +inline void RenderContext::release_owned_semaphore(SemaphoreType semaphore) +{ + get_active_frame().get_semaphore_pool().release_owned_semaphore(semaphore); +} + +template +inline typename RenderContext::SemaphoreType RenderContext::request_semaphore() +{ + return get_active_frame().get_semaphore_pool().request_semaphore(); +} + +template +inline typename RenderContext::SemaphoreType RenderContext::request_semaphore_with_ownership() +{ + return get_active_frame().get_semaphore_pool().request_semaphore_with_ownership(); +} + +template +inline void RenderContext::submit(std::shared_ptr> command_buffer) +{ + std::vector>> command_buffers(1, command_buffer); + submit(command_buffers); +} + +template +inline void RenderContext::submit(const std::vector>> &command_buffers) +{ + if constexpr (bindingType == vkb::BindingType::Cpp) + { + submit_impl(command_buffers); + } + else + { + submit_impl(reinterpret_cast> const &>(command_buffers)); + } +} + +template +inline void RenderContext::submit_impl(std::vector> const &command_buffers) +{ + assert(frame_active && "RenderContext is inactive, cannot submit command buffer. Please call begin()"); + + vk::Semaphore render_semaphore = nullptr; + + if (swapchain) + { + assert(acquired_semaphore && "We do not have acquired_semaphore, it was probably consumed?\n"); + render_semaphore = submit_impl(queue, command_buffers, acquired_semaphore, vk::PipelineStageFlagBits::eColorAttachmentOutput); + } + else + { + submit_impl(queue, command_buffers); + } + + end_frame(render_semaphore); +} + +template +inline typename RenderContext::SemaphoreType + RenderContext::submit(const QueueType &queue, + const std::vector>> &command_buffers, + SemaphoreType wait_semaphore, + PipelineStageFlagsType wait_pipeline_stage) +{ + if constexpr (bindingType == vkb::BindingType::Cpp) + { + return submit_impl(queue, command_buffers, wait_semaphore, wait_pipeline_stage); + } + else + { + return static_cast(submit_impl(reinterpret_cast(queue), + reinterpret_cast> const &>(command_buffers), + static_cast(wait_semaphore), + static_cast(wait_pipeline_stage))); + } +} + +template +inline vk::Semaphore RenderContext::submit_impl(const vkb::core::HPPQueue &queue, + const std::vector> &command_buffers, + vk::Semaphore wait_semaphore, + vk::PipelineStageFlags wait_pipeline_stage) +{ + std::vector cmd_buf_handles(command_buffers.size(), nullptr); + std::ranges::transform(command_buffers, cmd_buf_handles.begin(), [](auto const &cmd_buf) { return cmd_buf->get_handle(); }); + + vkb::rendering::RenderFrameCpp &frame = *frames[active_frame_index]; + + vk::Semaphore signal_semaphore = frame.get_semaphore_pool().request_semaphore(); + + vk::SubmitInfo submit_info{.commandBufferCount = to_u32(cmd_buf_handles.size()), + .pCommandBuffers = cmd_buf_handles.data(), + .signalSemaphoreCount = 1, + .pSignalSemaphores = &signal_semaphore}; + + if (wait_semaphore != nullptr) + { + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &wait_semaphore; + submit_info.pWaitDstStageMask = &wait_pipeline_stage; + } + + vk::Fence fence = frame.get_fence_pool().request_fence(); + + queue.get_handle().submit(submit_info, fence); + + return signal_semaphore; +} + +template +inline void RenderContext::submit(const QueueType &queue, + const std::vector>> &command_buffers) +{ + if constexpr (bindingType == vkb::BindingType::Cpp) + { + submit_impl(queue, command_buffers); + } + else + { + submit_impl(reinterpret_cast(queue), + reinterpret_cast> const &>(command_buffers)); + } +} + +template +inline void RenderContext::submit_impl(vkb::core::HPPQueue const &queue, + const std::vector> &command_buffers) +{ + std::vector cmd_buf_handles(command_buffers.size(), nullptr); + std::ranges::transform(command_buffers, cmd_buf_handles.begin(), [](auto const &cmd_buf) { return cmd_buf->get_handle(); }); + + vk::SubmitInfo submit_info{.commandBufferCount = to_u32(cmd_buf_handles.size()), .pCommandBuffers = cmd_buf_handles.data()}; + + vk::Fence fence = frames[active_frame_index]->get_fence_pool().request_fence(); + + queue.get_handle().submit(submit_info, fence); +} + +template +inline void RenderContext::update_swapchain(const Extent2DType &extent) +{ + if (!swapchain) + { + LOGW("Can't update the swapchains extent. No swapchain, offscreen rendering detected, skipping."); + return; + } + + device.get_resource_cache().clear_framebuffers(); + + swapchain = std::make_unique(*swapchain, extent); + + recreate(); +} + +template +inline void RenderContext::update_swapchain(const uint32_t image_count) +{ + if (!swapchain) + { + LOGW("Can't update the swapchains image count. No swapchain, offscreen rendering detected, skipping."); + return; + } + + device.get_resource_cache().clear_framebuffers(); + + device.get_handle().waitIdle(); + + swapchain = std::make_unique(*swapchain, image_count); + + recreate(); +} + +template +inline void RenderContext::update_swapchain(const std::set &image_usage_flags) +{ + if (!swapchain) + { + LOGW("Can't update the swapchains image usage. No swapchain, offscreen rendering detected, skipping."); + return; + } + + device.get_resource_cache().clear_framebuffers(); + + if constexpr (bindingType == vkb::BindingType::Cpp) + { + swapchain = std::make_unique(*swapchain, image_usage_flags); + } + else + { + swapchain = std::make_unique(reinterpret_cast(*swapchain), + reinterpret_cast const &>(image_usage_flags)); + } + + recreate(); +} + +template +inline void RenderContext::update_swapchain(const Extent2DType &extent, const SurfaceTransformFlagBitsType transform) +{ + if constexpr (bindingType == BindingType::Cpp) + { + update_swapchain_impl(extent, transform); + } + else + { + update_swapchain_impl(reinterpret_cast(extent), static_cast(transform)); + } +} + +template +inline void RenderContext::update_swapchain_impl(vk::Extent2D const &extent, vk::SurfaceTransformFlagBitsKHR transform) +{ + if (!swapchain) + { + LOGW("Can't update the swapchains extent and surface transform. No swapchain, offscreen rendering detected, skipping."); + return; + } + + device.get_resource_cache().clear_framebuffers(); + + auto width = extent.width; + auto height = extent.height; + if (transform == vk::SurfaceTransformFlagBitsKHR::eRotate90 || transform == vk::SurfaceTransformFlagBitsKHR::eRotate270) + { + // Pre-rotation: always use native orientation i.e. if rotated, use width and height of identity transform + std::swap(width, height); + } + + swapchain = std::make_unique(*swapchain, vk::Extent2D{width, height}, transform); + + // Save the preTransform attribute for future rotations + pre_transform = transform; + + recreate(); +} + +template +inline void RenderContext::update_swapchain(const ImageCompressionFlagsType compression, + const ImageCompressionFixedRateFlagsType compression_fixed_rate) +{ + if (!swapchain) + { + LOGW("Can't update the swapchains compression. No swapchain, offscreen rendering detected, skipping."); + return; + } + + device.get_resource_cache().clear_framebuffers(); + + if constexpr (bindingType == vkb::BindingType::Cpp) + { + swapchain = std::make_unique(*swapchain, compression, compression_fixed_rate); + } + else + { + swapchain = std::make_unique(reinterpret_cast(*swapchain), + static_cast(compression), + static_cast(compression_fixed_rate)); + } + + recreate(); +} + +template +inline void RenderContext::wait_frame() +{ + get_active_frame().reset(); +} +} // namespace rendering } // namespace vkb diff --git a/framework/rendering/render_pipeline.cpp b/framework/rendering/render_pipeline.cpp index 1a97d8cf6b..01fdb5ec5c 100644 --- a/framework/rendering/render_pipeline.cpp +++ b/framework/rendering/render_pipeline.cpp @@ -16,7 +16,7 @@ */ #include "render_pipeline.h" - +#include "core/command_buffer.h" #include "scene_graph/components/camera.h" #include "scene_graph/components/image.h" #include "scene_graph/components/material.h" diff --git a/framework/rendering/subpass.h b/framework/rendering/subpass.h index 095dc4d70f..3aae1d01d0 100644 --- a/framework/rendering/subpass.h +++ b/framework/rendering/subpass.h @@ -22,13 +22,13 @@ #include "rendering/hpp_pipeline_state.h" #include "rendering/hpp_render_target.h" #include "rendering/pipeline_state.h" +#include "rendering/render_context.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; @@ -40,8 +40,6 @@ class CommandBuffer; namespace rendering { -class HPPRenderContext; - struct alignas(16) Light { glm::vec4 position; // position.w represents type of light @@ -87,11 +85,10 @@ class Subpass using DepthStencilStateType = typename std::conditional::type; - using RenderContextType = typename std::conditional::type; - using RenderTargetType = typename std::conditional::type; + using RenderTargetType = typename std::conditional::type; public: - Subpass(RenderContextType &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader); + Subpass(vkb::rendering::RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader); Subpass(const Subpass &) = delete; Subpass(Subpass &&) = default; @@ -131,7 +128,7 @@ class Subpass const std::vector &get_input_attachments() const; LightingState &get_lighting_state(); const std::vector &get_output_attachments() const; - RenderContextType &get_render_context(); + RenderContext &get_render_context(); std::unordered_map const &get_resource_mode_map() const; SampleCountflagBitsType get_sample_count() const; const ShaderSource &get_vertex_shader() const; @@ -186,7 +183,7 @@ class Subpass /// Default to swapchain output attachment std::vector output_attachments = {0}; - vkb::rendering::HPPRenderContext &render_context; + vkb::rendering::RenderContextCpp &render_context; // A map of shader resource names and the mode of constant data std::unordered_map resource_mode_map; @@ -197,15 +194,7 @@ class Subpass 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. @@ -216,8 +205,10 @@ inline glm::mat4 vulkan_style_projection(const glm::mat4 &proj) } template -inline Subpass::Subpass(RenderContextType &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source) : - render_context{reinterpret_cast(render_context)}, +inline Subpass::Subpass(vkb::rendering::RenderContext &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)} { @@ -249,7 +240,7 @@ inline const std::vector &Subpass::get_output_attachments } template -inline typename vkb::rendering::Subpass::RenderContextType &Subpass::get_render_context() +inline typename vkb::rendering::RenderContext &Subpass::get_render_context() { if constexpr (bindingType == vkb::BindingType::Cpp) { @@ -257,7 +248,7 @@ inline typename vkb::rendering::Subpass::RenderContextType &Subpass } else { - return reinterpret_cast(render_context); + return reinterpret_cast(render_context); } } diff --git a/framework/rendering/subpasses/forward_subpass.cpp b/framework/rendering/subpasses/forward_subpass.cpp index 2066a97738..6c3848e7e8 100644 --- a/framework/rendering/subpasses/forward_subpass.cpp +++ b/framework/rendering/subpasses/forward_subpass.cpp @@ -19,7 +19,9 @@ #include "common/utils.h" #include "common/vk_common.h" +#include "core/command_buffer.h" #include "rendering/render_context.h" +#include "resource_cache.h" #include "scene_graph/components/camera.h" #include "scene_graph/components/image.h" #include "scene_graph/components/material.h" @@ -32,7 +34,8 @@ namespace vkb { -ForwardSubpass::ForwardSubpass(RenderContext &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source, sg::Scene &scene_, sg::Camera &camera) : +ForwardSubpass::ForwardSubpass( + vkb::rendering::RenderContextC &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source, sg::Scene &scene_, sg::Camera &camera) : GeometrySubpass{render_context, std::move(vertex_source), std::move(fragment_source), scene_, camera} { } diff --git a/framework/rendering/subpasses/forward_subpass.h b/framework/rendering/subpasses/forward_subpass.h index d9eae6aa5d..c41a55a1dd 100644 --- a/framework/rendering/subpasses/forward_subpass.h +++ b/framework/rendering/subpasses/forward_subpass.h @@ -55,7 +55,8 @@ class ForwardSubpass : public GeometrySubpass * @param scene Scene to render on this subpass * @param camera Camera used to look at the scene */ - ForwardSubpass(RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Scene &scene, sg::Camera &camera); + ForwardSubpass( + vkb::rendering::RenderContextC &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Scene &scene, sg::Camera &camera); virtual ~ForwardSubpass() = default; diff --git a/framework/rendering/subpasses/geometry_subpass.cpp b/framework/rendering/subpasses/geometry_subpass.cpp index 3579dbf24d..3531c4e9b1 100644 --- a/framework/rendering/subpasses/geometry_subpass.cpp +++ b/framework/rendering/subpasses/geometry_subpass.cpp @@ -18,7 +18,9 @@ #include "rendering/subpasses/geometry_subpass.h" #include "common/utils.h" #include "common/vk_common.h" +#include "core/command_buffer.h" #include "rendering/render_context.h" +#include "resource_cache.h" #include "scene_graph/components/camera.h" #include "scene_graph/components/image.h" #include "scene_graph/components/material.h" @@ -30,7 +32,8 @@ namespace vkb { -GeometrySubpass::GeometrySubpass(RenderContext &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source, sg::Scene &scene_, sg::Camera &camera) : +GeometrySubpass::GeometrySubpass( + vkb::rendering::RenderContextC &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source, sg::Scene &scene_, sg::Camera &camera) : Subpass{render_context, std::move(vertex_source), std::move(fragment_source)}, meshes{scene_.get_components()}, camera{camera}, diff --git a/framework/rendering/subpasses/geometry_subpass.h b/framework/rendering/subpasses/geometry_subpass.h index db2163a71f..80664143be 100644 --- a/framework/rendering/subpasses/geometry_subpass.h +++ b/framework/rendering/subpasses/geometry_subpass.h @@ -79,7 +79,8 @@ class GeometrySubpass : public vkb::rendering::SubpassC * @param scene Scene to render on this subpass * @param camera Camera used to look at the scene */ - GeometrySubpass(RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Scene &scene, sg::Camera &camera); + GeometrySubpass( + vkb::rendering::RenderContextC &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Scene &scene, sg::Camera &camera); virtual ~GeometrySubpass() = default; diff --git a/framework/rendering/subpasses/hpp_forward_subpass.h b/framework/rendering/subpasses/hpp_forward_subpass.h index 99ce75fc3a..94c6ef2dc1 100644 --- a/framework/rendering/subpasses/hpp_forward_subpass.h +++ b/framework/rendering/subpasses/hpp_forward_subpass.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -19,8 +19,6 @@ #include "rendering/subpasses/forward_subpass.h" -#include - namespace vkb { namespace rendering @@ -35,12 +33,12 @@ namespace subpasses class HPPForwardSubpass : public vkb::ForwardSubpass { public: - HPPForwardSubpass(vkb::rendering::HPPRenderContext &render_context, + HPPForwardSubpass(vkb::rendering::RenderContextCpp &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::scene_graph::HPPScene &scene, vkb::sg::Camera &camera) : - vkb::ForwardSubpass(reinterpret_cast(render_context), + vkb::ForwardSubpass(reinterpret_cast(render_context), std::forward(vertex_shader), std::forward(fragment_shader), reinterpret_cast(scene), diff --git a/framework/rendering/subpasses/lighting_subpass.cpp b/framework/rendering/subpasses/lighting_subpass.cpp index ba58bad460..8f008f34f2 100644 --- a/framework/rendering/subpasses/lighting_subpass.cpp +++ b/framework/rendering/subpasses/lighting_subpass.cpp @@ -18,16 +18,17 @@ #include "lighting_subpass.h" #include "buffer_pool.h" +#include "core/command_buffer.h" #include "rendering/render_context.h" +#include "resource_cache.h" #include "scene_graph/components/camera.h" #include "scene_graph/scene.h" namespace vkb { -LightingSubpass::LightingSubpass(RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Camera &cam, sg::Scene &scene_) : - Subpass{render_context, std::move(vertex_shader), std::move(fragment_shader)}, - camera{cam}, - scene{scene_} +LightingSubpass::LightingSubpass( + vkb::rendering::RenderContextC &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Camera &cam, sg::Scene &scene_) : + Subpass{render_context, std::move(vertex_shader), std::move(fragment_shader)}, camera{cam}, scene{scene_} { } diff --git a/framework/rendering/subpasses/lighting_subpass.h b/framework/rendering/subpasses/lighting_subpass.h index 5b9b4be849..1f55c890af 100644 --- a/framework/rendering/subpasses/lighting_subpass.h +++ b/framework/rendering/subpasses/lighting_subpass.h @@ -65,7 +65,8 @@ struct alignas(16) DeferredLights class LightingSubpass : public vkb::rendering::SubpassC { public: - LightingSubpass(RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Camera &camera, sg::Scene &scene); + LightingSubpass( + vkb::rendering::RenderContextC &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Camera &camera, sg::Scene &scene); virtual void prepare() override; diff --git a/framework/stats/hpp_stats.h b/framework/stats/hpp_stats.h index 848d7652a5..c46050991a 100644 --- a/framework/stats/hpp_stats.h +++ b/framework/stats/hpp_stats.h @@ -17,12 +17,17 @@ #pragma once -#include - -#include +#include "stats/stats.h" namespace vkb { +namespace rendering +{ +template +class RenderContext; +using RenderContextCpp = RenderContext; +} // namespace rendering + namespace stats { /** @@ -41,8 +46,8 @@ class HPPStats : private vkb::Stats using vkb::Stats::resize; using vkb::Stats::update; - explicit HPPStats(vkb::rendering::HPPRenderContext &render_context, size_t buffer_size = 16) : - vkb::Stats(reinterpret_cast(render_context), buffer_size) + explicit HPPStats(vkb::rendering::RenderContextCpp &render_context, size_t buffer_size = 16) : + vkb::Stats(reinterpret_cast(render_context), buffer_size) {} void begin_sampling(vkb::core::CommandBufferCpp &cb) diff --git a/framework/stats/stats.cpp b/framework/stats/stats.cpp index 364d495731..088e9f5973 100644 --- a/framework/stats/stats.cpp +++ b/framework/stats/stats.cpp @@ -33,7 +33,7 @@ namespace vkb { -Stats::Stats(RenderContext &render_context, size_t buffer_size) : +Stats::Stats(vkb::rendering::RenderContextC &render_context, size_t buffer_size) : render_context(render_context), buffer_size(buffer_size) { diff --git a/framework/stats/stats.h b/framework/stats/stats.h index c61ccfd0c5..5ecb75ae8f 100644 --- a/framework/stats/stats.h +++ b/framework/stats/stats.h @@ -31,7 +31,6 @@ namespace vkb { -class RenderContext; namespace core { @@ -40,6 +39,13 @@ class CommandBuffer; using CommandBufferC = CommandBuffer; } // namespace core +namespace rendering +{ +template +class RenderContext; +using RenderContextC = RenderContext; +} // namespace rendering + /* * @brief Helper class for querying statistics about the CPU and the GPU */ @@ -51,7 +57,7 @@ class Stats * @param render_context The RenderContext for this sample * @param buffer_size Size of the circular buffers */ - explicit Stats(RenderContext &render_context, size_t buffer_size = 16); + explicit Stats(vkb::rendering::RenderContextC &render_context, size_t buffer_size = 16); /** * @brief Destroys the Stats object @@ -140,7 +146,7 @@ class Stats private: /// The render context - RenderContext &render_context; + vkb::rendering::RenderContextC &render_context; /// Stats that were requested - they may not all be available std::set requested_stats; diff --git a/framework/stats/vulkan_stats_provider.cpp b/framework/stats/vulkan_stats_provider.cpp index 614d4dffd9..2094bb9b7f 100644 --- a/framework/stats/vulkan_stats_provider.cpp +++ b/framework/stats/vulkan_stats_provider.cpp @@ -24,9 +24,9 @@ namespace vkb { -VulkanStatsProvider::VulkanStatsProvider(std::set &requested_stats, - const CounterSamplingConfig &sampling_config, - RenderContext &render_context) : +VulkanStatsProvider::VulkanStatsProvider(std::set &requested_stats, + const CounterSamplingConfig &sampling_config, + vkb::rendering::RenderContextC &render_context) : render_context(render_context) { // Check all the Vulkan capabilities we require are present diff --git a/framework/stats/vulkan_stats_provider.h b/framework/stats/vulkan_stats_provider.h index 2376e2146e..f18888b167 100644 --- a/framework/stats/vulkan_stats_provider.h +++ b/framework/stats/vulkan_stats_provider.h @@ -22,7 +22,12 @@ namespace vkb { +namespace rendering +{ +template class RenderContext; +using RenderContextC = RenderContext; +} // namespace rendering class VulkanStatsProvider : public StatsProvider { @@ -83,8 +88,7 @@ class VulkanStatsProvider : public StatsProvider * @param sampling_config Sampling mode configuration (polling or continuous) * @param render_context The render context */ - VulkanStatsProvider(std::set &requested_stats, const CounterSamplingConfig &sampling_config, - RenderContext &render_context); + VulkanStatsProvider(std::set &requested_stats, const CounterSamplingConfig &sampling_config, vkb::rendering::RenderContextC &render_context); /** * @brief Destructs a VulkanStatsProvider @@ -133,7 +137,7 @@ class VulkanStatsProvider : public StatsProvider private: // The render context - RenderContext &render_context; + vkb::rendering::RenderContextC &render_context; // The query pool for the performance queries std::unique_ptr query_pool; diff --git a/framework/vulkan_sample.h b/framework/vulkan_sample.h index f424372af9..a2c2a04c4c 100644 --- a/framework/vulkan_sample.h +++ b/framework/vulkan_sample.h @@ -113,7 +113,9 @@ class HPPPhysicalDevice; namespace rendering { -class HPPRenderContext; +template +class RenderContext; + class HPPRenderTarget; } // namespace rendering @@ -134,7 +136,6 @@ class VulkanSample : public vkb::Application VulkanSample() = default; ~VulkanSample() override; - using RenderContextType = typename std::conditional::type; using RenderPipelineType = typename std::conditional::type; using RenderTargetType = typename std::conditional::type; using SceneType = typename std::conditional::type; @@ -144,10 +145,10 @@ class VulkanSample : public vkb::Application using SurfaceFormatType = typename std::conditional::type; using SurfaceType = typename std::conditional::type; - Configuration &get_configuration(); - RenderContextType &get_render_context(); - RenderContextType const &get_render_context() const; - bool has_render_context() const; + Configuration &get_configuration(); + vkb::rendering::RenderContext &get_render_context(); + vkb::rendering::RenderContext const &get_render_context() const; + bool has_render_context() const; /// /// PROTECTED VIRTUAL INTERFACE @@ -304,7 +305,7 @@ class VulkanSample : public vkb::Application */ void set_high_priority_graphics_queue_enable(bool enable); - void set_render_context(std::unique_ptr &&render_context); + void set_render_context(std::unique_ptr> &&render_context); void set_render_pipeline(std::unique_ptr &&render_pipeline); @@ -391,7 +392,7 @@ class VulkanSample : public vkb::Application /** * @brief Context used for rendering, it is responsible for managing the frames and their underlying images */ - std::unique_ptr render_context; + std::unique_ptr render_context; /** * @brief Pipeline used for rendering, it should be set up by the concrete sample @@ -552,7 +553,7 @@ void VulkanSample::create_render_context_impl(const std::vector(*device, surface, *window, present_mode, present_mode_priority_list, surface_priority_list); + std::make_unique(*device, surface, *window, present_mode, present_mode_priority_list, surface_priority_list); } template @@ -834,21 +835,21 @@ inline std::vector::LayerSettingType> const & } template -inline typename VulkanSample::RenderContextType const &VulkanSample::get_render_context() const +inline vkb::rendering::RenderContext const &VulkanSample::get_render_context() const { assert(render_context && "Render context is not valid"); if constexpr (bindingType == BindingType::Cpp) { - return reinterpret_cast(*render_context); + return *render_context; } else { - return *render_context; + return reinterpret_cast(*render_context); } } template -inline typename VulkanSample::RenderContextType &VulkanSample::get_render_context() +inline vkb::rendering::RenderContext &VulkanSample::get_render_context() { assert(render_context && "Render context is not valid"); if constexpr (bindingType == BindingType::Cpp) @@ -857,7 +858,7 @@ inline typename VulkanSample::RenderContextType &VulkanSample(*render_context); + return reinterpret_cast(*render_context); } } @@ -1177,7 +1178,7 @@ inline void VulkanSample::create_gui(const Window &window, StatsTyp } else { - gui = std::make_unique(reinterpret_cast(get_render_context()), + gui = std::make_unique(reinterpret_cast(get_render_context()), window, reinterpret_cast(stats), font_size, @@ -1267,7 +1268,7 @@ inline void VulkanSample::set_high_priority_graphics_queue_enable(b } template -inline void VulkanSample::set_render_context(std::unique_ptr &&rc) +inline void VulkanSample::set_render_context(std::unique_ptr> &&rc) { if constexpr (bindingType == BindingType::Cpp) { @@ -1275,7 +1276,7 @@ inline void VulkanSample::set_render_context(std::unique_ptr(rc.release())); + render_context.reset(reinterpret_cast(rc.release())); } } @@ -1344,7 +1345,7 @@ inline void VulkanSample::update(float delta_time) stats->end_sampling(*command_buffer); command_buffer->end(); - render_context->submit(*command_buffer); + render_context->submit(command_buffer); } template diff --git a/samples/performance/16bit_arithmetic/16bit_arithmetic.cpp b/samples/performance/16bit_arithmetic/16bit_arithmetic.cpp index f47ce4fef2..a2fd2e3f80 100644 --- a/samples/performance/16bit_arithmetic/16bit_arithmetic.cpp +++ b/samples/performance/16bit_arithmetic/16bit_arithmetic.cpp @@ -177,9 +177,7 @@ bool KHR16BitArithmeticSample::prepare(const vkb::ApplicationOptions &options) // Setup the visualization subpass which is there to blit the final result to screen. vkb::ShaderSource vertex_source{"16bit_arithmetic/visualize.vert.spv"}; vkb::ShaderSource fragment_source{"16bit_arithmetic/visualize.frag.spv"}; - auto subpass = std::make_unique(get_render_context(), - std::move(vertex_source), - std::move(fragment_source)); + auto subpass = std::make_unique(get_render_context(), std::move(vertex_source), std::move(fragment_source)); subpass->view = image_view.get(); subpass->sampler = sampler.get(); @@ -192,9 +190,9 @@ bool KHR16BitArithmeticSample::prepare(const vkb::ApplicationOptions &options) return true; } -KHR16BitArithmeticSample::VisualizationSubpass::VisualizationSubpass(vkb::RenderContext &context, - vkb::ShaderSource &&vertex_source, - vkb::ShaderSource &&fragment_source) : +KHR16BitArithmeticSample::VisualizationSubpass::VisualizationSubpass(vkb::rendering::RenderContextC &context, + vkb::ShaderSource &&vertex_source, + vkb::ShaderSource &&fragment_source) : vkb::rendering::SubpassC(context, std::move(vertex_source), std::move(fragment_source)) { set_output_attachments({0}); diff --git a/samples/performance/16bit_arithmetic/16bit_arithmetic.h b/samples/performance/16bit_arithmetic/16bit_arithmetic.h index 3fec31935e..c7a15ad56e 100644 --- a/samples/performance/16bit_arithmetic/16bit_arithmetic.h +++ b/samples/performance/16bit_arithmetic/16bit_arithmetic.h @@ -59,7 +59,7 @@ class KHR16BitArithmeticSample : public vkb::VulkanSampleC struct VisualizationSubpass : vkb::rendering::SubpassC { - VisualizationSubpass(vkb::RenderContext &context, vkb::ShaderSource &&vertex_source, vkb::ShaderSource &&fragment_source); + VisualizationSubpass(vkb::rendering::RenderContextC &context, vkb::ShaderSource &&vertex_source, vkb::ShaderSource &&fragment_source); virtual void prepare() override; virtual void draw(vkb::core::CommandBufferC &command_buffer) override; diff --git a/samples/performance/async_compute/async_compute.cpp b/samples/performance/async_compute/async_compute.cpp index db99205e03..c87597a862 100644 --- a/samples/performance/async_compute/async_compute.cpp +++ b/samples/performance/async_compute/async_compute.cpp @@ -825,7 +825,7 @@ std::unique_ptr create_async_compute() return std::make_unique(); } -AsyncComputeSample::DepthMapSubpass::DepthMapSubpass(vkb::RenderContext &render_context, +AsyncComputeSample::DepthMapSubpass::DepthMapSubpass(vkb::rendering::RenderContextC &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) : vkb::ForwardSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera) @@ -841,7 +841,7 @@ void AsyncComputeSample::DepthMapSubpass::draw(vkb::core::CommandBufferC &comman vkb::ForwardSubpass::draw(command_buffer); } -AsyncComputeSample::ShadowMapForwardSubpass::ShadowMapForwardSubpass(vkb::RenderContext &render_context, +AsyncComputeSample::ShadowMapForwardSubpass::ShadowMapForwardSubpass(vkb::rendering::RenderContextC &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera, vkb::sg::Camera &shadow_camera_) : vkb::ForwardSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera), @@ -874,7 +874,9 @@ void AsyncComputeSample::ShadowMapForwardSubpass::draw(vkb::core::CommandBufferC vkb::ForwardSubpass::draw(command_buffer); } -AsyncComputeSample::CompositeSubpass::CompositeSubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader) : +AsyncComputeSample::CompositeSubpass::CompositeSubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader) : vkb::rendering::SubpassC(render_context, std::move(vertex_shader), std::move(fragment_shader)) { } diff --git a/samples/performance/async_compute/async_compute.h b/samples/performance/async_compute/async_compute.h index 251e7ca27c..4ce2fd766b 100644 --- a/samples/performance/async_compute/async_compute.h +++ b/samples/performance/async_compute/async_compute.h @@ -83,17 +83,22 @@ class AsyncComputeSample : public vkb::VulkanSampleC struct DepthMapSubpass : vkb::ForwardSubpass { - DepthMapSubpass(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, - vkb::sg::Scene &scene, vkb::sg::Camera &camera); + DepthMapSubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera); virtual void draw(vkb::core::CommandBufferC &command_buffer) override; }; struct ShadowMapForwardSubpass : vkb::ForwardSubpass { - ShadowMapForwardSubpass(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, - vkb::sg::Scene &scene, vkb::sg::Camera &camera, vkb::sg::Camera &shadow_camera); + ShadowMapForwardSubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera, + vkb::sg::Camera &shadow_camera); void set_shadow_map(const vkb::core::ImageView *view, const vkb::core::Sampler *sampler); virtual void draw(vkb::core::CommandBufferC &command_buffer) override; @@ -104,8 +109,7 @@ class AsyncComputeSample : public vkb::VulkanSampleC struct CompositeSubpass : vkb::rendering::SubpassC { - CompositeSubpass(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader); + CompositeSubpass(vkb::rendering::RenderContextC &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader); void set_texture(const vkb::core::ImageView *hdr_view, const vkb::core::ImageView *bloom_view, const vkb::core::Sampler *sampler); virtual void draw(vkb::core::CommandBufferC &command_buffer) override; diff --git a/samples/performance/command_buffer_usage/command_buffer_usage.cpp b/samples/performance/command_buffer_usage/command_buffer_usage.cpp index e6669177f3..7b5f40afa3 100644 --- a/samples/performance/command_buffer_usage/command_buffer_usage.cpp +++ b/samples/performance/command_buffer_usage/command_buffer_usage.cpp @@ -252,8 +252,11 @@ void CommandBufferUsage::draw_renderpass(vkb::core::CommandBufferC &primary_comm primary_command_buffer.end_render_pass(); } -CommandBufferUsage::ForwardSubpassSecondary::ForwardSubpassSecondary(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene_, vkb::sg::Camera &camera) : +CommandBufferUsage::ForwardSubpassSecondary::ForwardSubpassSecondary(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader, + vkb::sg::Scene &scene_, + vkb::sg::Camera &camera) : vkb::ForwardSubpass{render_context, std::move(vertex_shader), std::move(fragment_shader), scene_, camera} { } diff --git a/samples/performance/command_buffer_usage/command_buffer_usage.h b/samples/performance/command_buffer_usage/command_buffer_usage.h index e53c200742..f0f2010ce6 100644 --- a/samples/performance/command_buffer_usage/command_buffer_usage.h +++ b/samples/performance/command_buffer_usage/command_buffer_usage.h @@ -144,9 +144,11 @@ class CommandBufferUsage : public vkb::VulkanSampleC class ForwardSubpassSecondary : public vkb::ForwardSubpass { public: - ForwardSubpassSecondary(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_source, vkb::ShaderSource &&fragment_source, - vkb::sg::Scene &scene, vkb::sg::Camera &camera); + ForwardSubpassSecondary(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_source, + vkb::ShaderSource &&fragment_source, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera); void draw(vkb::core::CommandBufferC &primary_command_buffer) override; diff --git a/samples/performance/constant_data/constant_data.h b/samples/performance/constant_data/constant_data.h index 3fa134d463..06c0fdaa49 100644 --- a/samples/performance/constant_data/constant_data.h +++ b/samples/performance/constant_data/constant_data.h @@ -105,7 +105,11 @@ class ConstantData : public vkb::VulkanSampleC class ConstantDataSubpass : public vkb::ForwardSubpass { public: - ConstantDataSubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) : + ConstantDataSubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera) : vkb::ForwardSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera) {} @@ -122,7 +126,11 @@ class ConstantData : public vkb::VulkanSampleC class PushConstantSubpass : public ConstantDataSubpass { public: - PushConstantSubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) : + PushConstantSubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera) : ConstantDataSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera) {} @@ -155,7 +163,11 @@ class ConstantData : public vkb::VulkanSampleC class DescriptorSetSubpass : public ConstantDataSubpass { public: - DescriptorSetSubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) : + DescriptorSetSubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera) : ConstantDataSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera) {} @@ -188,7 +200,11 @@ class ConstantData : public vkb::VulkanSampleC class BufferArraySubpass : public ConstantDataSubpass { public: - BufferArraySubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) : + BufferArraySubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera) : ConstantDataSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera) {} diff --git a/samples/performance/multithreading_render_passes/multithreading_render_passes.cpp b/samples/performance/multithreading_render_passes/multithreading_render_passes.cpp index 5ede159b15..ce7704b6fe 100644 --- a/samples/performance/multithreading_render_passes/multithreading_render_passes.cpp +++ b/samples/performance/multithreading_render_passes/multithreading_render_passes.cpp @@ -441,7 +441,7 @@ void MultithreadingRenderPasses::draw_main_pass(vkb::core::CommandBufferC &comma } } -MultithreadingRenderPasses::MainSubpass::MainSubpass(vkb::RenderContext &render_context, +MultithreadingRenderPasses::MainSubpass::MainSubpass(vkb::rendering::RenderContextC &render_context, vkb::ShaderSource &&vertex_source, vkb::ShaderSource &&fragment_source, vkb::sg::Scene &scene, @@ -498,11 +498,11 @@ void MultithreadingRenderPasses::MainSubpass::draw(vkb::core::CommandBufferC &co ForwardSubpass::draw(command_buffer); } -MultithreadingRenderPasses::ShadowSubpass::ShadowSubpass(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_source, - vkb::ShaderSource &&fragment_source, - vkb::sg::Scene &scene, - vkb::sg::Camera &camera) : +MultithreadingRenderPasses::ShadowSubpass::ShadowSubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_source, + vkb::ShaderSource &&fragment_source, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera) : vkb::GeometrySubpass{render_context, std::move(vertex_source), std::move(fragment_source), scene, camera} { } diff --git a/samples/performance/multithreading_render_passes/multithreading_render_passes.h b/samples/performance/multithreading_render_passes/multithreading_render_passes.h index 3d4acb5c64..06b34db4f8 100644 --- a/samples/performance/multithreading_render_passes/multithreading_render_passes.h +++ b/samples/performance/multithreading_render_passes/multithreading_render_passes.h @@ -61,11 +61,11 @@ class MultithreadingRenderPasses : public vkb::VulkanSampleC class ShadowSubpass : public vkb::GeometrySubpass { public: - ShadowSubpass(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_source, - vkb::ShaderSource &&fragment_source, - vkb::sg::Scene &scene, - vkb::sg::Camera &camera); + ShadowSubpass(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_source, + vkb::ShaderSource &&fragment_source, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera); protected: virtual void prepare_pipeline_state(vkb::core::CommandBufferC &command_buffer, VkFrontFace front_face, bool double_sided_material) @@ -84,7 +84,7 @@ class MultithreadingRenderPasses : public vkb::VulkanSampleC class MainSubpass : public vkb::ForwardSubpass { public: - MainSubpass(vkb::RenderContext &render_context, + MainSubpass(vkb::rendering::RenderContextC &render_context, vkb::ShaderSource &&vertex_source, vkb::ShaderSource &&fragment_source, vkb::sg::Scene &scene, diff --git a/samples/performance/specialization_constants/specialization_constants.cpp b/samples/performance/specialization_constants/specialization_constants.cpp index 49c1c65d83..4f510163dc 100644 --- a/samples/performance/specialization_constants/specialization_constants.cpp +++ b/samples/performance/specialization_constants/specialization_constants.cpp @@ -34,8 +34,11 @@ SpecializationConstants::SpecializationConstants() config.insert(1, specialization_constants_enabled, 1); } -SpecializationConstants::ForwardSubpassCustomLights::ForwardSubpassCustomLights(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene_, vkb::sg::Camera &camera) : +SpecializationConstants::ForwardSubpassCustomLights::ForwardSubpassCustomLights(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_shader, + vkb::ShaderSource &&fragment_shader, + vkb::sg::Scene &scene_, + vkb::sg::Camera &camera) : vkb::ForwardSubpass{render_context, std::move(vertex_shader), std::move(fragment_shader), scene_, camera} { } diff --git a/samples/performance/specialization_constants/specialization_constants.h b/samples/performance/specialization_constants/specialization_constants.h index 82d4fdc9b3..d302ef301b 100644 --- a/samples/performance/specialization_constants/specialization_constants.h +++ b/samples/performance/specialization_constants/specialization_constants.h @@ -52,9 +52,11 @@ class SpecializationConstants : public vkb::VulkanSampleC class ForwardSubpassCustomLights : public vkb::ForwardSubpass { public: - ForwardSubpassCustomLights(vkb::RenderContext &render_context, - vkb::ShaderSource &&vertex_source, vkb::ShaderSource &&fragment_source, - vkb::sg::Scene &scene, vkb::sg::Camera &camera); + ForwardSubpassCustomLights(vkb::rendering::RenderContextC &render_context, + vkb::ShaderSource &&vertex_source, + vkb::ShaderSource &&fragment_source, + vkb::sg::Scene &scene, + vkb::sg::Camera &camera); virtual void prepare() override; diff --git a/samples/performance/wait_idle/wait_idle.h b/samples/performance/wait_idle/wait_idle.h index 81f84a0d78..97105047d8 100644 --- a/samples/performance/wait_idle/wait_idle.h +++ b/samples/performance/wait_idle/wait_idle.h @@ -35,7 +35,7 @@ class WaitIdle : public vkb::VulkanSampleC * @brief This RenderContext is responsible containing the scene's RenderFrames * It implements a custom wait_frame function which alternates between waiting with WaitIdle or Fences */ - class CustomRenderContext : public vkb::RenderContext + class CustomRenderContext : public vkb::rendering::RenderContextC { public: CustomRenderContext(vkb::core::DeviceC &device, VkSurfaceKHR surface, const vkb::Window &window, int &wait_idle_enabled);