// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once #include "vulkan_loader.h" #include "common/string_util.h" #include #include #include #ifdef _DEBUG #define ENABLE_VULKAN_DEBUG_OBJECTS 1 #endif #define LOG_VULKAN_ERROR(res, ...) ::Vulkan::LogVulkanResult(__func__, res, __VA_ARGS__) namespace Vulkan { // Adds a structure to a chain. void AddPointerToChain(void* head, const void* ptr); const char* VkResultToString(VkResult res); void LogVulkanResult(const char* func_name, VkResult res, const char* msg, ...); class DescriptorSetLayoutBuilder { public: enum : u32 { MAX_BINDINGS = 16, }; DescriptorSetLayoutBuilder(); void Clear(); void SetPushFlag(); VkDescriptorSetLayout Create(VkDevice device); void AddBinding(u32 binding, VkDescriptorType dtype, u32 dcount, VkShaderStageFlags stages); private: VkDescriptorSetLayoutCreateInfo m_ci{}; std::array m_bindings{}; }; class PipelineLayoutBuilder { public: enum : u32 { MAX_SETS = 8, MAX_PUSH_CONSTANTS = 1 }; PipelineLayoutBuilder(); void Clear(); VkPipelineLayout Create(VkDevice device); void AddDescriptorSet(VkDescriptorSetLayout layout); void AddPushConstants(VkShaderStageFlags stages, u32 offset, u32 size); private: VkPipelineLayoutCreateInfo m_ci{}; std::array m_sets{}; std::array m_push_constants{}; }; class GraphicsPipelineBuilder { public: enum : u32 { MAX_SHADER_STAGES = 3, MAX_VERTEX_ATTRIBUTES = 16, MAX_VERTEX_BUFFERS = 8, MAX_ATTACHMENTS = 2, MAX_DYNAMIC_STATE = 8 }; GraphicsPipelineBuilder(); void Clear(); VkPipeline Create(VkDevice device, VkPipelineCache pipeline_cache = VK_NULL_HANDLE, bool clear = true); void SetShaderStage(VkShaderStageFlagBits stage, VkShaderModule module, const char* entry_point); void SetVertexShader(VkShaderModule module) { SetShaderStage(VK_SHADER_STAGE_VERTEX_BIT, module, "main"); } void SetGeometryShader(VkShaderModule module) { SetShaderStage(VK_SHADER_STAGE_GEOMETRY_BIT, module, "main"); } void SetFragmentShader(VkShaderModule module) { SetShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, module, "main"); } void AddVertexBuffer(u32 binding, u32 stride, VkVertexInputRate input_rate = VK_VERTEX_INPUT_RATE_VERTEX); void AddVertexAttribute(u32 location, u32 binding, VkFormat format, u32 offset); void SetPrimitiveTopology(VkPrimitiveTopology topology, bool enable_primitive_restart = false); void SetRasterizationState(VkPolygonMode polygon_mode, VkCullModeFlags cull_mode, VkFrontFace front_face); void SetLineWidth(float width); void SetLineRasterizationMode(VkLineRasterizationModeEXT mode); void SetMultisamples(VkSampleCountFlagBits samples); void SetMultisamples(u32 multisamples, bool per_sample_shading); void SetNoCullRasterizationState(); void SetDepthState(bool depth_test, bool depth_write, VkCompareOp compare_op); void SetStencilState(bool stencil_test, const VkStencilOpState& front, const VkStencilOpState& back); void SetNoDepthTestState(); void SetNoStencilState(); void AddBlendAttachment(bool blend_enable, VkBlendFactor src_factor, VkBlendFactor dst_factor, VkBlendOp op, VkBlendFactor alpha_src_factor, VkBlendFactor alpha_dst_factor, VkBlendOp alpha_op, VkColorComponentFlags write_mask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); void SetBlendAttachment(u32 attachment, bool blend_enable, VkBlendFactor src_factor, VkBlendFactor dst_factor, VkBlendOp op, VkBlendFactor alpha_src_factor, VkBlendFactor alpha_dst_factor, VkBlendOp alpha_op, VkColorComponentFlags write_mask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); void SetColorWriteMask(u32 attachment, VkColorComponentFlags write_mask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); void AddBlendFlags(u32 flags); void ClearBlendAttachments(); void SetBlendConstants(float r, float g, float b, float a); void SetNoBlendingState(); void AddDynamicState(VkDynamicState state); void SetDynamicViewportAndScissorState(); void SetViewport(float x, float y, float width, float height, float min_depth, float max_depth); void SetScissorRect(s32 x, s32 y, u32 width, u32 height); void SetPipelineLayout(VkPipelineLayout layout); void SetRenderPass(VkRenderPass render_pass, u32 subpass); void SetProvokingVertex(VkProvokingVertexModeEXT mode); private: VkGraphicsPipelineCreateInfo m_ci; std::array m_shader_stages; VkPipelineVertexInputStateCreateInfo m_vertex_input_state; std::array m_vertex_buffers; std::array m_vertex_attributes; VkPipelineInputAssemblyStateCreateInfo m_input_assembly; VkPipelineRasterizationStateCreateInfo m_rasterization_state; VkPipelineDepthStencilStateCreateInfo m_depth_state; VkPipelineColorBlendStateCreateInfo m_blend_state; std::array m_blend_attachments; VkPipelineViewportStateCreateInfo m_viewport_state; VkViewport m_viewport; VkRect2D m_scissor; VkPipelineDynamicStateCreateInfo m_dynamic_state; std::array m_dynamic_state_values; VkPipelineMultisampleStateCreateInfo m_multisample_state; VkPipelineRasterizationProvokingVertexStateCreateInfoEXT m_provoking_vertex; VkPipelineRasterizationLineStateCreateInfoEXT m_line_rasterization_state; }; class ComputePipelineBuilder { public: enum : u32 { SPECIALIZATION_CONSTANT_SIZE = 4, MAX_SPECIALIZATION_CONSTANTS = 4, }; ComputePipelineBuilder(); void Clear(); VkPipeline Create(VkDevice device, VkPipelineCache pipeline_cache = VK_NULL_HANDLE, bool clear = true); void SetShader(VkShaderModule module, const char* entry_point); void SetPipelineLayout(VkPipelineLayout layout); void SetSpecializationBool(u32 index, bool value); private: void SetSpecializationValue(u32 index, u32 value); VkComputePipelineCreateInfo m_ci; VkSpecializationInfo m_si; std::array m_smap_entries; std::array m_smap_constants; }; class SamplerBuilder { public: SamplerBuilder(); void Clear(); VkSampler Create(VkDevice device, bool clear = true); void SetFilter(VkFilter mag_filter, VkFilter min_filter, VkSamplerMipmapMode mip_filter); void SetAddressMode(VkSamplerAddressMode u, VkSamplerAddressMode v, VkSamplerAddressMode w); void SetPointSampler(VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); void SetLinearSampler(bool mipmaps, VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); private: VkSamplerCreateInfo m_ci; }; class DescriptorSetUpdateBuilder { enum : u32 { MAX_WRITES = 16, MAX_IMAGE_INFOS = 8, MAX_BUFFER_INFOS = 4, MAX_VIEWS = 4, }; public: DescriptorSetUpdateBuilder(); void Clear(); void Update(VkDevice device, bool clear = true); void PushUpdate(VkCommandBuffer cmdbuf, VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 set, bool clear = true); void AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view, VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); void AddSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkSampler sampler); void AddSamplerDescriptorWrites(VkDescriptorSet set, u32 binding, const VkSampler* samplers, u32 num_samplers); void AddCombinedImageSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view, VkSampler sampler, VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); void AddCombinedImageSamplerDescriptorWrites(VkDescriptorSet set, u32 binding, const VkImageView* views, const VkSampler* samplers, u32 num_views, VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); void AddBufferDescriptorWrite(VkDescriptorSet set, u32 binding, VkDescriptorType dtype, VkBuffer buffer, u32 offset, u32 size); void AddBufferViewDescriptorWrite(VkDescriptorSet set, u32 binding, VkDescriptorType dtype, VkBufferView view); void AddInputAttachmentDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view, VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL); void AddStorageImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view, VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL); private: std::array m_writes; u32 m_num_writes = 0; std::array m_buffer_infos; std::array m_image_infos; std::array m_views; u32 m_num_buffer_infos = 0; u32 m_num_image_infos = 0; u32 m_num_views = 0; }; class FramebufferBuilder { enum : u32 { MAX_ATTACHMENTS = 2, }; public: FramebufferBuilder(); void Clear(); VkFramebuffer Create(VkDevice device, bool clear = true); void AddAttachment(VkImageView image); void SetSize(u32 width, u32 height, u32 layers); void SetRenderPass(VkRenderPass render_pass); private: VkFramebufferCreateInfo m_ci; std::array m_images; }; class RenderPassBuilder { enum : u32 { MAX_ATTACHMENTS = 2, MAX_ATTACHMENT_REFERENCES = 2, MAX_SUBPASSES = 1, }; public: RenderPassBuilder(); void Clear(); VkRenderPass Create(VkDevice device, bool clear = true); u32 AddAttachment(VkFormat format, VkSampleCountFlagBits samples, VkAttachmentLoadOp load_op, VkAttachmentStoreOp store_op, VkImageLayout initial_layout, VkImageLayout final_layout); u32 AddSubpass(); void AddSubpassColorAttachment(u32 subpass, u32 attachment, VkImageLayout layout); void AddSubpassDepthAttachment(u32 subpass, u32 attachment, VkImageLayout layout); private: VkRenderPassCreateInfo m_ci; std::array m_attachments; std::array m_attachment_references; u32 m_num_attachment_references = 0; std::array m_subpasses; }; class BufferViewBuilder { public: BufferViewBuilder(); void Clear(); VkBufferView Create(VkDevice device, bool clear = true); void Set(VkBuffer buffer, VkFormat format, u32 offset, u32 size); private: VkBufferViewCreateInfo m_ci; }; #ifdef ENABLE_VULKAN_DEBUG_OBJECTS // Provides a compile-time mapping between a Vulkan-type into its matching VkObjectType template struct VkObjectTypeMap; // clang-format off template<> struct VkObjectTypeMap { using type = VkInstance; static constexpr VkObjectType value = VK_OBJECT_TYPE_INSTANCE; }; template<> struct VkObjectTypeMap { using type = VkPhysicalDevice; static constexpr VkObjectType value = VK_OBJECT_TYPE_PHYSICAL_DEVICE; }; template<> struct VkObjectTypeMap { using type = VkDevice; static constexpr VkObjectType value = VK_OBJECT_TYPE_DEVICE; }; template<> struct VkObjectTypeMap { using type = VkQueue; static constexpr VkObjectType value = VK_OBJECT_TYPE_QUEUE; }; template<> struct VkObjectTypeMap { using type = VkSemaphore; static constexpr VkObjectType value = VK_OBJECT_TYPE_SEMAPHORE; }; template<> struct VkObjectTypeMap { using type = VkCommandBuffer; static constexpr VkObjectType value = VK_OBJECT_TYPE_COMMAND_BUFFER; }; template<> struct VkObjectTypeMap { using type = VkFence; static constexpr VkObjectType value = VK_OBJECT_TYPE_FENCE; }; template<> struct VkObjectTypeMap { using type = VkDeviceMemory; static constexpr VkObjectType value = VK_OBJECT_TYPE_DEVICE_MEMORY; }; template<> struct VkObjectTypeMap { using type = VkBuffer; static constexpr VkObjectType value = VK_OBJECT_TYPE_BUFFER; }; template<> struct VkObjectTypeMap { using type = VkImage; static constexpr VkObjectType value = VK_OBJECT_TYPE_IMAGE; }; template<> struct VkObjectTypeMap { using type = VkEvent; static constexpr VkObjectType value = VK_OBJECT_TYPE_EVENT; }; template<> struct VkObjectTypeMap { using type = VkQueryPool; static constexpr VkObjectType value = VK_OBJECT_TYPE_QUERY_POOL; }; template<> struct VkObjectTypeMap { using type = VkBufferView; static constexpr VkObjectType value = VK_OBJECT_TYPE_BUFFER_VIEW; }; template<> struct VkObjectTypeMap { using type = VkImageView; static constexpr VkObjectType value = VK_OBJECT_TYPE_IMAGE_VIEW; }; template<> struct VkObjectTypeMap { using type = VkShaderModule; static constexpr VkObjectType value = VK_OBJECT_TYPE_SHADER_MODULE; }; template<> struct VkObjectTypeMap { using type = VkPipelineCache; static constexpr VkObjectType value = VK_OBJECT_TYPE_PIPELINE_CACHE; }; template<> struct VkObjectTypeMap { using type = VkPipelineLayout; static constexpr VkObjectType value = VK_OBJECT_TYPE_PIPELINE_LAYOUT; }; template<> struct VkObjectTypeMap { using type = VkRenderPass; static constexpr VkObjectType value = VK_OBJECT_TYPE_RENDER_PASS; }; template<> struct VkObjectTypeMap { using type = VkPipeline; static constexpr VkObjectType value = VK_OBJECT_TYPE_PIPELINE; }; template<> struct VkObjectTypeMap { using type = VkDescriptorSetLayout; static constexpr VkObjectType value = VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT; }; template<> struct VkObjectTypeMap { using type = VkSampler; static constexpr VkObjectType value = VK_OBJECT_TYPE_SAMPLER; }; template<> struct VkObjectTypeMap { using type = VkDescriptorPool; static constexpr VkObjectType value = VK_OBJECT_TYPE_DESCRIPTOR_POOL; }; template<> struct VkObjectTypeMap { using type = VkDescriptorSet; static constexpr VkObjectType value = VK_OBJECT_TYPE_DESCRIPTOR_SET; }; template<> struct VkObjectTypeMap { using type = VkFramebuffer; static constexpr VkObjectType value = VK_OBJECT_TYPE_FRAMEBUFFER; }; template<> struct VkObjectTypeMap { using type = VkCommandPool; static constexpr VkObjectType value = VK_OBJECT_TYPE_COMMAND_POOL; }; template<> struct VkObjectTypeMap { using type = VkDescriptorUpdateTemplate; static constexpr VkObjectType value = VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE; }; template<> struct VkObjectTypeMap { using type = VkSurfaceKHR; static constexpr VkObjectType value = VK_OBJECT_TYPE_SURFACE_KHR; }; template<> struct VkObjectTypeMap { using type = VkSwapchainKHR; static constexpr VkObjectType value = VK_OBJECT_TYPE_SWAPCHAIN_KHR; }; template<> struct VkObjectTypeMap { using type = VkDebugUtilsMessengerEXT; static constexpr VkObjectType value = VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT; }; // clang-format on #endif static inline void SetFormattedObjectName(VkDevice device, void* object_handle, VkObjectType object_type, const char* format, va_list ap) { #ifdef ENABLE_VULKAN_DEBUG_OBJECTS if (!vkSetDebugUtilsObjectNameEXT) { return; } const std::string str(StringUtil::StdStringFromFormatV(format, ap)); const VkDebugUtilsObjectNameInfoEXT nameInfo{VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, nullptr, object_type, reinterpret_cast(object_handle), str.c_str()}; vkSetDebugUtilsObjectNameEXT(device, &nameInfo); #endif } template static inline void SetFormattedObjectName(VkDevice device, T object_handle, const char* format, ...) { #ifdef ENABLE_VULKAN_DEBUG_OBJECTS std::va_list ap; va_start(ap, format); SetFormattedObjectName(device, reinterpret_cast((typename VkObjectTypeMap::type)object_handle), VkObjectTypeMap::value, format, ap); va_end(ap); #endif } template static inline void SetObjectName(VkDevice device, T object_handle, const std::string_view& sv) { #ifdef ENABLE_VULKAN_DEBUG_OBJECTS SetFormattedObjectName(device, object_handle, "%.*s", static_cast(sv.length()), sv.data()); #endif } } // namespace Vulkan