Files
Vulkan-Samples/framework/core/command_buffer.h
T
2025-09-04 10:54:47 +08:00

1533 lines
70 KiB
C++

/* Copyright (c) 2019-2025, Arm Limited and Contributors
* Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/hpp_vk_common.h"
#include "core/device.h"
#include "core/hpp_descriptor_set_layout.h"
#include "core/hpp_framebuffer.h"
#include "core/hpp_physical_device.h"
#include "core/hpp_render_pass.h"
#include "core/hpp_sampler.h"
#include "core/image.h"
#include "core/physical_device.h"
#include "hpp_resource_binding_state.h"
#include "rendering/hpp_pipeline_state.h"
#include "rendering/hpp_render_target.h"
#include "rendering/subpass.h"
namespace vkb
{
class QueryPool;
namespace core
{
class HPPQueryPool;
/**
* @brief Helper class to manage and record a command buffer, building and
* keeping track of pipeline state and resource bindings
*/
template <vkb::BindingType bindingType>
class CommandBuffer
: public vkb::core::VulkanResource<bindingType, typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBuffer, VkCommandBuffer>::type>
{
using ParentType =
vkb::core::VulkanResource<bindingType, typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBuffer, VkCommandBuffer>::type>;
public:
using BufferImageCopyType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferImageCopy, VkBufferImageCopy>::type;
using ClearAttachmentType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ClearAttachment, VkClearAttachment>::type;
using ClearRectType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ClearRect, VkClearRect>::type;
using ClearValueType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ClearValue, VkClearValue>::type;
using CommandBufferLevelType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBufferLevel, VkCommandBufferLevel>::type;
using CommandBufferType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBuffer, VkCommandBuffer>::type;
using CommandBufferUsageFlagsType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBufferUsageFlags, VkCommandBufferUsageFlags>::type;
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
using ImageBlitType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ImageBlit, VkImageBlit>::type;
using ImageCopyType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ImageCopy, VkImageCopy>::type;
using ImageLayoutType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ImageLayout, VkImageLayout>::type;
using ImageResolveType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ImageResolve, VkImageResolve>::type;
using IndexTypeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::IndexType, VkIndexType>::type;
using PipelineStagFlagBitsType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::PipelineStageFlagBits, VkPipelineStageFlagBits>::type;
using QueryControlFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::QueryControlFlags, VkQueryControlFlags>::type;
using Rect2DType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Rect2D, VkRect2D>::type;
using ResultType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Result, VkResult>::type;
using SubpassContentsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SubpassContents, VkSubpassContents>::type;
using ViewportType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Viewport, VkViewport>::type;
using BufferMemoryBarrierType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::common::HPPBufferMemoryBarrier, vkb::BufferMemoryBarrier>::type;
using ColorBlendStateType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPColorBlendState, vkb::ColorBlendState>::type;
using DepthStencilStateType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPDepthStencilState, vkb::DepthStencilState>::type;
using FramebufferType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPFramebuffer, vkb::Framebuffer>::type;
using ImageMemoryBarrierType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::common::HPPImageMemoryBarrier, vkb::ImageMemoryBarrier>::type;
using ImageType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPImage, vkb::core::Image>::type;
using ImageViewType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPImageView, vkb::core::ImageView>::type;
using InputAssemblyStateType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPInputAssemblyState, vkb::InputAssemblyState>::type;
using LoadStoreInfoType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::common::HPPLoadStoreInfo, vkb::LoadStoreInfo>::type;
using MultisampleStateType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPMultisampleState, vkb::MultisampleState>::type;
using PipelineLayoutType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPPipelineLayout, vkb::PipelineLayout>::type;
using QueryPoolType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPQueryPool, vkb::QueryPool>::type;
using RasterizationStateType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPRasterizationState, vkb::RasterizationState>::type;
using RenderPassType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPRenderPass, vkb::RenderPass>::type;
using RenderTargetType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPRenderTarget, vkb::RenderTarget>::type;
using SamplerType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPSampler, vkb::core::Sampler>::type;
using VertexInputStateType =
typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPVertexInputState, vkb::VertexInputState>::type;
using ViewportStateType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::rendering::HPPViewportState, vkb::ViewportState>::type;
public:
CommandBuffer(vkb::core::CommandPool<bindingType> &command_pool, CommandBufferLevelType level);
~CommandBuffer();
CommandBuffer(CommandBuffer<bindingType> &&other) = default;
CommandBuffer &operator=(CommandBuffer<bindingType> &&) = default;
CommandBuffer(CommandBuffer<bindingType> const &) = delete;
CommandBuffer &operator=(CommandBuffer<bindingType> const &) = delete;
/**
* @brief Sets the command buffer so that it is ready for recording
* If it is a secondary command buffer, a pointer to the
* primary command buffer it inherits from must be provided
* @param flags Usage behavior for the command buffer
* @param primary_cmd_buf (optional)
*/
void begin(CommandBufferUsageFlagsType flags, CommandBuffer<bindingType> *primary_cmd_buf = nullptr);
/**
* @brief Sets the command buffer so that it is ready for recording
* If it is a secondary command buffer, pointers to the
* render pass and framebuffer as well as subpass index must be provided
* @param flags Usage behavior for the command buffer
* @param render_pass
* @param framebuffer
* @param subpass_index
*/
void begin(CommandBufferUsageFlagsType flags, const RenderPassType *render_pass, const FramebufferType *framebuffer, uint32_t subpass_index);
void begin_query(QueryPoolType const &query_pool, uint32_t query, QueryControlFlagsType flags);
void begin_render_pass(RenderTargetType const &render_target,
std::vector<LoadStoreInfoType> const &load_store_infos,
std::vector<ClearValueType> const &clear_values,
std::vector<std::unique_ptr<vkb::rendering::Subpass<bindingType>>> const &subpasses,
SubpassContentsType contents = VK_SUBPASS_CONTENTS_INLINE);
void begin_render_pass(RenderTargetType const &render_target,
RenderPassType const &render_pass,
FramebufferType const &framebuffer,
std::vector<ClearValueType> const &clear_values,
SubpassContentsType contents = vk::SubpassContents::eInline);
void bind_buffer(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, DeviceSizeType range, uint32_t set, uint32_t binding, uint32_t array_element);
void bind_image(ImageViewType const &image_view, SamplerType const &sampler, uint32_t set, uint32_t binding, uint32_t array_element);
void bind_image(ImageViewType const &image_view, uint32_t set, uint32_t binding, uint32_t array_element);
void bind_index_buffer(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, IndexTypeType index_type);
void bind_input(ImageViewType const &image_view, uint32_t set, uint32_t binding, uint32_t array_element);
void bind_lighting(vkb::rendering::LightingState<bindingType> &lighting_state, uint32_t set, uint32_t binding);
void bind_pipeline_layout(PipelineLayoutType &pipeline_layout);
void bind_vertex_buffers(uint32_t first_binding,
std::vector<std::reference_wrapper<const vkb::core::Buffer<bindingType>>> const &buffers,
std::vector<DeviceSizeType> const &offsets);
void blit_image(ImageType const &src_img, ImageType const &dst_img, std::vector<ImageBlitType> const &regions);
void buffer_memory_barrier(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, DeviceSizeType size, BufferMemoryBarrierType const &memory_barrier);
void clear(ClearAttachmentType const &info, ClearRectType const &rect);
void copy_buffer(vkb::core::Buffer<bindingType> const &src_buffer, vkb::core::Buffer<bindingType> const &dst_buffer, DeviceSizeType size);
void copy_buffer_to_image(vkb::core::Buffer<bindingType> const &buffer, ImageType const &image, std::vector<BufferImageCopyType> const &regions);
void copy_image(ImageType const &src_img, ImageType const &dst_img, std::vector<ImageCopyType> const &regions);
void copy_image_to_buffer(ImageType const &image,
ImageLayoutType image_layout,
vkb::core::Buffer<bindingType> const &buffer,
std::vector<BufferImageCopyType> const &regions);
void dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z);
void dispatch_indirect(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset);
void draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance);
void draw_indexed(uint32_t index_count, uint32_t instance_count, uint32_t first_index, int32_t vertex_offset, uint32_t first_instance);
void draw_indexed_indirect(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, uint32_t draw_count, uint32_t stride);
void end();
void end_query(QueryPoolType const &query_pool, uint32_t query);
void end_render_pass();
void execute_commands(vkb::core::CommandBuffer<bindingType> &secondary_command_buffer);
void execute_commands(std::vector<std::shared_ptr<vkb::core::CommandBuffer<bindingType>>> &secondary_command_buffers);
CommandBufferLevelType get_level() const;
RenderPassType &get_render_pass(RenderTargetType const &render_target,
std::vector<LoadStoreInfoType> const &load_store_infos,
std::vector<std::unique_ptr<vkb::rendering::Subpass<bindingType>>> const &subpasses);
void image_memory_barrier(ImageViewType const &image_view, ImageMemoryBarrierType const &memory_barrier) const;
void image_memory_barrier(RenderTargetType &render_target, uint32_t view_index, ImageMemoryBarrierType const &memory_barrier) const;
void next_subpass();
/**
* @brief Records byte data into the command buffer to be pushed as push constants to each draw call
* @param values The byte data to store
*/
void push_constants(const std::vector<uint8_t> &values);
template <typename T>
void push_constants(const T &value);
/**
* @brief Reset the command buffer to a state where it can be recorded to
* @param reset_mode How to reset the buffer, should match the one used by the pool to allocate it
*/
ResultType reset(vkb::CommandBufferResetMode reset_mode);
void reset_query_pool(QueryPoolType const &query_pool, uint32_t first_query, uint32_t query_count);
void resolve_image(ImageType const &src_img, ImageType const &dst_img, std::vector<ImageResolveType> const &regions);
void set_blend_constants(std::array<float, 4> const &blend_constants);
void set_color_blend_state(ColorBlendStateType const &state_info);
void set_depth_bias(float depth_bias_constant_factor, float depth_bias_clamp, float depth_bias_slope_factor);
void set_depth_bounds(float min_depth_bounds, float max_depth_bounds);
void set_depth_stencil_state(DepthStencilStateType const &state_info);
void set_input_assembly_state(InputAssemblyStateType const &state_info);
void set_line_width(float line_width);
void set_multisample_state(MultisampleStateType const &state_info);
void set_rasterization_state(RasterizationStateType const &state_info);
void set_scissor(uint32_t first_scissor, std::vector<Rect2DType> const &scissors);
template <class T>
void set_specialization_constant(uint32_t constant_id, T const &data);
void set_specialization_constant(uint32_t constant_id, std::vector<uint8_t> const &data);
void set_update_after_bind(bool update_after_bind_);
void set_vertex_input_state(VertexInputStateType const &state_info);
void set_viewport(uint32_t first_viewport, std::vector<ViewportType> const &viewports);
void set_viewport_state(ViewportStateType const &state_info);
void update_buffer(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, std::vector<uint8_t> const &data);
void write_timestamp(PipelineStagFlagBitsType pipeline_stage, QueryPoolType const &query_pool, uint32_t query);
private:
/**
* @brief Flushes the command buffer, pushing the new changes
*/
void flush(vk::PipelineBindPoint pipeline_bind_point);
/**
* @brief Flush the push constant state
*/
void flush_push_constants();
/**
* @brief Check that the render area is an optimal size by comparing to the render area granularity
*/
bool is_render_size_optimal(const vk::Extent2D &extent, const vk::Rect2D &render_area);
private:
void begin_impl(vk::CommandBufferUsageFlags flags, vkb::core::CommandBuffer<bindingType> *primary_cmd_buf);
void begin_impl(vk::CommandBufferUsageFlags flags,
vkb::core::HPPRenderPass const *render_pass,
vkb::core::HPPFramebuffer const *framebuffer,
uint32_t subpass_index);
void begin_render_pass_impl(vkb::rendering::HPPRenderTarget const &render_target,
vkb::core::HPPRenderPass const &render_pass,
vkb::core::HPPFramebuffer const &framebuffer,
std::vector<vk::ClearValue> const &clear_values,
vk::SubpassContents contents);
void bind_vertex_buffers_impl(uint32_t first_binding,
std::vector<std::reference_wrapper<const vkb::core::BufferCpp>> const &buffers,
std::vector<vk::DeviceSize> const &offsets);
void buffer_memory_barrier_impl(vkb::core::BufferCpp const &buffer,
vk::DeviceSize offset,
vk::DeviceSize size,
vkb::common::HPPBufferMemoryBarrier const &memory_barrier);
void copy_buffer_impl(vkb::core::BufferCpp const &src_buffer, vkb::core::BufferCpp const &dst_buffer, vk::DeviceSize size);
void execute_commands_impl(std::vector<std::shared_ptr<vkb::core::CommandBuffer<vkb::BindingType::Cpp>>> &secondary_command_buffers);
void flush_impl(vkb::core::DeviceCpp &device, vk::PipelineBindPoint pipeline_bind_point);
void flush_descriptor_state_impl(vk::PipelineBindPoint pipeline_bind_point);
void flush_pipeline_state_impl(vkb::core::DeviceCpp &device, vk::PipelineBindPoint pipeline_bind_point);
vkb::core::HPPRenderPass &get_render_pass_impl(vkb::core::DeviceCpp &device,
vkb::rendering::HPPRenderTarget const &render_target,
std::vector<vkb::common::HPPLoadStoreInfo> const &load_store_infos,
std::vector<std::unique_ptr<vkb::rendering::SubpassCpp>> const &subpasses);
void image_memory_barrier_impl(vkb::core::HPPImageView const &image_view, vkb::common::HPPImageMemoryBarrier const &memory_barrier) const;
vk::Result reset_impl(vkb::CommandBufferResetMode reset_mode);
private:
vkb::core::CommandPoolCpp &command_pool;
vkb::core::HPPFramebuffer const *current_framebuffer = nullptr;
vkb::core::HPPRenderPass const *current_render_pass = nullptr;
std::unordered_map<uint32_t, vkb::core::HPPDescriptorSetLayout const *> descriptor_set_layout_binding_state;
vk::Extent2D last_framebuffer_extent = {};
vk::Extent2D last_render_area_extent = {};
const vk::CommandBufferLevel level = {};
const uint32_t max_push_constants_size = {};
vkb::rendering::HPPPipelineState pipeline_state = {};
vkb::HPPResourceBindingState resource_binding_state = {};
std::vector<uint8_t> stored_push_constants = {};
// If true, it becomes the responsibility of the caller to update ANY descriptor bindings
// that contain update after bind, as they wont be implicitly updated
bool update_after_bind = false;
};
using CommandBufferC = CommandBuffer<vkb::BindingType::C>;
using CommandBufferCpp = CommandBuffer<vkb::BindingType::Cpp>;
} // namespace core
} // namespace vkb
#include "core/command_pool.h"
#include "core/device.h"
#include "rendering/render_frame.h"
namespace vkb
{
namespace core
{
template <vkb::BindingType bindingType>
inline vkb::core::CommandBuffer<bindingType>::CommandBuffer(vkb::core::CommandPool<bindingType> &command_pool_, CommandBufferLevelType level_) :
vkb::core::VulkanResource<bindingType, CommandBufferType>(nullptr, &command_pool_.get_device()), level(static_cast<vk::CommandBufferLevel>(level_)), command_pool(reinterpret_cast<vkb::core::CommandPoolCpp &>(command_pool_)), max_push_constants_size(command_pool_.get_device().get_gpu().get_properties().limits.maxPushConstantsSize)
{
vk::CommandBufferAllocateInfo allocate_info{.commandPool = command_pool.get_handle(), .level = level, .commandBufferCount = 1};
this->set_handle(this->get_device().get_resource().allocateCommandBuffers(allocate_info).front());
}
template <vkb::BindingType bindingType>
inline CommandBuffer<bindingType>::~CommandBuffer()
{
// Destroy command buffer
if (this->has_handle())
{
this->get_device().get_resource().freeCommandBuffers(command_pool.get_handle(), this->get_resource());
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::begin(CommandBufferUsageFlagsType flags,
vkb::core::CommandBuffer<bindingType> *primary_cmd_buf)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
begin_impl(flags, primary_cmd_buf);
}
else
{
begin_impl(static_cast<vk::CommandBufferUsageFlags>(flags), primary_cmd_buf);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::begin_impl(vk::CommandBufferUsageFlags flags, vkb::core::CommandBuffer<bindingType> *primary_cmd_buf)
{
if (level == vk::CommandBufferLevel::eSecondary)
{
assert(primary_cmd_buf && "A primary command buffer pointer must be provided when calling begin from a secondary one");
return begin_impl(flags, primary_cmd_buf->current_render_pass, primary_cmd_buf->current_framebuffer, primary_cmd_buf->pipeline_state.get_subpass_index());
}
else
{
return begin_impl(flags, nullptr, nullptr, 0);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::begin(CommandBufferUsageFlagsType flags,
const RenderPassType *render_pass,
const FramebufferType *framebuffer,
uint32_t subpass_index)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
begin_impl(flags, render_pass, framebuffer, subpass_index);
}
else
{
begin_impl(static_cast<vk::CommandBufferUsageFlags>(flags),
reinterpret_cast<vkb::core::HPPRenderPass const *>(render_pass),
reinterpret_cast<vkb::core::HPPFramebuffer const *>(framebuffer),
subpass_index);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::begin_impl(vk::CommandBufferUsageFlags flags,
vkb::core::HPPRenderPass const *render_pass,
vkb::core::HPPFramebuffer const *framebuffer,
uint32_t subpass_index)
{
// Reset state
pipeline_state.reset();
resource_binding_state.reset();
descriptor_set_layout_binding_state.clear();
stored_push_constants.clear();
vk::CommandBufferBeginInfo begin_info{.flags = flags};
vk::CommandBufferInheritanceInfo inheritance;
if (level == vk::CommandBufferLevel::eSecondary)
{
assert((render_pass && framebuffer) && "Render pass and framebuffer must be provided when calling begin from a secondary one");
current_render_pass = render_pass;
current_framebuffer = framebuffer;
inheritance.renderPass = current_render_pass->get_handle();
inheritance.framebuffer = current_framebuffer->get_handle();
inheritance.subpass = subpass_index;
begin_info.pInheritanceInfo = &inheritance;
}
this->get_resource().begin(begin_info);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::begin_query(QueryPoolType const &query_pool, uint32_t query, QueryControlFlagsType flags)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().beginQuery(query_pool.get_resource(), query, flags);
}
else
{
this->get_resource().beginQuery(static_cast<vk::QueryPool const>(query_pool.get_handle()), query, static_cast<vk::QueryControlFlags>(flags));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::begin_render_pass(RenderTargetType const &render_target,
std::vector<LoadStoreInfoType> const &load_store_infos,
std::vector<ClearValueType> const &clear_values,
std::vector<std::unique_ptr<vkb::rendering::Subpass<bindingType>>> const &subpasses,
SubpassContentsType contents)
{
// Reset state
pipeline_state.reset();
resource_binding_state.reset();
descriptor_set_layout_binding_state.clear();
auto &render_pass = get_render_pass(render_target, load_store_infos, subpasses);
auto &framebuffer = this->get_device().get_resource_cache().request_framebuffer(render_target, render_pass);
begin_render_pass(render_target, render_pass, framebuffer, clear_values, contents);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::begin_render_pass(RenderTargetType const &render_target,
RenderPassType const &render_pass,
FramebufferType const &framebuffer,
std::vector<ClearValueType> const &clear_values,
SubpassContentsType contents)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
begin_render_pass_impl(render_target, render_pass, framebuffer, clear_values, contents);
}
else
{
begin_render_pass_impl(reinterpret_cast<vkb::rendering::HPPRenderTarget const &>(render_target),
reinterpret_cast<vkb::core::HPPRenderPass const &>(render_pass),
reinterpret_cast<vkb::core::HPPFramebuffer const &>(framebuffer),
reinterpret_cast<std::vector<vk::ClearValue> const &>(clear_values),
static_cast<vk::SubpassContents>(contents));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::begin_render_pass_impl(vkb::rendering::HPPRenderTarget const &render_target,
vkb::core::HPPRenderPass const &render_pass,
vkb::core::HPPFramebuffer const &framebuffer,
std::vector<vk::ClearValue> const &clear_values,
vk::SubpassContents contents)
{
current_render_pass = &render_pass;
current_framebuffer = &framebuffer;
// Begin render pass
vk::RenderPassBeginInfo begin_info{.renderPass = current_render_pass->get_handle(),
.framebuffer = current_framebuffer->get_handle(),
.renderArea = {.extent = render_target.get_extent()},
.clearValueCount = static_cast<uint32_t>(clear_values.size()),
.pClearValues = clear_values.data()};
const auto &framebuffer_extent = current_framebuffer->get_extent();
// Test the requested render area to confirm that it is optimal and could not cause a performance reduction
if (!is_render_size_optimal(framebuffer_extent, begin_info.renderArea))
{
// Only prints the warning if the framebuffer or render area are different since the last time the render size was not optimal
if ((framebuffer_extent != last_framebuffer_extent) || (begin_info.renderArea.extent != last_render_area_extent))
{
LOGW("Render target extent is not an optimal size, this may result in reduced performance.");
}
last_framebuffer_extent = framebuffer_extent;
last_render_area_extent = begin_info.renderArea.extent;
}
this->get_resource().beginRenderPass(begin_info, contents);
// Update blend state attachments for first subpass
auto blend_state = pipeline_state.get_color_blend_state();
blend_state.attachments.resize(current_render_pass->get_color_output_count(pipeline_state.get_subpass_index()));
pipeline_state.set_color_blend_state(blend_state);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_buffer(
vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, DeviceSizeType range, uint32_t set, uint32_t binding, uint32_t array_element)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
resource_binding_state.bind_buffer(buffer, offset, range, set, binding, array_element);
}
else
{
resource_binding_state.bind_buffer(
reinterpret_cast<vkb::core::Buffer<vkb::BindingType::Cpp> const &>(buffer), offset, range, set, binding, array_element);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_image(
ImageViewType const &image_view, SamplerType const &sampler, uint32_t set, uint32_t binding, uint32_t array_element)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
resource_binding_state.bind_image(image_view, sampler, set, binding, array_element);
}
else
{
resource_binding_state.bind_image(reinterpret_cast<vkb::core::HPPImageView const &>(image_view),
reinterpret_cast<vkb::core::HPPSampler const &>(sampler),
set,
binding,
array_element);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_image(ImageViewType const &image_view, uint32_t set, uint32_t binding, uint32_t array_element)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
resource_binding_state.bind_image(image_view, set, binding, array_element);
}
else
{
resource_binding_state.bind_image(reinterpret_cast<vkb::core::HPPImageView const &>(image_view), set, binding, array_element);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_index_buffer(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, IndexTypeType index_type)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().bindIndexBuffer(buffer.get_resource(), offset, index_type);
}
else
{
this->get_resource().bindIndexBuffer(buffer.get_resource(), static_cast<vk::DeviceSize>(offset), static_cast<vk::IndexType>(index_type));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_input(ImageViewType const &image_view, uint32_t set, uint32_t binding, uint32_t array_element)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
resource_binding_state.bind_input(image_view, set, binding, array_element);
}
else
{
resource_binding_state.bind_input(reinterpret_cast<vkb::core::HPPImageView const &>(image_view), set, binding, array_element);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_lighting(vkb::rendering::LightingState<bindingType> &lighting_state, uint32_t set, uint32_t binding)
{
bind_buffer(lighting_state.light_buffer.get_buffer(), lighting_state.light_buffer.get_offset(), lighting_state.light_buffer.get_size(), set, binding, 0);
set_specialization_constant(0, to_u32(lighting_state.directional_lights.size()));
set_specialization_constant(1, to_u32(lighting_state.point_lights.size()));
set_specialization_constant(2, to_u32(lighting_state.spot_lights.size()));
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_pipeline_layout(PipelineLayoutType &pipeline_layout)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
pipeline_state.set_pipeline_layout(pipeline_layout);
}
else
{
pipeline_state.set_pipeline_layout(reinterpret_cast<vkb::core::HPPPipelineLayout &>(pipeline_layout));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_vertex_buffers(uint32_t first_binding,
std::vector<std::reference_wrapper<const vkb::core::Buffer<bindingType>>> const &buffers,
std::vector<DeviceSizeType> const &offsets)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
bind_vertex_buffers_impl(first_binding, buffers, offsets);
}
else
{
bind_vertex_buffers_impl(first_binding,
reinterpret_cast<std::vector<std::reference_wrapper<vkb::core::BufferCpp const>> const &>(buffers),
reinterpret_cast<std::vector<vk::DeviceSize> const &>(offsets));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::bind_vertex_buffers_impl(uint32_t first_binding,
std::vector<std::reference_wrapper<const vkb::core::BufferCpp>> const &buffers,
std::vector<vk::DeviceSize> const &offsets)
{
std::vector<vk::Buffer> buffer_handles(buffers.size(), nullptr);
std::transform(buffers.begin(), buffers.end(), buffer_handles.begin(), [](auto const &buffer) { return buffer.get().get_handle(); });
this->get_resource().bindVertexBuffers(first_binding, buffer_handles, offsets);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::blit_image(ImageType const &src_img, ImageType const &dst_img, std::vector<ImageBlitType> const &regions)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().blitImage(src_img.get_handle(),
vk::ImageLayout::eTransferSrcOptimal,
dst_img.get_handle(),
vk::ImageLayout::eTransferDstOptimal,
regions,
vk::Filter::eNearest);
}
else
{
this->get_resource().blitImage(src_img.get_resource(),
vk::ImageLayout::eTransferSrcOptimal,
dst_img.get_resource(),
vk::ImageLayout::eTransferDstOptimal,
reinterpret_cast<std::vector<vk::ImageBlit> const &>(regions),
vk::Filter::eNearest);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::buffer_memory_barrier(vkb::core::Buffer<bindingType> const &buffer,
DeviceSizeType offset,
DeviceSizeType size,
BufferMemoryBarrierType const &memory_barrier)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
buffer_memory_barrier_impl(buffer, offset, size, memory_barrier);
}
else
{
buffer_memory_barrier_impl(reinterpret_cast<vkb::core::BufferCpp const &>(buffer),
static_cast<vk::DeviceSize>(offset),
static_cast<vk::DeviceSize>(size),
reinterpret_cast<vkb::common::HPPBufferMemoryBarrier const &>(memory_barrier));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::buffer_memory_barrier_impl(vkb::core::BufferCpp const &buffer,
vk::DeviceSize offset,
vk::DeviceSize size,
vkb::common::HPPBufferMemoryBarrier const &memory_barrier)
{
vk::BufferMemoryBarrier buffer_memory_barrier{.srcAccessMask = memory_barrier.src_access_mask,
.dstAccessMask = memory_barrier.dst_access_mask,
.buffer = buffer.get_handle(),
.offset = offset,
.size = size};
this->get_resource().pipelineBarrier(memory_barrier.src_stage_mask, memory_barrier.dst_stage_mask, {}, {}, buffer_memory_barrier, {});
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::clear(ClearAttachmentType const &attachment, ClearRectType const &rect)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().clearAttachments(attachment, rect);
}
else
{
this->get_resource().clearAttachments(reinterpret_cast<vk::ClearAttachment const &>(attachment), reinterpret_cast<vk::ClearRect const &>(rect));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::copy_buffer(vkb::core::Buffer<bindingType> const &src_buffer,
vkb::core::Buffer<bindingType> const &dst_buffer,
DeviceSizeType size)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
copy_buffer_impl(src_buffer, dst_buffer, size);
}
else
{
copy_buffer_impl(reinterpret_cast<vkb::core::BufferCpp const &>(src_buffer),
reinterpret_cast<vkb::core::BufferCpp const &>(dst_buffer),
static_cast<vk::DeviceSize>(size));
}
}
template <vkb::BindingType bindingType>
inline void
CommandBuffer<bindingType>::copy_buffer_impl(vkb::core::BufferCpp const &src_buffer, vkb::core::BufferCpp const &dst_buffer, vk::DeviceSize size)
{
vk::BufferCopy copy_region{.size = size};
this->get_resource().copyBuffer(src_buffer.get_handle(), dst_buffer.get_handle(), copy_region);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::copy_buffer_to_image(vkb::core::Buffer<bindingType> const &buffer,
ImageType const &image,
std::vector<BufferImageCopyType> const &regions)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().copyBufferToImage(buffer.get_handle(), image.get_handle(), vk::ImageLayout::eTransferDstOptimal, regions);
}
else
{
this->get_resource().copyBufferToImage(buffer.get_resource(),
image.get_resource(),
vk::ImageLayout::eTransferDstOptimal,
reinterpret_cast<std::vector<vk::BufferImageCopy> const &>(regions));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::copy_image(ImageType const &src_img, ImageType const &dst_img, std::vector<ImageCopyType> const &regions)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().copyImage(src_img.get_handle(), vk::ImageLayout::eTransferSrcOptimal, dst_img.get_handle(), vk::ImageLayout::eTransferDstOptimal, regions);
}
else
{
this->get_resource().copyImage(src_img.get_resource(),
vk::ImageLayout::eTransferSrcOptimal,
dst_img.get_resource(),
vk::ImageLayout::eTransferDstOptimal,
reinterpret_cast<std::vector<vk::ImageCopy> const &>(regions));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::copy_image_to_buffer(ImageType const &image,
ImageLayoutType image_layout,
vkb::core::Buffer<bindingType> const &buffer,
std::vector<BufferImageCopyType> const &regions)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().copyImageToBuffer(image.get_handle(), image_layout, buffer.get_handle(), regions);
}
else
{
this->get_resource().copyImageToBuffer(image.get_resource(),
static_cast<vk::ImageLayout>(image_layout),
buffer.get_resource(),
reinterpret_cast<std::vector<vk::BufferImageCopy> const &>(regions));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z)
{
flush(vk::PipelineBindPoint::eCompute);
this->get_resource().dispatch(group_count_x, group_count_y, group_count_z);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::dispatch_indirect(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset)
{
flush(vk::PipelineBindPoint::eCompute);
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().dispatchIndirect(buffer.get_handle(), offset);
}
else
{
this->get_resource().dispatchIndirect(buffer.get_resource(), static_cast<vk::DeviceSize>(offset));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance)
{
flush(vk::PipelineBindPoint::eGraphics);
this->get_resource().draw(vertex_count, instance_count, first_vertex, first_instance);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::draw_indexed(
uint32_t index_count, uint32_t instance_count, uint32_t first_index, int32_t vertex_offset, uint32_t first_instance)
{
flush(vk::PipelineBindPoint::eGraphics);
this->get_resource().drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::draw_indexed_indirect(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, uint32_t draw_count, uint32_t stride)
{
flush(vk::PipelineBindPoint::eGraphics);
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().drawIndexedIndirect(buffer.get_handle(), offset, draw_count, stride);
}
else
{
this->get_resource().drawIndexedIndirect(buffer.get_resource(), static_cast<vk::DeviceSize>(offset), draw_count, stride);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::end()
{
this->get_resource().end();
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::end_query(QueryPoolType const &query_pool, uint32_t query)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().endQuery(query_pool.get_handle(), query);
}
else
{
this->get_resource().endQuery(static_cast<vk::QueryPool>(query_pool.get_handle()), query);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::end_render_pass()
{
this->get_resource().endRenderPass();
}
template <vkb::BindingType bindingType>
inline typename CommandBuffer<bindingType>::CommandBufferLevelType CommandBuffer<bindingType>::get_level() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return level;
}
else
{
return static_cast<VkCommandBufferLevel>(level);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::execute_commands(vkb::core::CommandBuffer<bindingType> &secondary_command_buffer)
{
this->get_resource().executeCommands(secondary_command_buffer.get_resource());
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::execute_commands(std::vector<std::shared_ptr<vkb::core::CommandBuffer<bindingType>>> &secondary_command_buffers)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
execute_commands_impl(secondary_command_buffers);
}
else
{
execute_commands_impl(reinterpret_cast<std::vector<std::shared_ptr<vkb::core::CommandBufferCpp>> &>(secondary_command_buffers));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::execute_commands_impl(std::vector<std::shared_ptr<vkb::core::CommandBuffer<vkb::BindingType::Cpp>>> &secondary_command_buffers)
{
std::vector<vk::CommandBuffer> sec_cmd_buf_handles(secondary_command_buffers.size(), nullptr);
std::transform(secondary_command_buffers.begin(),
secondary_command_buffers.end(),
sec_cmd_buf_handles.begin(),
[](auto const &sec_cmd_buf) { return sec_cmd_buf->get_handle(); });
this->get_resource().executeCommands(sec_cmd_buf_handles);
}
template <vkb::BindingType bindingType>
inline typename vkb::core::CommandBuffer<bindingType>::RenderPassType &
CommandBuffer<bindingType>::get_render_pass(RenderTargetType const &render_target,
std::vector<LoadStoreInfoType> const &load_store_infos,
std::vector<std::unique_ptr<vkb::rendering::Subpass<bindingType>>> const &subpasses)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return get_render_pass_impl(this->get_device(), render_target, load_store_infos, subpasses);
}
else
{
return reinterpret_cast<vkb::RenderPass &>(
get_render_pass_impl(reinterpret_cast<vkb::core::DeviceCpp &>(this->get_device()),
reinterpret_cast<vkb::rendering::HPPRenderTarget const &>(render_target),
reinterpret_cast<std::vector<vkb::common::HPPLoadStoreInfo> const &>(load_store_infos),
reinterpret_cast<std::vector<std::unique_ptr<vkb::rendering::SubpassCpp>> const &>(subpasses)));
}
}
template <vkb::BindingType bindingType>
inline vkb::core::HPPRenderPass &
CommandBuffer<bindingType>::get_render_pass_impl(vkb::core::DeviceCpp &device,
vkb::rendering::HPPRenderTarget const &render_target,
std::vector<vkb::common::HPPLoadStoreInfo> const &load_store_infos,
std::vector<std::unique_ptr<vkb::rendering::SubpassCpp>> const &subpasses)
{
// Create render pass
assert(subpasses.size() > 0 && "Cannot create a render pass without any subpass");
std::vector<vkb::core::HPPSubpassInfo> subpass_infos(subpasses.size());
auto subpass_info_it = subpass_infos.begin();
for (auto &subpass : subpasses)
{
subpass_info_it->input_attachments = subpass->get_input_attachments();
subpass_info_it->output_attachments = subpass->get_output_attachments();
subpass_info_it->color_resolve_attachments = subpass->get_color_resolve_attachments();
subpass_info_it->disable_depth_stencil_attachment = subpass->get_disable_depth_stencil_attachment();
subpass_info_it->depth_stencil_resolve_mode = subpass->get_depth_stencil_resolve_mode();
subpass_info_it->depth_stencil_resolve_attachment = subpass->get_depth_stencil_resolve_attachment();
subpass_info_it->debug_name = subpass->get_debug_name();
++subpass_info_it;
}
return device.get_resource_cache().request_render_pass(render_target.get_attachments(), load_store_infos, subpass_infos);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::image_memory_barrier(RenderTargetType &render_target, uint32_t view_index, ImageMemoryBarrierType const &memory_barrier) const
{
auto const &image_view = render_target.get_views()[view_index];
if constexpr (bindingType == vkb::BindingType::Cpp)
{
image_memory_barrier_impl(image_view, memory_barrier);
}
else
{
image_memory_barrier_impl(reinterpret_cast<vkb::core::HPPImageView const &>(image_view),
reinterpret_cast<vkb::common::HPPImageMemoryBarrier const &>(memory_barrier));
}
render_target.set_layout(view_index, memory_barrier.new_layout);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::image_memory_barrier(ImageViewType const &image_view, ImageMemoryBarrierType const &memory_barrier) const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
image_memory_barrier_impl(image_view, memory_barrier);
}
else
{
image_memory_barrier_impl(reinterpret_cast<vkb::core::HPPImageView const &>(image_view),
reinterpret_cast<vkb::common::HPPImageMemoryBarrier const &>(memory_barrier));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::image_memory_barrier_impl(vkb::core::HPPImageView const &image_view,
vkb::common::HPPImageMemoryBarrier const &memory_barrier) const
{
// Adjust barrier's subresource range for depth images
auto subresource_range = image_view.get_subresource_range();
auto format = image_view.get_format();
if (vkb::common::is_depth_only_format(format))
{
subresource_range.aspectMask = vk::ImageAspectFlagBits::eDepth;
}
else if (vkb::common::is_depth_stencil_format(format))
{
subresource_range.aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
}
// This can cause a queue family ownership transfer. Check the async_compute sample.
vk::ImageMemoryBarrier image_memory_barrier{.srcAccessMask = memory_barrier.src_access_mask,
.dstAccessMask = memory_barrier.dst_access_mask,
.oldLayout = memory_barrier.old_layout,
.newLayout = memory_barrier.new_layout,
.srcQueueFamilyIndex = memory_barrier.src_queue_family,
.dstQueueFamilyIndex = memory_barrier.dst_queue_family,
.image = image_view.get_image().get_handle(),
.subresourceRange = subresource_range};
vk::PipelineStageFlags src_stage_mask = memory_barrier.src_stage_mask;
vk::PipelineStageFlags dst_stage_mask = memory_barrier.dst_stage_mask;
this->get_resource().pipelineBarrier(src_stage_mask, dst_stage_mask, {}, {}, {}, image_memory_barrier);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::next_subpass()
{
// Increment subpass index
pipeline_state.set_subpass_index(pipeline_state.get_subpass_index() + 1);
// Update blend state attachments
auto blend_state = pipeline_state.get_color_blend_state();
blend_state.attachments.resize(current_render_pass->get_color_output_count(pipeline_state.get_subpass_index()));
pipeline_state.set_color_blend_state(blend_state);
// Reset descriptor sets
resource_binding_state.reset();
descriptor_set_layout_binding_state.clear();
// Clear stored push constants
stored_push_constants.clear();
this->get_resource().nextSubpass(vk::SubpassContents::eInline);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::push_constants(const std::vector<uint8_t> &values)
{
uint32_t push_constant_size = to_u32(stored_push_constants.size() + values.size());
if (push_constant_size > max_push_constants_size)
{
LOGE("Push constant limit of {} exceeded (pushing {} bytes for a total of {} bytes)", max_push_constants_size, values.size(), push_constant_size);
throw std::runtime_error("Push constant limit exceeded.");
}
else
{
stored_push_constants.insert(stored_push_constants.end(), values.begin(), values.end());
}
}
template <vkb::BindingType bindingType>
template <typename T>
inline void CommandBuffer<bindingType>::push_constants(const T &value)
{
auto data = to_bytes(value);
uint32_t size = to_u32(stored_push_constants.size() + data.size());
if (size > max_push_constants_size)
{
LOGE("Push constant limit exceeded ({} / {} bytes)", size, max_push_constants_size);
throw std::runtime_error("Cannot overflow push constant limit");
}
stored_push_constants.insert(stored_push_constants.end(), data.begin(), data.end());
}
template <vkb::BindingType bindingType>
inline typename CommandBuffer<bindingType>::ResultType CommandBuffer<bindingType>::reset(vkb::CommandBufferResetMode reset_mode)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return reset_impl(reset_mode);
}
else
{
return static_cast<VkResult>(reset_impl(reset_mode));
}
}
template <vkb::BindingType bindingType>
inline vk::Result CommandBuffer<bindingType>::reset_impl(vkb::CommandBufferResetMode reset_mode)
{
assert(reset_mode == command_pool.get_reset_mode() && "Command buffer reset mode must match the one used by the pool to allocate it");
if (reset_mode == vkb::CommandBufferResetMode::ResetIndividually)
{
this->get_resource().reset(vk::CommandBufferResetFlagBits::eReleaseResources);
}
return vk::Result::eSuccess;
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::reset_query_pool(QueryPoolType const &query_pool, uint32_t first_query, uint32_t query_count)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().resetQueryPool(query_pool.get_handle(), first_query, query_count);
}
else
{
this->get_resource().resetQueryPool(static_cast<vk::QueryPool>(query_pool.get_handle()), first_query, query_count);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::resolve_image(ImageType const &src_img, ImageType const &dst_img, std::vector<ImageResolveType> const &regions)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().resolveImage(
src_img.get_handle(), vk::ImageLayout::eTransferSrcOptimal, dst_img.get_handle(), vk::ImageLayout::eTransferDstOptimal, regions);
}
else
{
this->get_resource().resolveImage(src_img.get_resource(),
vk::ImageLayout::eTransferSrcOptimal,
dst_img.get_resource(),
vk::ImageLayout::eTransferDstOptimal,
reinterpret_cast<std::vector<vk::ImageResolve> const &>(regions));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_blend_constants(std::array<float, 4> const &blend_constants)
{
this->get_resource().setBlendConstants(blend_constants.data());
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_color_blend_state(ColorBlendStateType const &state_info)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
pipeline_state.set_color_blend_state(state_info);
}
else
{
pipeline_state.set_color_blend_state(reinterpret_cast<vkb::rendering::HPPColorBlendState const &>(state_info));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_depth_bias(float depth_bias_constant_factor, float depth_bias_clamp, float depth_bias_slope_factor)
{
this->get_resource().setDepthBias(depth_bias_constant_factor, depth_bias_clamp, depth_bias_slope_factor);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_depth_bounds(float min_depth_bounds, float max_depth_bounds)
{
this->get_handle().setDepthBounds(min_depth_bounds, max_depth_bounds);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_depth_stencil_state(DepthStencilStateType const &state_info)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
pipeline_state.set_depth_stencil_state(state_info);
}
else
{
pipeline_state.set_depth_stencil_state(reinterpret_cast<vkb::rendering::HPPDepthStencilState const &>(state_info));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_input_assembly_state(InputAssemblyStateType const &state_info)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
pipeline_state.set_input_assembly_state(state_info);
}
else
{
pipeline_state.set_input_assembly_state(reinterpret_cast<vkb::rendering::HPPInputAssemblyState const &>(state_info));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_line_width(float line_width)
{
this->get_resource().setLineWidth(line_width);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_multisample_state(MultisampleStateType const &state_info)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
pipeline_state.set_multisample_state(state_info);
}
else
{
pipeline_state.set_multisample_state(reinterpret_cast<vkb::rendering::HPPMultisampleState const &>(state_info));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_rasterization_state(RasterizationStateType const &state_info)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
pipeline_state.set_rasterization_state(state_info);
}
else
{
pipeline_state.set_rasterization_state(reinterpret_cast<vkb::rendering::HPPRasterizationState const &>(state_info));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_scissor(uint32_t first_scissor, std::vector<Rect2DType> const &scissors)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().setScissor(first_scissor, scissors);
}
else
{
this->get_resource().setScissor(first_scissor, reinterpret_cast<std::vector<vk::Rect2D> const &>(scissors));
}
}
template <vkb::BindingType bindingType>
template <class T>
inline void CommandBuffer<bindingType>::set_specialization_constant(uint32_t constant_id, T const &data)
{
if constexpr (std::is_same<T, bool>::value)
{
set_specialization_constant(constant_id, to_bytes(to_u32(data)));
}
else
{
set_specialization_constant(constant_id, to_bytes(data));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_specialization_constant(uint32_t constant_id, std::vector<uint8_t> const &data)
{
pipeline_state.set_specialization_constant(constant_id, data);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_update_after_bind(bool update_after_bind_)
{
update_after_bind = update_after_bind_;
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_vertex_input_state(VertexInputStateType const &state_info)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
pipeline_state.set_vertex_input_state(state_info);
}
else
{
pipeline_state.set_vertex_input_state(reinterpret_cast<vkb::rendering::HPPVertexInputState const &>(state_info));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_viewport(uint32_t first_viewport, std::vector<ViewportType> const &viewports)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().setViewport(first_viewport, viewports);
}
else
{
this->get_resource().setViewport(first_viewport, reinterpret_cast<std::vector<vk::Viewport> const &>(viewports));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::set_viewport_state(ViewportStateType const &state_info)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
pipeline_state.set_viewport_state(state_info);
}
else
{
pipeline_state.set_viewport_state(reinterpret_cast<vkb::rendering::HPPViewportState const &>(state_info));
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::update_buffer(vkb::core::Buffer<bindingType> const &buffer, DeviceSizeType offset, std::vector<uint8_t> const &data)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().template updateBuffer<uint8_t>(buffer.get_handle(), offset, data);
}
else
{
this->get_resource().template updateBuffer<uint8_t>(buffer.get_resource(), static_cast<vk::DeviceSize>(offset), data);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::write_timestamp(PipelineStagFlagBitsType pipeline_stage, QueryPoolType const &query_pool, uint32_t query)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_resource().writeTimestamp(pipeline_stage, query_pool.get_handle(), query);
}
else
{
this->get_resource().writeTimestamp(static_cast<vk::PipelineStageFlagBits>(pipeline_stage), query_pool.get_handle(), query);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::flush(vk::PipelineBindPoint pipeline_bind_point)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
flush_impl(this->get_device(), pipeline_bind_point);
}
else
{
flush_impl(reinterpret_cast<vkb::core::DeviceCpp &>(this->get_device()), pipeline_bind_point);
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::flush_impl(vkb::core::DeviceCpp &device, vk::PipelineBindPoint pipeline_bind_point)
{
flush_pipeline_state_impl(device, pipeline_bind_point);
flush_push_constants();
flush_descriptor_state_impl(pipeline_bind_point);
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::flush_descriptor_state_impl(vk::PipelineBindPoint pipeline_bind_point)
{
assert(command_pool.get_render_frame() && "The command pool must be associated to a render frame");
const auto &pipeline_layout = pipeline_state.get_pipeline_layout();
std::unordered_set<uint32_t> update_descriptor_sets;
// Iterate over the shader sets to check if they have already been bound
// If they have, add the set so that the command buffer later updates it
for (auto &set_it : pipeline_layout.get_shader_sets())
{
uint32_t descriptor_set_id = set_it.first;
auto descriptor_set_layout_it = descriptor_set_layout_binding_state.find(descriptor_set_id);
if (descriptor_set_layout_it != descriptor_set_layout_binding_state.end())
{
if (descriptor_set_layout_it->second->get_handle() != pipeline_layout.get_descriptor_set_layout(descriptor_set_id).get_handle())
{
update_descriptor_sets.emplace(descriptor_set_id);
}
}
}
// Validate that the bound descriptor set layouts exist in the pipeline layout
for (auto set_it = descriptor_set_layout_binding_state.begin(); set_it != descriptor_set_layout_binding_state.end();)
{
if (!pipeline_layout.has_descriptor_set_layout(set_it->first))
{
set_it = descriptor_set_layout_binding_state.erase(set_it);
}
else
{
++set_it;
}
}
// Check if a descriptor set needs to be created
if (resource_binding_state.is_dirty() || !update_descriptor_sets.empty())
{
resource_binding_state.clear_dirty();
// Iterate over all of the resource sets bound by the command buffer
for (auto &resource_set_it : resource_binding_state.get_resource_sets())
{
uint32_t descriptor_set_id = resource_set_it.first;
auto &resource_set = resource_set_it.second;
// Don't update resource set if it's not in the update list OR its state hasn't changed
if (!resource_set.is_dirty() && (update_descriptor_sets.find(descriptor_set_id) == update_descriptor_sets.end()))
{
continue;
}
// Clear dirty flag for resource set
resource_binding_state.clear_dirty(descriptor_set_id);
// Skip resource set if a descriptor set layout doesn't exist for it
if (!pipeline_layout.has_descriptor_set_layout(descriptor_set_id))
{
continue;
}
auto &descriptor_set_layout = pipeline_layout.get_descriptor_set_layout(descriptor_set_id);
// Make descriptor set layout bound for current set
descriptor_set_layout_binding_state[descriptor_set_id] = &descriptor_set_layout;
BindingMap<vk::DescriptorBufferInfo> buffer_infos;
BindingMap<vk::DescriptorImageInfo> image_infos;
std::vector<uint32_t> dynamic_offsets;
// Iterate over all resource bindings
for (auto &binding_it : resource_set.get_resource_bindings())
{
auto binding_index = binding_it.first;
auto &binding_resources = binding_it.second;
// Check if binding exists in the pipeline layout
if (auto binding_info = descriptor_set_layout.get_layout_binding(binding_index))
{
// Iterate over all binding resources
for (auto &element_it : binding_resources)
{
auto array_element = element_it.first;
auto &resource_info = element_it.second;
// Pointer references
auto &buffer = resource_info.buffer;
auto &sampler = resource_info.sampler;
auto &image_view = resource_info.image_view;
// Get buffer info
if (buffer != nullptr && vkb::common::is_buffer_descriptor_type(binding_info->descriptorType))
{
vk::DescriptorBufferInfo buffer_info{resource_info.buffer->get_handle(), resource_info.offset, resource_info.range};
if (vkb::common::is_dynamic_buffer_descriptor_type(binding_info->descriptorType))
{
dynamic_offsets.push_back(to_u32(buffer_info.offset));
buffer_info.offset = 0;
}
buffer_infos[binding_index][array_element] = buffer_info;
}
// Get image info
else if (image_view != nullptr || sampler != nullptr)
{
// Can be null for input attachments
vk::DescriptorImageInfo image_info{sampler ? sampler->get_handle() : nullptr, image_view->get_handle()};
if (image_view != nullptr)
{
// Add image layout info based on descriptor type
switch (binding_info->descriptorType)
{
case vk::DescriptorType::eCombinedImageSampler:
image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
break;
case vk::DescriptorType::eInputAttachment:
image_info.imageLayout = vkb::common::is_depth_format(image_view->get_format()) ? vk::ImageLayout::eDepthStencilReadOnlyOptimal : vk::ImageLayout::eShaderReadOnlyOptimal;
break;
case vk::DescriptorType::eStorageImage:
image_info.imageLayout = vk::ImageLayout::eGeneral;
break;
default:
continue;
}
}
image_infos[binding_index][array_element] = image_info;
}
}
assert((!update_after_bind || (buffer_infos.count(binding_index) > 0 || (image_infos.count(binding_index) > 0))) &&
"binding index with no buffer or image infos can't be checked for adding to bindings_to_update");
}
}
vk::DescriptorSet descriptor_set_handle = command_pool.get_render_frame()->request_descriptor_set(
descriptor_set_layout, buffer_infos, image_infos, update_after_bind, command_pool.get_thread_index());
// Bind descriptor set
this->get_resource().bindDescriptorSets(pipeline_bind_point, pipeline_layout.get_handle(), descriptor_set_id, descriptor_set_handle, dynamic_offsets);
}
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::flush_pipeline_state_impl(vkb::core::DeviceCpp &device, vk::PipelineBindPoint pipeline_bind_point)
{
// Create a new pipeline only if the graphics state changed
if (!pipeline_state.is_dirty())
{
return;
}
pipeline_state.clear_dirty();
// Create and bind pipeline
if (pipeline_bind_point == vk::PipelineBindPoint::eGraphics)
{
pipeline_state.set_render_pass(*current_render_pass);
auto &pipeline = device.get_resource_cache().request_graphics_pipeline(pipeline_state);
this->get_resource().bindPipeline(pipeline_bind_point, pipeline.get_handle());
}
else if (pipeline_bind_point == vk::PipelineBindPoint::eCompute)
{
auto &pipeline = device.get_resource_cache().request_compute_pipeline(pipeline_state);
this->get_resource().bindPipeline(pipeline_bind_point, pipeline.get_handle());
}
else
{
throw "Only graphics and compute pipeline bind points are supported now";
}
}
template <vkb::BindingType bindingType>
inline void CommandBuffer<bindingType>::flush_push_constants()
{
if (stored_push_constants.empty())
{
return;
}
auto const &pipeline_layout = pipeline_state.get_pipeline_layout();
vk::ShaderStageFlags shader_stage = pipeline_layout.get_push_constant_range_stage(to_u32(stored_push_constants.size()));
if (shader_stage)
{
this->get_resource().template pushConstants<uint8_t>(pipeline_layout.get_handle(), shader_stage, 0, stored_push_constants);
}
else
{
LOGW("Push constant range [{}, {}] not found", 0, stored_push_constants.size());
}
stored_push_constants.clear();
}
template <vkb::BindingType bindingType>
inline bool CommandBuffer<bindingType>::is_render_size_optimal(const vk::Extent2D &framebuffer_extent, const vk::Rect2D &render_area)
{
auto render_area_granularity = current_render_pass->get_render_area_granularity();
return ((render_area.offset.x % render_area_granularity.width == 0) && (render_area.offset.y % render_area_granularity.height == 0) &&
((render_area.extent.width % render_area_granularity.width == 0) ||
(render_area.offset.x + render_area.extent.width == framebuffer_extent.width)) &&
((render_area.extent.height % render_area_granularity.height == 0) ||
(render_area.offset.y + render_area.extent.height == framebuffer_extent.height)));
}
} // namespace core
} // namespace vkb