1451 lines
45 KiB
C++
1451 lines
45 KiB
C++
/* Copyright (c) 2019-2025, Arm Limited and Contributors
|
|
* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 the "License";
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "common/hpp_utils.h"
|
|
#include "core/debug.h"
|
|
#include "gui.h"
|
|
#include "hpp_gltf_loader.h"
|
|
#include "platform/application.h"
|
|
#include "platform/window.h"
|
|
#include "rendering/hpp_render_pipeline.h"
|
|
#include "stats/hpp_stats.h"
|
|
|
|
#if defined(PLATFORM__MACOS)
|
|
# include <TargetConditionals.h>
|
|
#endif
|
|
|
|
namespace vkb
|
|
{
|
|
/**
|
|
* @mainpage Overview of the framework
|
|
*
|
|
* @section initialization Initialization
|
|
*
|
|
* @subsection platform_init Platform initialization
|
|
* The lifecycle of a Vulkan sample starts by instantiating the correct Platform
|
|
* (e.g. WindowsPlatform) and then calling initialize() on it, which sets up
|
|
* the windowing system and logging. Then it calls the parent Platform::initialize(),
|
|
* which takes ownership of the active application. It's the platforms responsibility
|
|
* to then call VulkanSample::prepare() to prepare the vulkan sample when it is ready.
|
|
*
|
|
* @subsection sample_init Sample initialization
|
|
* The preparation step is divided in two steps, one in VulkanSample and the other in the
|
|
* specific sample, such as SurfaceRotation.
|
|
* VulkanSample::prepare() contains functions that do not require customization,
|
|
* including creating a Vulkan instance, the surface and getting physical devices.
|
|
* The prepare() function for the specific sample completes the initialization, including:
|
|
* - setting enabled Stats
|
|
* - creating the Device
|
|
* - creating the Swapchain
|
|
* - creating the RenderContext (or child class)
|
|
* - preparing the RenderContext
|
|
* - loading the sg::Scene
|
|
* - creating the RenderPipeline with ShaderModule (s)
|
|
* - creating the sg::Camera
|
|
* - creating the Gui
|
|
*
|
|
* @section frame_rendering Frame rendering
|
|
*
|
|
* @subsection update Update function
|
|
* Rendering happens in the update() function. Each sample can override it, e.g.
|
|
* to recreate the Swapchain in SwapchainImages when required by user input.
|
|
* Typically a sample will then call VulkanSample::update().
|
|
*
|
|
* @subsection rendering Rendering
|
|
* A series of steps are performed, some of which can be customized (it will be
|
|
* highlighted when that's the case):
|
|
*
|
|
* - calling sg::Script::update() for all sg::Script (s)
|
|
* - beginning a frame in RenderContext (does the necessary waiting on fences and
|
|
* acquires an core::Image)
|
|
* - requesting a CommandBuffer
|
|
* - updating Stats and Gui
|
|
* - getting an active RenderTarget constructed by the factory function of the RenderFrame
|
|
* - setting up barriers for color and depth, note that these are only for the default RenderTarget
|
|
* - calling VulkanSample::draw_swapchain_renderpass (see below)
|
|
* - setting up a barrier for the Swapchain transition to present
|
|
* - submitting the CommandBuffer and end the Frame (present)
|
|
*
|
|
* @subsection draw_swapchain Draw swapchain renderpass
|
|
* The function starts and ends a RenderPass which includes setting up viewport, scissors,
|
|
* blend state (etc.) and calling draw_scene.
|
|
* Note that RenderPipeline::draw is not virtual in RenderPipeline, but internally it calls
|
|
* Subpass::draw for each Subpass, which is virtual and can be customized.
|
|
*
|
|
* @section framework_classes Main framework classes
|
|
*
|
|
* - RenderContext
|
|
* - RenderFrame
|
|
* - RenderTarget
|
|
* - RenderPipeline
|
|
* - ShaderModule
|
|
* - ResourceCache
|
|
* - BufferPool
|
|
* - Core classes: Classes in vkb::core wrap Vulkan objects for indexing and hashing.
|
|
*/
|
|
|
|
class RenderPipeline;
|
|
class Stats;
|
|
|
|
namespace core
|
|
{
|
|
class HPPDevice;
|
|
class HPPInstance;
|
|
class HPPPhysicalDevice;
|
|
} // namespace core
|
|
|
|
namespace rendering
|
|
{
|
|
class HPPRenderContext;
|
|
class HPPRenderTarget;
|
|
} // namespace rendering
|
|
|
|
namespace stats
|
|
{
|
|
class HPPStats;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
class VulkanSample : public vkb::Application
|
|
{
|
|
using Parent = vkb::Application;
|
|
|
|
/// <summary>
|
|
/// PUBLIC INTERFACE
|
|
/// </summary>
|
|
public:
|
|
VulkanSample() = default;
|
|
~VulkanSample() override;
|
|
|
|
using PhysicalDeviceType = typename std::conditional<bindingType == BindingType::Cpp, vkb::core::HPPPhysicalDevice, vkb::PhysicalDevice>::type;
|
|
using RenderContextType = typename std::conditional<bindingType == BindingType::Cpp, vkb::rendering::HPPRenderContext, vkb::RenderContext>::type;
|
|
using RenderPipelineType = typename std::conditional<bindingType == BindingType::Cpp, vkb::rendering::HPPRenderPipeline, vkb::RenderPipeline>::type;
|
|
using RenderTargetType = typename std::conditional<bindingType == BindingType::Cpp, vkb::rendering::HPPRenderTarget, vkb::RenderTarget>::type;
|
|
using SceneType = typename std::conditional<bindingType == BindingType::Cpp, vkb::scene_graph::HPPScene, vkb::sg::Scene>::type;
|
|
using StatsType = typename std::conditional<bindingType == BindingType::Cpp, vkb::stats::HPPStats, vkb::Stats>::type;
|
|
using Extent2DType = typename std::conditional<bindingType == BindingType::Cpp, vk::Extent2D, VkExtent2D>::type;
|
|
using LayerSettingType = typename std::conditional<bindingType == BindingType::Cpp, vk::LayerSettingEXT, VkLayerSettingEXT>::type;
|
|
using SurfaceFormatType = typename std::conditional<bindingType == BindingType::Cpp, vk::SurfaceFormatKHR, VkSurfaceFormatKHR>::type;
|
|
using SurfaceType = typename std::conditional<bindingType == BindingType::Cpp, vk::SurfaceKHR, VkSurfaceKHR>::type;
|
|
|
|
Configuration &get_configuration();
|
|
RenderContextType &get_render_context();
|
|
RenderContextType const &get_render_context() const;
|
|
bool has_render_context() const;
|
|
|
|
/// <summary>
|
|
/// PROTECTED VIRTUAL INTERFACE
|
|
/// </summary>
|
|
protected:
|
|
// from Application
|
|
void input_event(const InputEvent &input_event) override;
|
|
void finish() override;
|
|
bool resize(uint32_t width, uint32_t height) override;
|
|
|
|
/**
|
|
* @brief Create the Vulkan device used by this sample
|
|
* @note Can be overridden to implement custom device creation
|
|
*/
|
|
virtual std::unique_ptr<vkb::core::Device<bindingType>> create_device(PhysicalDeviceType &gpu);
|
|
|
|
/**
|
|
* @brief Create the Vulkan instance used by this sample
|
|
* @note Can be overridden to implement custom instance creation
|
|
*/
|
|
virtual std::unique_ptr<vkb::core::Instance<bindingType>> create_instance();
|
|
|
|
/**
|
|
* @brief Override this to customise the creation of the render_context
|
|
*/
|
|
virtual void create_render_context();
|
|
|
|
/**
|
|
* @brief Prepares the render target and draws to it, calling draw_renderpass
|
|
* @param command_buffer The command buffer to record the commands to
|
|
* @param render_target The render target that is being drawn to
|
|
*/
|
|
virtual void draw(vkb::core::CommandBuffer<bindingType> &command_buffer, RenderTargetType &render_target);
|
|
|
|
/**
|
|
* @brief Samples should override this function to draw their interface
|
|
*/
|
|
virtual void draw_gui();
|
|
|
|
/**
|
|
* @brief Starts the render pass, executes the render pipeline, and then ends the render pass
|
|
* @param command_buffer The command buffer to record the commands to
|
|
* @param render_target The render target that is being drawn to
|
|
*/
|
|
virtual void draw_renderpass(vkb::core::CommandBuffer<bindingType> &command_buffer, RenderTargetType &render_target);
|
|
|
|
/**
|
|
* @brief Override this to customise the creation of the swapchain and render_context
|
|
*/
|
|
virtual void prepare_render_context();
|
|
|
|
/**
|
|
* @brief Triggers the render pipeline, it can be overridden by samples to specialize their rendering logic
|
|
* @param command_buffer The command buffer to record the commands to
|
|
*/
|
|
virtual void render(vkb::core::CommandBuffer<bindingType> &command_buffer);
|
|
|
|
/**
|
|
* @brief Request features from the gpu based on what is supported
|
|
*/
|
|
virtual void request_gpu_features(PhysicalDeviceType &gpu);
|
|
|
|
/**
|
|
* @brief Resets the stats view max values for high demanding configs
|
|
* Should be overridden by the samples since they
|
|
* know which configuration is resource demanding
|
|
*/
|
|
virtual void reset_stats_view();
|
|
|
|
/**
|
|
* @brief Updates the debug window, samples can override this to insert their own data elements
|
|
*/
|
|
virtual void update_debug_window();
|
|
|
|
/// <summary>
|
|
/// PROTECTED INTERFACE
|
|
/// </summary>
|
|
/**
|
|
* @brief Add a sample-specific device extension
|
|
* @param extension The extension name
|
|
* @param optional (Optional) Whether the extension is optional
|
|
*/
|
|
void add_device_extension(const char *extension, bool optional = false);
|
|
|
|
/**
|
|
* @brief Add a sample-specific instance extension
|
|
* @param extension The extension name
|
|
* @param optional (Optional) Whether the extension is optional
|
|
*/
|
|
void add_instance_extension(const char *extension, bool optional = false);
|
|
|
|
/**
|
|
* @brief Add a sample-specific instance layer
|
|
* @param layer The layer name
|
|
* @param optional (Optional) Whether the extension is optional
|
|
*/
|
|
void add_instance_layer(const char *layer, bool optional = false);
|
|
|
|
/**
|
|
* @brief Add a sample-specific layer setting
|
|
* @param layerSetting The layer setting
|
|
*/
|
|
void add_layer_setting(LayerSettingType const &layerSetting);
|
|
|
|
void create_gui(const Window &window, StatsType const *stats = nullptr, const float font_size = 21.0f, bool explicit_update = false);
|
|
|
|
/**
|
|
* @brief A helper to create a render context
|
|
*/
|
|
void create_render_context(const std::vector<SurfaceFormatType> &surface_priority_list);
|
|
|
|
vkb::core::Device<bindingType> &get_device();
|
|
vkb::core::Device<bindingType> const &get_device() const;
|
|
vkb::Gui<bindingType> &get_gui();
|
|
vkb::Gui<bindingType> const &get_gui() const;
|
|
vkb::core::Instance<bindingType> &get_instance();
|
|
vkb::core::Instance<bindingType> const &get_instance() const;
|
|
RenderPipelineType &get_render_pipeline();
|
|
RenderPipelineType const &get_render_pipeline() const;
|
|
SceneType &get_scene();
|
|
StatsType &get_stats();
|
|
SurfaceType get_surface() const;
|
|
std::vector<SurfaceFormatType> &get_surface_priority_list();
|
|
std::vector<SurfaceFormatType> const &get_surface_priority_list() const;
|
|
bool has_device() const;
|
|
bool has_instance() const;
|
|
bool has_gui() const;
|
|
bool has_render_pipeline() const;
|
|
bool has_scene();
|
|
|
|
/**
|
|
* @brief Loads the scene
|
|
*
|
|
* @param path The path of the glTF file
|
|
*/
|
|
void load_scene(const std::string &path);
|
|
|
|
/**
|
|
* @brief Additional sample initialization
|
|
*/
|
|
bool prepare(const ApplicationOptions &options) override;
|
|
|
|
/**
|
|
* @brief Set the Vulkan API version to request at instance creation time
|
|
*/
|
|
void set_api_version(uint32_t requested_api_version);
|
|
|
|
/**
|
|
* @brief Sets whether or not the first graphics queue should have higher priority than other queues.
|
|
* Very specific feature which is used by async compute samples.
|
|
* Needs to be called before prepare().
|
|
* @param enable If true, present queue will have prio 1.0 and other queues have prio 0.5.
|
|
* Default state is false, where all queues have 0.5 priority.
|
|
*/
|
|
void set_high_priority_graphics_queue_enable(bool enable);
|
|
|
|
void set_render_context(std::unique_ptr<RenderContextType> &&render_context);
|
|
|
|
void set_render_pipeline(std::unique_ptr<RenderPipelineType> &&render_pipeline);
|
|
|
|
/**
|
|
* @brief Main loop sample events
|
|
*/
|
|
void update(float delta_time) override;
|
|
|
|
/**
|
|
* @brief Update GUI
|
|
* @param delta_time
|
|
*/
|
|
void update_gui(float delta_time);
|
|
|
|
/**
|
|
* @brief Update scene
|
|
* @param delta_time
|
|
*/
|
|
void update_scene(float delta_time);
|
|
|
|
/**
|
|
* @brief Update counter values
|
|
* @param delta_time
|
|
*/
|
|
void update_stats(float delta_time);
|
|
|
|
/**
|
|
* @brief Set viewport and scissor state in command buffer for a given extent
|
|
*/
|
|
static void set_viewport_and_scissor(vkb::core::CommandBuffer<bindingType> const &command_buffer, Extent2DType const &extent);
|
|
|
|
/// <summary>
|
|
/// PRIVATE INTERFACE
|
|
/// </summary>
|
|
private:
|
|
void create_render_context_impl(const std::vector<vk::SurfaceFormatKHR> &surface_priority_list);
|
|
void draw_impl(vkb::core::CommandBufferCpp &command_buffer, vkb::rendering::HPPRenderTarget &render_target);
|
|
void draw_renderpass_impl(vkb::core::CommandBufferCpp &command_buffer, vkb::rendering::HPPRenderTarget &render_target);
|
|
void render_impl(vkb::core::CommandBufferCpp &command_buffer);
|
|
static void set_viewport_and_scissor_impl(vkb::core::CommandBufferCpp const &command_buffer, vk::Extent2D const &extent);
|
|
|
|
/**
|
|
* @brief Get sample-specific device extensions.
|
|
*
|
|
* @return Map of device extensions and whether or not they are optional. Default is empty map.
|
|
*/
|
|
std::unordered_map<const char *, bool> const &get_device_extensions() const;
|
|
|
|
/**
|
|
* @brief Get sample-specific instance extensions.
|
|
*
|
|
* @return Map of instance extensions and whether or not they are optional. Default is empty map.
|
|
*/
|
|
std::unordered_map<const char *, bool> const &get_instance_extensions() const;
|
|
|
|
/**
|
|
* @brief Get sample-specific instance layers.
|
|
*
|
|
* @return Map of instance layers and whether or not they are optional. Default is empty map.
|
|
*/
|
|
std::unordered_map<const char *, bool> const &get_instance_layers() const;
|
|
|
|
/**
|
|
* @brief Get sample-specific layer settings.
|
|
*
|
|
* @return Vector of layer settings. Default is empty vector.
|
|
*/
|
|
std::vector<LayerSettingType> const &get_layer_settings() const;
|
|
|
|
/// <summary>
|
|
/// PRIVATE MEMBERS
|
|
/// </summary>
|
|
private:
|
|
/**
|
|
* @brief The Vulkan instance
|
|
*/
|
|
std::unique_ptr<vkb::core::InstanceCpp> instance;
|
|
|
|
/**
|
|
* @brief The Vulkan device
|
|
*/
|
|
std::unique_ptr<vkb::core::DeviceCpp> device;
|
|
|
|
/**
|
|
* @brief Context used for rendering, it is responsible for managing the frames and their underlying images
|
|
*/
|
|
std::unique_ptr<vkb::rendering::HPPRenderContext> render_context;
|
|
|
|
/**
|
|
* @brief Pipeline used for rendering, it should be set up by the concrete sample
|
|
*/
|
|
std::unique_ptr<vkb::rendering::HPPRenderPipeline> render_pipeline;
|
|
|
|
/**
|
|
* @brief Holds all scene information
|
|
*/
|
|
std::unique_ptr<vkb::scene_graph::HPPScene> scene;
|
|
|
|
std::unique_ptr<vkb::GuiCpp> gui;
|
|
|
|
std::unique_ptr<vkb::stats::HPPStats> stats;
|
|
|
|
static constexpr float STATS_VIEW_RESET_TIME{10.0f}; // 10 seconds
|
|
|
|
/**
|
|
* @brief The Vulkan surface
|
|
*/
|
|
vk::SurfaceKHR surface;
|
|
|
|
/**
|
|
* @brief A list of surface formats in order of priority (vector[0] has high priority, vector[size-1] has low priority)
|
|
*/
|
|
std::vector<vk::SurfaceFormatKHR> surface_priority_list = {
|
|
{vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear},
|
|
{vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}};
|
|
|
|
/**
|
|
* @brief The configuration of the sample
|
|
*/
|
|
Configuration configuration{};
|
|
|
|
/** @brief Set of device extensions to be enabled for this example and whether they are optional (must be set in the derived constructor) */
|
|
std::unordered_map<const char *, bool> device_extensions;
|
|
|
|
/** @brief Set of instance extensions to be enabled for this example and whether they are optional (must be set in the derived constructor) */
|
|
std::unordered_map<const char *, bool> instance_extensions;
|
|
|
|
/** @brief Set of instance layers to be enabled for this example and whether they are optional (must be set in the derived constructor) */
|
|
std::unordered_map<const char *, bool> instance_layers;
|
|
|
|
/** @brief Vector of layer settings to be enabled for this example (must be set in the derived constructor) */
|
|
std::vector<vk::LayerSettingEXT> layer_settings;
|
|
|
|
/** @brief The Vulkan API version to request for this sample at instance creation time */
|
|
uint32_t api_version = VK_API_VERSION_1_1;
|
|
|
|
/** @brief Whether or not we want a high priority graphics queue. */
|
|
bool high_priority_graphics_queue{false};
|
|
|
|
std::unique_ptr<vkb::core::HPPDebugUtils> debug_utils;
|
|
};
|
|
|
|
using VulkanSampleC = VulkanSample<vkb::BindingType::C>;
|
|
using VulkanSampleCpp = VulkanSample<vkb::BindingType::Cpp>;
|
|
|
|
// Member function definitions
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline VulkanSample<bindingType>::~VulkanSample()
|
|
{
|
|
if (device)
|
|
{
|
|
device->get_handle().waitIdle();
|
|
}
|
|
|
|
scene.reset();
|
|
stats.reset();
|
|
gui.reset();
|
|
render_context.reset();
|
|
device.reset();
|
|
|
|
if (surface)
|
|
{
|
|
instance->get_handle().destroySurfaceKHR(surface);
|
|
}
|
|
|
|
instance.reset();
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::add_device_extension(const char *extension, bool optional)
|
|
{
|
|
device_extensions[extension] = optional;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::add_instance_extension(const char *extension, bool optional)
|
|
{
|
|
instance_extensions[extension] = optional;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::add_layer_setting(LayerSettingType const &layerSetting)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
layer_settings.push_back(layerSetting);
|
|
}
|
|
else
|
|
{
|
|
layer_settings.push_back(reinterpret_cast<vk::LayerSettingEXT const &>(layerSetting));
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::unique_ptr<typename vkb::core::Device<bindingType>> VulkanSample<bindingType>::create_device(PhysicalDeviceType &gpu)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return std::make_unique<vkb::core::DeviceCpp>(gpu, surface, std::move(debug_utils), get_device_extensions());
|
|
}
|
|
else
|
|
{
|
|
return std::make_unique<vkb::core::DeviceC>(gpu,
|
|
static_cast<VkSurfaceKHR>(surface),
|
|
std::unique_ptr<vkb::DebugUtils>(reinterpret_cast<vkb::DebugUtils *>(debug_utils.release())),
|
|
get_device_extensions());
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::unique_ptr<vkb::core::Instance<bindingType>> VulkanSample<bindingType>::create_instance()
|
|
{
|
|
return std::make_unique<vkb::core::Instance<bindingType>>(get_name(), get_instance_extensions(), get_instance_layers(), get_layer_settings(), api_version);
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::create_render_context()
|
|
{
|
|
create_render_context_impl(surface_priority_list);
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::create_render_context(const std::vector<SurfaceFormatType> &surface_priority_list)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
create_render_context_impl(surface_priority_list);
|
|
}
|
|
else
|
|
{
|
|
create_render_context_impl(reinterpret_cast<std::vector<vk::SurfaceFormatKHR> const &>(surface_priority_list));
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
void VulkanSample<bindingType>::create_render_context_impl(const std::vector<vk::SurfaceFormatKHR> &surface_priority_list)
|
|
{
|
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
|
vk::PresentModeKHR present_mode = (window->get_properties().vsync == Window::Vsync::OFF) ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo;
|
|
std::vector<vk::PresentModeKHR> present_mode_priority_list{vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox, vk::PresentModeKHR::eImmediate};
|
|
#else
|
|
vk::PresentModeKHR present_mode = (window->get_properties().vsync == Window::Vsync::ON) ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eMailbox;
|
|
std::vector<vk::PresentModeKHR> present_mode_priority_list{vk::PresentModeKHR::eMailbox, vk::PresentModeKHR::eImmediate, vk::PresentModeKHR::eFifo};
|
|
#endif
|
|
|
|
render_context =
|
|
std::make_unique<vkb::rendering::HPPRenderContext>(*device, surface, *window, present_mode, present_mode_priority_list, surface_priority_list);
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::draw(vkb::core::CommandBuffer<bindingType> &command_buffer, RenderTargetType &render_target)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
draw_impl(command_buffer, render_target);
|
|
}
|
|
else
|
|
{
|
|
draw_impl(reinterpret_cast<vkb::core::CommandBufferCpp &>(command_buffer),
|
|
reinterpret_cast<vkb::rendering::HPPRenderTarget &>(render_target));
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::draw_impl(vkb::core::CommandBufferCpp &command_buffer,
|
|
vkb::rendering::HPPRenderTarget &render_target)
|
|
{
|
|
auto &views = render_target.get_views();
|
|
|
|
{
|
|
// Image 0 is the swapchain
|
|
vkb::common::HPPImageMemoryBarrier memory_barrier{};
|
|
memory_barrier.old_layout = vk::ImageLayout::eUndefined;
|
|
memory_barrier.new_layout = vk::ImageLayout::eColorAttachmentOptimal;
|
|
memory_barrier.src_access_mask = {};
|
|
memory_barrier.dst_access_mask = vk::AccessFlagBits::eColorAttachmentWrite;
|
|
memory_barrier.src_stage_mask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
memory_barrier.dst_stage_mask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
|
|
command_buffer.image_memory_barrier(views[0], memory_barrier);
|
|
render_target.set_layout(0, memory_barrier.new_layout);
|
|
|
|
// Skip 1 as it is handled later as a depth-stencil attachment
|
|
for (size_t i = 2; i < views.size(); ++i)
|
|
{
|
|
command_buffer.image_memory_barrier(views[i], memory_barrier);
|
|
render_target.set_layout(static_cast<uint32_t>(i), memory_barrier.new_layout);
|
|
}
|
|
}
|
|
|
|
{
|
|
vkb::common::HPPImageMemoryBarrier memory_barrier{};
|
|
memory_barrier.old_layout = vk::ImageLayout::eUndefined;
|
|
memory_barrier.new_layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
|
|
memory_barrier.src_access_mask = {};
|
|
memory_barrier.dst_access_mask = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
|
|
memory_barrier.src_stage_mask = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
memory_barrier.dst_stage_mask = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
|
|
|
|
command_buffer.image_memory_barrier(views[1], memory_barrier);
|
|
render_target.set_layout(1, memory_barrier.new_layout);
|
|
}
|
|
|
|
// draw_renderpass is a virtual function, thus we have to call that, instead of directly calling draw_renderpass_impl!
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
draw_renderpass(command_buffer, render_target);
|
|
}
|
|
else
|
|
{
|
|
draw_renderpass(reinterpret_cast<vkb::core::CommandBufferC &>(command_buffer), reinterpret_cast<vkb::RenderTarget &>(render_target));
|
|
}
|
|
|
|
{
|
|
vkb::common::HPPImageMemoryBarrier memory_barrier{};
|
|
memory_barrier.old_layout = vk::ImageLayout::eColorAttachmentOptimal;
|
|
memory_barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
|
|
memory_barrier.src_access_mask = vk::AccessFlagBits::eColorAttachmentWrite;
|
|
memory_barrier.src_stage_mask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
memory_barrier.dst_stage_mask = vk::PipelineStageFlagBits::eBottomOfPipe;
|
|
|
|
command_buffer.image_memory_barrier(views[0], memory_barrier);
|
|
render_target.set_layout(0, memory_barrier.new_layout);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::draw_gui()
|
|
{
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::draw_renderpass(vkb::core::CommandBuffer<bindingType> &command_buffer, RenderTargetType &render_target)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
draw_renderpass_impl(command_buffer, render_target);
|
|
}
|
|
else
|
|
{
|
|
draw_renderpass_impl(reinterpret_cast<vkb::core::CommandBufferCpp &>(command_buffer),
|
|
reinterpret_cast<vkb::rendering::HPPRenderTarget &>(render_target));
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::draw_renderpass_impl(vkb::core::CommandBufferCpp &command_buffer,
|
|
vkb::rendering::HPPRenderTarget &render_target)
|
|
{
|
|
set_viewport_and_scissor_impl(command_buffer, render_target.get_extent());
|
|
|
|
// render is a virtual function, thus we have to call that, instead of directly calling render_impl!
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
render(command_buffer);
|
|
}
|
|
else
|
|
{
|
|
render(reinterpret_cast<vkb::core::CommandBufferC &>(command_buffer));
|
|
}
|
|
|
|
if (gui)
|
|
{
|
|
gui->draw(command_buffer);
|
|
}
|
|
|
|
command_buffer.get_handle().endRenderPass();
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::finish()
|
|
{
|
|
Parent::finish();
|
|
|
|
if (device)
|
|
{
|
|
device->get_handle().waitIdle();
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline Configuration &VulkanSample<bindingType>::get_configuration()
|
|
{
|
|
return configuration;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline vkb::core::Device<bindingType> const &VulkanSample<bindingType>::get_device() const
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *device;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::core::DeviceC const &>(*device);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline vkb::core::Device<bindingType> &VulkanSample<bindingType>::get_device()
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *device;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::core::DeviceC &>(*device);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::unordered_map<const char *, bool> const &VulkanSample<bindingType>::get_device_extensions() const
|
|
{
|
|
return device_extensions;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline vkb::Gui<bindingType> &VulkanSample<bindingType>::get_gui()
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *gui;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::GuiC &>(*gui);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline vkb::Gui<bindingType> const &VulkanSample<bindingType>::get_gui() const
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *gui;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::GuiC const &>(*gui);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::vector<typename VulkanSample<bindingType>::SurfaceFormatType> &VulkanSample<bindingType>::get_surface_priority_list()
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return surface_priority_list;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<std::vector<VkSurfaceFormatKHR> &>(surface_priority_list);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::vector<typename VulkanSample<bindingType>::SurfaceFormatType> const &VulkanSample<bindingType>::get_surface_priority_list() const
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return surface_priority_list;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<std::vector<VkSurfaceFormatKHR> const &>(surface_priority_list);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline vkb::core::Instance<bindingType> &VulkanSample<bindingType>::get_instance()
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *instance;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::core::InstanceC &>(*instance);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline vkb::core::Instance<bindingType> const &VulkanSample<bindingType>::get_instance() const
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *instance;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::core::InstanceC const &>(*instance);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::unordered_map<const char *, bool> const &VulkanSample<bindingType>::get_instance_extensions() const
|
|
{
|
|
return instance_extensions;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::unordered_map<const char *, bool> const &VulkanSample<bindingType>::get_instance_layers() const
|
|
{
|
|
return instance_layers;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::add_instance_layer(const char *layer, bool optional)
|
|
{
|
|
instance_layers[layer] = optional;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::vector<typename VulkanSample<bindingType>::LayerSettingType> const &VulkanSample<bindingType>::get_layer_settings() const
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return layer_settings;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<std::vector<VkLayerSettingEXT> const &>(layer_settings);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename VulkanSample<bindingType>::RenderContextType const &VulkanSample<bindingType>::get_render_context() const
|
|
{
|
|
assert(render_context && "Render context is not valid");
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return reinterpret_cast<vkb::rendering::HPPRenderContext const &>(*render_context);
|
|
}
|
|
else
|
|
{
|
|
return *render_context;
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename VulkanSample<bindingType>::RenderContextType &VulkanSample<bindingType>::get_render_context()
|
|
{
|
|
assert(render_context && "Render context is not valid");
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *render_context;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::RenderContext &>(*render_context);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename VulkanSample<bindingType>::RenderPipelineType const &VulkanSample<bindingType>::get_render_pipeline() const
|
|
{
|
|
assert(render_pipeline && "Render pipeline was not created");
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *render_pipeline;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::RenderPipeline const &>(*render_pipeline);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename VulkanSample<bindingType>::RenderPipelineType &VulkanSample<bindingType>::get_render_pipeline()
|
|
{
|
|
assert(render_pipeline && "Render pipeline was not created");
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *render_pipeline;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::RenderPipeline &>(*render_pipeline);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename VulkanSample<bindingType>::SceneType &VulkanSample<bindingType>::get_scene()
|
|
{
|
|
assert(scene && "Scene not loaded");
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *scene;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::sg::Scene &>(*scene);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename VulkanSample<bindingType>::StatsType &VulkanSample<bindingType>::get_stats()
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return *stats;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::Stats &>(*stats);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename VulkanSample<bindingType>::SurfaceType VulkanSample<bindingType>::get_surface() const
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return surface;
|
|
}
|
|
else
|
|
{
|
|
return static_cast<VkSurfaceKHR>(surface);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline bool VulkanSample<bindingType>::has_device() const
|
|
{
|
|
return device != nullptr;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline bool VulkanSample<bindingType>::has_instance() const
|
|
{
|
|
return instance != nullptr;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline bool VulkanSample<bindingType>::has_gui() const
|
|
{
|
|
return gui != nullptr;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline bool VulkanSample<bindingType>::has_render_context() const
|
|
{
|
|
return render_context != nullptr;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline bool VulkanSample<bindingType>::has_render_pipeline() const
|
|
{
|
|
return render_pipeline != nullptr;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
bool VulkanSample<bindingType>::has_scene()
|
|
{
|
|
return scene != nullptr;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::input_event(const InputEvent &input_event)
|
|
{
|
|
Parent::input_event(input_event);
|
|
|
|
bool gui_captures_event = false;
|
|
|
|
if (gui)
|
|
{
|
|
gui_captures_event = gui->input_event(input_event);
|
|
}
|
|
|
|
if (!gui_captures_event)
|
|
{
|
|
if (scene && scene->has_component<sg::Script>())
|
|
{
|
|
auto scripts = scene->get_components<sg::Script>();
|
|
|
|
for (auto script : scripts)
|
|
{
|
|
script->input_event(input_event);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (input_event.get_source() == EventSource::Keyboard)
|
|
{
|
|
const auto &key_event = static_cast<const KeyInputEvent &>(input_event);
|
|
if (key_event.get_action() == KeyAction::Down &&
|
|
(key_event.get_code() == KeyCode::PrintScreen || key_event.get_code() == KeyCode::F12))
|
|
{
|
|
vkb::common::screenshot(*render_context, "screenshot-" + get_name());
|
|
}
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::load_scene(const std::string &path)
|
|
{
|
|
vkb::HPPGLTFLoader loader(*device);
|
|
|
|
scene = loader.read_scene_from_file(path);
|
|
|
|
if (!scene)
|
|
{
|
|
LOGE("Cannot load scene: {}", path.c_str());
|
|
throw std::runtime_error("Cannot load scene: " + path);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline bool VulkanSample<bindingType>::prepare(const ApplicationOptions &options)
|
|
{
|
|
if (!Parent::prepare(options))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
LOGI("Initializing Vulkan sample");
|
|
|
|
// initialize C++-Bindings default dispatcher, first step
|
|
#if defined(_HPP_VULKAN_LIBRARY)
|
|
static vk::detail::DynamicLoader dl(_HPP_VULKAN_LIBRARY);
|
|
#else
|
|
static vk::detail::DynamicLoader dl;
|
|
#endif
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr"));
|
|
|
|
bool headless = window->get_window_mode() == Window::Mode::Headless;
|
|
|
|
// for a while we're running on mixed C- and C++-bindings, needing volk for the C-bindings!
|
|
VkResult result = volkInitialize();
|
|
if (result)
|
|
{
|
|
throw VulkanException(result, "Failed to initialize volk.");
|
|
}
|
|
|
|
// Creating the vulkan instance
|
|
for (const char *extension_name : window->get_required_surface_extensions())
|
|
{
|
|
add_instance_extension(extension_name);
|
|
}
|
|
|
|
#ifdef VKB_VULKAN_DEBUG
|
|
{
|
|
std::vector<vk::ExtensionProperties> available_instance_extensions = vk::enumerateInstanceExtensionProperties();
|
|
auto debugExtensionIt =
|
|
std::ranges::find_if(available_instance_extensions,
|
|
[](vk::ExtensionProperties const &ep) { return strcmp(ep.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0; });
|
|
if (debugExtensionIt != available_instance_extensions.end())
|
|
{
|
|
LOGI("Vulkan debug utils enabled ({})", VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
|
|
debug_utils = std::make_unique<vkb::core::HPPDebugUtilsExtDebugUtils>();
|
|
add_instance_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
instance = create_instance();
|
|
}
|
|
else
|
|
{
|
|
instance.reset(reinterpret_cast<vkb::core::InstanceCpp *>(create_instance().release()));
|
|
}
|
|
|
|
// initialize C++-Bindings default dispatcher, second step
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance->get_handle());
|
|
|
|
// Getting a valid vulkan surface from the platform
|
|
surface = static_cast<vk::SurfaceKHR>(window->create_surface(reinterpret_cast<vkb::core::InstanceC &>(*instance)));
|
|
if (!surface)
|
|
{
|
|
throw std::runtime_error("Failed to create window surface.");
|
|
}
|
|
|
|
auto &gpu = instance->get_suitable_gpu(surface, headless);
|
|
gpu.set_high_priority_graphics_queue_enable(high_priority_graphics_queue);
|
|
|
|
// Request to enable ASTC
|
|
if (gpu.get_features().textureCompressionASTC_LDR)
|
|
{
|
|
gpu.get_mutable_requested_features().textureCompressionASTC_LDR = true;
|
|
}
|
|
|
|
// Request sample required GPU features
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
request_gpu_features(gpu);
|
|
}
|
|
else
|
|
{
|
|
request_gpu_features(reinterpret_cast<vkb::PhysicalDevice &>(gpu));
|
|
}
|
|
|
|
// Creating vulkan device, specifying the swapchain extension always
|
|
// If using VK_EXT_headless_surface, we still create and use a swap-chain
|
|
{
|
|
add_device_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
|
|
|
if (instance_extensions.find(VK_KHR_DISPLAY_EXTENSION_NAME) != instance_extensions.end())
|
|
{
|
|
add_device_extension(VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME, /*optional=*/true);
|
|
}
|
|
}
|
|
|
|
#ifdef VKB_ENABLE_PORTABILITY
|
|
// VK_KHR_portability_subset must be enabled if present in the implementation (e.g on macOS/iOS with beta extensions enabled)
|
|
add_device_extension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME, /*optional=*/true);
|
|
#endif
|
|
|
|
#ifdef VKB_VULKAN_DEBUG
|
|
if (!debug_utils)
|
|
{
|
|
std::vector<vk::ExtensionProperties> available_device_extensions = gpu.get_handle().enumerateDeviceExtensionProperties();
|
|
auto debugExtensionIt =
|
|
std::ranges::find_if(available_device_extensions,
|
|
[](vk::ExtensionProperties const &ep) { return strcmp(ep.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME) == 0; });
|
|
if (debugExtensionIt != available_device_extensions.end())
|
|
{
|
|
LOGI("Vulkan debug utils enabled ({})", VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
|
|
|
debug_utils = std::make_unique<vkb::core::HPPDebugMarkerExtDebugUtils>();
|
|
add_device_extension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
|
}
|
|
}
|
|
|
|
if (!debug_utils)
|
|
{
|
|
LOGW("Vulkan debug utils were requested, but no extension that provides them was found");
|
|
}
|
|
#endif
|
|
|
|
if (!debug_utils)
|
|
{
|
|
debug_utils = std::make_unique<vkb::core::HPPDummyDebugUtils>();
|
|
}
|
|
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
device = create_device(gpu);
|
|
}
|
|
else
|
|
{
|
|
device.reset(reinterpret_cast<vkb::core::DeviceCpp *>(create_device(reinterpret_cast<vkb::PhysicalDevice &>(gpu)).release()));
|
|
}
|
|
|
|
// initialize C++-Bindings default dispatcher, optional third step
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(device->get_handle());
|
|
|
|
create_render_context();
|
|
prepare_render_context();
|
|
|
|
stats = std::make_unique<vkb::stats::HPPStats>(*render_context);
|
|
|
|
// Start the sample in the first GUI configuration
|
|
configuration.reset();
|
|
|
|
return true;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::create_gui(const Window &window, StatsType const *stats, const float font_size, bool explicit_update)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
gui = std::make_unique<vkb::GuiCpp>(get_render_context(), window, stats, font_size, explicit_update);
|
|
}
|
|
else
|
|
{
|
|
gui = std::make_unique<vkb::GuiCpp>(reinterpret_cast<vkb::rendering::HPPRenderContext &>(get_render_context()),
|
|
window,
|
|
reinterpret_cast<vkb::stats::HPPStats const *>(stats),
|
|
font_size,
|
|
explicit_update);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::prepare_render_context()
|
|
{
|
|
render_context->prepare();
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::render(vkb::core::CommandBuffer<bindingType> &command_buffer)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
render_impl(command_buffer);
|
|
}
|
|
else
|
|
{
|
|
render_impl(reinterpret_cast<vkb::core::CommandBufferCpp &>(command_buffer));
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::render_impl(vkb::core::CommandBufferCpp &command_buffer)
|
|
{
|
|
if (render_pipeline)
|
|
{
|
|
render_pipeline->draw(command_buffer, render_context->get_active_frame().get_render_target());
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::request_gpu_features(PhysicalDeviceType &gpu)
|
|
{
|
|
// To be overridden by sample
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::reset_stats_view()
|
|
{
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline bool VulkanSample<bindingType>::resize(uint32_t width, uint32_t height)
|
|
{
|
|
if (!Parent::resize(width, height))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (gui)
|
|
{
|
|
gui->resize(width, height);
|
|
}
|
|
|
|
if (scene && scene->has_component<sg::Script>())
|
|
{
|
|
auto scripts = scene->get_components<sg::Script>();
|
|
|
|
for (auto script : scripts)
|
|
{
|
|
script->resize(width, height);
|
|
}
|
|
}
|
|
|
|
if (stats)
|
|
{
|
|
stats->resize(width);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::set_api_version(uint32_t requested_api_version)
|
|
{
|
|
api_version = requested_api_version;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::set_high_priority_graphics_queue_enable(bool enable)
|
|
{
|
|
high_priority_graphics_queue = enable;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::set_render_context(std::unique_ptr<RenderContextType> &&rc)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
render_context.reset(rc.release());
|
|
}
|
|
else
|
|
{
|
|
render_context.reset(reinterpret_cast<vkb::rendering::HPPRenderContext *>(rc.release()));
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::set_render_pipeline(std::unique_ptr<RenderPipelineType> &&rp)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
render_pipeline.reset(rp.release());
|
|
}
|
|
else
|
|
{
|
|
render_pipeline.reset(reinterpret_cast<vkb::rendering::HPPRenderPipeline *>(rp.release()));
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::set_viewport_and_scissor(vkb::core::CommandBuffer<bindingType> const &command_buffer, Extent2DType const &extent)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
set_viewport_and_scissor_impl(command_buffer, extent);
|
|
}
|
|
else
|
|
{
|
|
set_viewport_and_scissor_impl(reinterpret_cast<vkb::core::CommandBufferCpp const &>(command_buffer),
|
|
reinterpret_cast<vk::Extent2D const &>(extent));
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::set_viewport_and_scissor_impl(vkb::core::CommandBufferCpp const &command_buffer,
|
|
vk::Extent2D const &extent)
|
|
{
|
|
command_buffer.get_handle().setViewport(0, {{0.0f, 0.0f, static_cast<float>(extent.width), static_cast<float>(extent.height), 0.0f, 1.0f}});
|
|
command_buffer.get_handle().setScissor(0, vk::Rect2D{{}, extent});
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::update(float delta_time)
|
|
{
|
|
vkb::Application::update(delta_time);
|
|
|
|
update_scene(delta_time);
|
|
|
|
update_gui(delta_time);
|
|
|
|
auto command_buffer = render_context->begin();
|
|
|
|
// Collect the performance data for the sample graphs
|
|
update_stats(delta_time);
|
|
|
|
command_buffer->begin(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
|
|
stats->begin_sampling(*command_buffer);
|
|
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
draw(*command_buffer, render_context->get_active_frame().get_render_target());
|
|
}
|
|
else
|
|
{
|
|
draw(reinterpret_cast<vkb::core::CommandBufferC &>(*command_buffer),
|
|
reinterpret_cast<vkb::RenderTarget &>(render_context->get_active_frame().get_render_target()));
|
|
}
|
|
|
|
stats->end_sampling(*command_buffer);
|
|
command_buffer->end();
|
|
|
|
render_context->submit(*command_buffer);
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::update_debug_window()
|
|
{
|
|
auto driver_version = device->get_gpu().get_driver_version();
|
|
std::string driver_version_str = fmt::format("major: {} minor: {} patch: {}", driver_version.major, driver_version.minor, driver_version.patch);
|
|
|
|
get_debug_info().template insert<field::Static, std::string>("driver_version", driver_version_str);
|
|
get_debug_info().template insert<field::Static, std::string>("resolution",
|
|
to_string(static_cast<VkExtent2D const &>(render_context->get_swapchain().get_extent())));
|
|
get_debug_info().template insert<field::Static, std::string>("surface_format",
|
|
to_string(render_context->get_swapchain().get_format()) + " (" +
|
|
to_string(vkb::common::get_bits_per_pixel(render_context->get_swapchain().get_format())) +
|
|
"bpp)");
|
|
|
|
if (scene != nullptr)
|
|
{
|
|
get_debug_info().template insert<field::Static, uint32_t>("mesh_count", to_u32(scene->get_components<sg::SubMesh>().size()));
|
|
get_debug_info().template insert<field::Static, uint32_t>("texture_count", to_u32(scene->get_components<sg::Texture>().size()));
|
|
|
|
if (auto camera = scene->get_components<vkb::sg::Camera>()[0])
|
|
{
|
|
if (auto camera_node = camera->get_node())
|
|
{
|
|
const glm::vec3 &pos = camera_node->get_transform().get_translation();
|
|
get_debug_info().template insert<field::Vector, float>("camera_pos", pos.x, pos.y, pos.z);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::update_gui(float delta_time)
|
|
{
|
|
if (gui)
|
|
{
|
|
if (gui->is_debug_view_active())
|
|
{
|
|
update_debug_window();
|
|
}
|
|
|
|
gui->new_frame();
|
|
|
|
gui->show_top_window(get_name(), stats.get(), &get_debug_info());
|
|
|
|
// Samples can override this
|
|
draw_gui();
|
|
|
|
gui->update(delta_time);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::update_scene(float delta_time)
|
|
{
|
|
if (scene)
|
|
{
|
|
// Update scripts
|
|
if (scene->has_component<sg::Script>())
|
|
{
|
|
auto scripts = scene->get_components<sg::Script>();
|
|
|
|
for (auto script : scripts)
|
|
{
|
|
script->update(delta_time);
|
|
}
|
|
}
|
|
|
|
// Update animations
|
|
if (scene->has_component<sg::Animation>())
|
|
{
|
|
auto animations = scene->get_components<sg::Animation>();
|
|
|
|
for (auto animation : animations)
|
|
{
|
|
animation->update(delta_time);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void VulkanSample<bindingType>::update_stats(float delta_time)
|
|
{
|
|
if (stats)
|
|
{
|
|
stats->update(delta_time);
|
|
|
|
static float stats_view_count = 0.0f;
|
|
stats_view_count += delta_time;
|
|
|
|
// Reset every STATS_VIEW_RESET_TIME seconds
|
|
if (stats_view_count > STATS_VIEW_RESET_TIME)
|
|
{
|
|
reset_stats_view();
|
|
stats_view_count = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace vkb
|