diff --git a/include/inexor/vulkan-renderer/msaa_target.hpp b/include/inexor/vulkan-renderer/msaa_target.hpp deleted file mode 100644 index d6884b43f..000000000 --- a/include/inexor/vulkan-renderer/msaa_target.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "inexor/vulkan-renderer/wrapper/image.hpp" - -#include - -namespace inexor::vulkan_renderer { - -struct MSAATarget { - // The color buffer. - std::unique_ptr m_color; - - // The depth buffer. - std::unique_ptr m_depth; -}; - -} // namespace inexor::vulkan_renderer diff --git a/include/inexor/vulkan-renderer/render_graph.hpp b/include/inexor/vulkan-renderer/render_graph.hpp index faac93feb..95a43456a 100644 --- a/include/inexor/vulkan-renderer/render_graph.hpp +++ b/include/inexor/vulkan-renderer/render_graph.hpp @@ -5,6 +5,11 @@ #include "inexor/vulkan-renderer/wrapper/device.hpp" #include "inexor/vulkan-renderer/wrapper/fence.hpp" #include "inexor/vulkan-renderer/wrapper/framebuffer.hpp" +#include "inexor/vulkan-renderer/wrapper/image.hpp" +#include "inexor/vulkan-renderer/wrapper/pipeline.hpp" +#include "inexor/vulkan-renderer/wrapper/pipeline_builder.hpp" +#include "inexor/vulkan-renderer/wrapper/pipeline_layout.hpp" +#include "inexor/vulkan-renderer/wrapper/renderpass.hpp" #include "inexor/vulkan-renderer/wrapper/semaphore.hpp" #include "inexor/vulkan-renderer/wrapper/shader.hpp" #include "inexor/vulkan-renderer/wrapper/swapchain.hpp" @@ -89,7 +94,7 @@ class BufferResource : public RenderResource { private: const BufferUsage m_usage; - std::vector m_vertex_attributes; + std::vector m_vert_input_attr_descs; // Data to upload during render graph compilation. const void *m_data{nullptr}; @@ -100,14 +105,14 @@ class BufferResource : public RenderResource { public: BufferResource(std::string &&name, BufferUsage usage) : RenderResource(name), m_usage(usage) {} - /// @brief Specifies that element `offset` of this vertex buffer is of format `format`. - /// @note Calling this function is only valid on buffers of type BufferUsage::VERTEX_BUFFER. - void add_vertex_attribute(VkFormat format, std::uint32_t offset); - - /// @brief Specifies the element size of the buffer upfront if data is not to be uploaded immediately. - /// @param element_size The element size in bytes - void set_element_size(std::size_t element_size) { - m_element_size = element_size; + /// Set the vertex input attribute descriptions + /// @tparam VertexDataType The vertex data type + /// @param vertex_attribute_descriptions the vertex input attribute descriptions + template + BufferResource *set_vertex_attributes(std::vector vert_attr_descs) { + m_vert_input_attr_descs = std::move(vert_attr_descs); + m_element_size = sizeof(VertexDataType); + return this; } /// @brief Specifies the data that should be uploaded to this buffer at the start of the next frame. @@ -115,13 +120,13 @@ class BufferResource : public RenderResource { /// @param data A pointer to a contiguous block of memory that is at least `count * sizeof(T)` bytes long // TODO: Use std::span when we switch to C++ 20. template - void upload_data(const T *data, std::size_t count); + BufferResource *upload_data(const T *data, std::size_t count); /// @brief @copybrief upload_data(const T *, std::size_t) /// @note This is equivalent to doing `upload_data(data.data(), data.size() * sizeof(T))` /// @see upload_data(const T *data, std::size_t count) template - void upload_data(const std::vector &data); + BufferResource *upload_data(const std::vector &data); }; enum class TextureUsage { @@ -145,14 +150,8 @@ class TextureResource : public RenderResource { VkFormat m_format{VK_FORMAT_UNDEFINED}; public: - TextureResource(std::string &&name, TextureUsage usage) : RenderResource(name), m_usage(usage) {} - - /// @brief Specifies the format of this texture that is required when the physical texture is made. - /// @details For TextureUsage::BACK_BUFFER textures, using the swapchain image format is preferable in most cases. - /// For TextureUsage::DEPTH_STENCIL_BUFFER textures, a VK_FORMAT_D* must be used. - void set_format(VkFormat format) { - m_format = format; - } + TextureResource(std::string &&name, TextureUsage usage, VkFormat format) + : RenderResource(name), m_usage(usage), m_format(format) {} }; /// @brief A single render stage in the render graph. @@ -182,23 +181,30 @@ class RenderStage : public RenderGraphObject { RenderStage &operator=(RenderStage &&) = delete; /// @brief Specifies that this stage writes to `resource`. - void writes_to(const RenderResource *resource); + RenderStage *writes_to(const RenderResource *resource); /// @brief Specifies that this stage reads from `resource`. - void reads_from(const RenderResource *resource); + RenderStage *reads_from(const RenderResource *resource); /// @brief Binds a descriptor set layout to this render stage. /// @note This function will be removed in the near future, as we are aiming for users of the API to not have to /// deal with descriptors at all. // TODO: Refactor descriptor management in the render graph - void add_descriptor_layout(VkDescriptorSetLayout layout) { + RenderStage *add_descriptor_layout(VkDescriptorSetLayout layout) { m_descriptor_layouts.push_back(layout); + return this; } - /// @brief Add a push constant range to this render stage. - /// @param range The push constant range - void add_push_constant_range(VkPushConstantRange range) { - m_push_constant_ranges.push_back(range); + /// Add a push constant range to this render stage + template + RenderStage *add_push_constant_range(const VkShaderStageFlags stage_flags = VK_SHADER_STAGE_VERTEX_BIT, + const std::uint32_t offset = 0) { + m_push_constant_ranges.push_back({ + .stageFlags = stage_flags, + .offset = offset, + .size = sizeof(PushConstantDataType), + }); + return this; } [[nodiscard]] const std::string &name() const { @@ -208,8 +214,9 @@ class RenderStage : public RenderGraphObject { /// @brief Specifies a function that will be called during command buffer recording for this stage /// @details This function can be used to specify other vulkan commands during command buffer recording. The most /// common use for this is for draw commands. - void set_on_record(std::function on_record) { + RenderStage *set_on_record(std::function on_record) { m_on_record = std::move(on_record); + return this; } }; @@ -234,30 +241,37 @@ class GraphicsStage : public RenderStage { GraphicsStage &operator=(GraphicsStage &&) = delete; /// @brief Specifies that this stage should clear the screen before rendering. - void set_clears_screen(bool clears_screen) { + GraphicsStage *set_clears_screen(bool clears_screen) { m_clears_screen = clears_screen; + return this; } /// @brief Specifies the depth options for this stage. /// @param depth_test Whether depth testing should be performed /// @param depth_write Whether depth writing should be performed - void set_depth_options(bool depth_test, bool depth_write) { + GraphicsStage *set_depth_options(bool depth_test, bool depth_write) { m_depth_test = depth_test; m_depth_write = depth_write; + return this; } /// @brief Set the blend attachment for this stage. /// @param blend_attachment The blend attachment - void set_blend_attachment(VkPipelineColorBlendAttachmentState blend_attachment) { + GraphicsStage *set_blend_attachment(VkPipelineColorBlendAttachmentState blend_attachment) { m_blend_attachment = blend_attachment; + return this; } /// @brief Specifies that `buffer` should map to `binding` in the shaders of this stage. - void bind_buffer(const BufferResource *buffer, std::uint32_t binding); + GraphicsStage *bind_buffer(const BufferResource *buffer, std::uint32_t binding); /// @brief Specifies that `shader` should be used during the pipeline of this stage. /// @note Binding two shaders of same type (e.g. two vertex shaders) is undefined behaviour. - void uses_shader(const wrapper::Shader &shader); + GraphicsStage *uses_shader(const wrapper::Shader &shader); + + /// Specifies the shaders which will be used during the pipeline of this stage + /// @param shaders The shaders to use during the pipeline of this stage + GraphicsStage *uses_shaders(std::span shaders); }; // TODO: Add wrapper::Allocation that can be made by doing `device->make(...)`. @@ -300,17 +314,20 @@ class PhysicalImage : public PhysicalResource { friend RenderGraph; private: - VkImage m_image{VK_NULL_HANDLE}; - VkImageView m_image_view{VK_NULL_HANDLE}; + std::unique_ptr m_img; public: explicit PhysicalImage(const wrapper::Device &device) : PhysicalResource(device) {} PhysicalImage(const PhysicalImage &) = delete; PhysicalImage(PhysicalImage &&) = delete; - ~PhysicalImage() override; + ~PhysicalImage() override = default; PhysicalImage &operator=(const PhysicalImage &) = delete; PhysicalImage &operator=(PhysicalImage &&) = delete; + + [[nodiscard]] VkImageView image_view() const { + return m_img->image_view(); + } }; class PhysicalBackBuffer : public PhysicalResource { @@ -334,8 +351,8 @@ class PhysicalStage : public RenderGraphObject { friend RenderGraph; private: - VkPipeline m_pipeline{VK_NULL_HANDLE}; - VkPipelineLayout m_pipeline_layout{VK_NULL_HANDLE}; + std::unique_ptr m_pipeline_layout; + std::unique_ptr m_pipeline; protected: const wrapper::Device &m_device; @@ -344,7 +361,7 @@ class PhysicalStage : public RenderGraphObject { explicit PhysicalStage(const wrapper::Device &device) : m_device(device) {} PhysicalStage(const PhysicalStage &) = delete; PhysicalStage(PhysicalStage &&) = delete; - ~PhysicalStage() override; + ~PhysicalStage() override = default; PhysicalStage &operator=(const PhysicalStage &) = delete; PhysicalStage &operator=(PhysicalStage &&) = delete; @@ -352,7 +369,7 @@ class PhysicalStage : public RenderGraphObject { /// @brief Retrieve the pipeline layout of this physical stage. // TODO: This can be removed once descriptors are properly implemented in the render graph. [[nodiscard]] VkPipelineLayout pipeline_layout() const { - return m_pipeline_layout; + return m_pipeline_layout->pipeline_layout(); } }; @@ -360,17 +377,21 @@ class PhysicalGraphicsStage : public PhysicalStage { friend RenderGraph; private: - VkRenderPass m_render_pass{VK_NULL_HANDLE}; std::vector m_framebuffers; + std::unique_ptr m_render_pass; public: explicit PhysicalGraphicsStage(const wrapper::Device &device) : PhysicalStage(device) {} PhysicalGraphicsStage(const PhysicalGraphicsStage &) = delete; PhysicalGraphicsStage(PhysicalGraphicsStage &&) = delete; - ~PhysicalGraphicsStage() override; + ~PhysicalGraphicsStage() override = default; PhysicalGraphicsStage &operator=(const PhysicalGraphicsStage &) = delete; PhysicalGraphicsStage &operator=(PhysicalGraphicsStage &&) = delete; + + [[nodiscard]] VkRenderPass render_pass() const noexcept { + return m_render_pass->render_pass(); + } }; class RenderGraph { @@ -389,8 +410,7 @@ class RenderGraph { // Functions for building resource related vulkan objects. void build_buffer(const BufferResource &, PhysicalBuffer &) const; - void build_image(const TextureResource &, PhysicalImage &, VmaAllocationCreateInfo *) const; - void build_image_view(const TextureResource &, PhysicalImage &) const; + void build_image(const TextureResource &, PhysicalImage &) const; // Functions for building stage related vulkan objects. void build_pipeline_layout(const RenderStage *, PhysicalStage &) const; @@ -408,7 +428,7 @@ class RenderGraph { /// @brief Adds either a render resource or render stage to the render graph. /// @return A mutable reference to the just-added resource or stage template - T *add(Args &&...args) { + T *add(Args &&... args) { auto ptr = std::make_unique(std::forward(args)...); if constexpr (std::is_same_v) { return static_cast(m_buffer_resources.emplace_back(std::move(ptr)).get()); @@ -442,15 +462,17 @@ template } template -void BufferResource::upload_data(const T *data, std::size_t count) { +BufferResource *BufferResource::upload_data(const T *data, std::size_t count) { m_data = data; m_data_size = count * (m_element_size = sizeof(T)); m_data_upload_needed = true; + return this; } template -void BufferResource::upload_data(const std::vector &data) { +BufferResource *BufferResource::upload_data(const std::vector &data) { upload_data(data.data(), data.size()); + return this; } } // namespace inexor::vulkan_renderer diff --git a/include/inexor/vulkan-renderer/renderer.hpp b/include/inexor/vulkan-renderer/renderer.hpp index 57019e92a..25ea3b226 100644 --- a/include/inexor/vulkan-renderer/renderer.hpp +++ b/include/inexor/vulkan-renderer/renderer.hpp @@ -3,7 +3,6 @@ #include "inexor/vulkan-renderer/camera.hpp" #include "inexor/vulkan-renderer/fps_counter.hpp" #include "inexor/vulkan-renderer/imgui.hpp" -#include "inexor/vulkan-renderer/msaa_target.hpp" #include "inexor/vulkan-renderer/octree_gpu_vertex.hpp" #include "inexor/vulkan-renderer/render_graph.hpp" #include "inexor/vulkan-renderer/time_step.hpp" @@ -17,7 +16,6 @@ #include "inexor/vulkan-renderer/wrapper/gpu_texture.hpp" #include "inexor/vulkan-renderer/wrapper/image.hpp" #include "inexor/vulkan-renderer/wrapper/instance.hpp" -#include "inexor/vulkan-renderer/wrapper/mesh_buffer.hpp" #include "inexor/vulkan-renderer/wrapper/semaphore.hpp" #include "inexor/vulkan-renderer/wrapper/shader.hpp" #include "inexor/vulkan-renderer/wrapper/swapchain.hpp" diff --git a/include/inexor/vulkan-renderer/wrapper/device.hpp b/include/inexor/vulkan-renderer/wrapper/device.hpp index 07193007e..16f4aba4f 100644 --- a/include/inexor/vulkan-renderer/wrapper/device.hpp +++ b/include/inexor/vulkan-renderer/wrapper/device.hpp @@ -267,11 +267,21 @@ class Device { void create_graphics_pipeline(const VkGraphicsPipelineCreateInfo &pipeline_ci, VkPipeline *pipeline, const std::string &name) const; + /// Call vmaCreateImage (VMA wrapper) + /// @param img_ci The image create info structure + /// @param img_alloc_ci The image allocation create info + /// @param img The image to create + /// @param alloc The VMA allocation structure + /// @param alloc_info The VMA allocation info structure + /// @param name The internal debug name of the image + void create_image(const VkImageCreateInfo &img_ci, const VmaAllocationCreateInfo &img_alloc_ci, VkImage *img, + VmaAllocation *alloc, VmaAllocationInfo *alloc_info, const std::string &name) const; + /// Call vkCreateImageView - /// @param image_view_ci The image view create info structure - /// @param image_view The image view to create - /// @param name The internal debug marker name which will be assigned to this image view - void create_image_view(const VkImageViewCreateInfo &image_view_ci, VkImageView *image_view, + /// @param img_view_ci The image view create info structure + /// @param img_view The image view to create + /// @param name The internal debug name which will be assigned to this image view + void create_image_view(const VkImageViewCreateInfo &img_view_ci, VkImageView *img_view, const std::string &name) const; /// Call vkCreatePipelineLayout diff --git a/include/inexor/vulkan-renderer/wrapper/gpu_texture.hpp b/include/inexor/vulkan-renderer/wrapper/gpu_texture.hpp index d50ae49f0..36469f17a 100644 --- a/include/inexor/vulkan-renderer/wrapper/gpu_texture.hpp +++ b/include/inexor/vulkan-renderer/wrapper/gpu_texture.hpp @@ -1,9 +1,9 @@ #pragma once #include "inexor/vulkan-renderer/wrapper/cpu_texture.hpp" -#include "inexor/vulkan-renderer/wrapper/device.hpp" #include "inexor/vulkan-renderer/wrapper/gpu_memory_buffer.hpp" #include "inexor/vulkan-renderer/wrapper/image.hpp" +#include "inexor/vulkan-renderer/wrapper/sampler.hpp" #include @@ -12,24 +12,25 @@ namespace inexor::vulkan_renderer::wrapper { +// Forward declarations class Device; class GPUMemoryBuffer; +// TODO: Support 3D textures and cube maps (implement new and separate wrappers though). + /// @note The code which loads textures from files is wrapped in CpuTexture. -/// @brief RAII wrapper class for textures which are stored in GPU memory. -/// @todo Support 3D textures and cube maps (implement new and separate wrappers though). +/// RAII wrapper class for textures which are stored in GPU memory. class GpuTexture { + const wrapper::Device &m_device; std::unique_ptr m_texture_image; - VkSampler m_sampler{VK_NULL_HANDLE}; + std::unique_ptr m_sampler; + VkFormat m_texture_format{VK_FORMAT_R8G8B8A8_UNORM}; int m_texture_width{0}; int m_texture_height{0}; int m_texture_channels{0}; int m_mip_levels{0}; - std::string m_name; - const wrapper::Device &m_device; - const VkFormat m_texture_image_format{VK_FORMAT_R8G8B8A8_UNORM}; /// @brief Create the texture. /// @param texture_data A pointer to the texture data. @@ -42,15 +43,12 @@ class GpuTexture { /// @param new_layout The new image layout. void transition_image_layout(VkImage image, VkImageLayout old_layout, VkImageLayout new_layout); - /// @brief Create the texture sampler. - void create_texture_sampler(); - public: /// @brief Construct a texture from a file. /// @param device The const reference to a device RAII wrapper instance. /// @param file_name The name of the texture file. /// @param name The internal debug marker name of the texture. - GpuTexture(const wrapper::Device &device, const CpuTexture &cpu_texture); + GpuTexture(const Device &device, const CpuTexture &cpu_texture); /// @brief Construct a texture from a block of memory. /// @param device The const reference to a device RAII wrapper instance. @@ -60,7 +58,7 @@ class GpuTexture { /// @param texture_height The height of the texture. /// @param texture_size The size of the texture. /// @param name The internal debug marker name of the texture. - GpuTexture(const wrapper::Device &device, void *data, std::size_t data_size, int texture_width, int texture_height, + GpuTexture(const Device &device, void *data, std::size_t data_size, int texture_width, int texture_height, int texture_channels, int mip_levels, std::string name); GpuTexture(const GpuTexture &) = delete; @@ -76,7 +74,7 @@ class GpuTexture { } [[nodiscard]] VkImage image() const { - return m_texture_image->get(); + return m_texture_image->image(); } [[nodiscard]] VkImageView image_view() const { @@ -84,7 +82,7 @@ class GpuTexture { } [[nodiscard]] VkSampler sampler() const { - return m_sampler; + return m_sampler->sampler(); } }; diff --git a/include/inexor/vulkan-renderer/wrapper/image.hpp b/include/inexor/vulkan-renderer/wrapper/image.hpp index a297ec39f..ad64e8cc7 100644 --- a/include/inexor/vulkan-renderer/wrapper/image.hpp +++ b/include/inexor/vulkan-renderer/wrapper/image.hpp @@ -2,38 +2,34 @@ #include -#include #include namespace inexor::vulkan_renderer::wrapper { +// Forward declarations class Device; -/// @brief RAII wrapper class for VkImage. +/// RAII wrapper class for VkImage class Image { - const wrapper::Device &m_device; - VmaAllocation m_allocation{VK_NULL_HANDLE}; - VmaAllocationInfo m_allocation_info{}; - VkImage m_image{VK_NULL_HANDLE}; + const Device &m_device; VkFormat m_format{VK_FORMAT_UNDEFINED}; - VkImageView m_image_view{VK_NULL_HANDLE}; + VmaAllocation m_alloc{VK_NULL_HANDLE}; + VmaAllocationInfo m_alloc_info{}; + VkImage m_img{VK_NULL_HANDLE}; + VkImageView m_img_view{VK_NULL_HANDLE}; std::string m_name; public: - /// @brief Default constructor. - /// @param device The const reference to a device RAII wrapper instance. - /// @param format The color format. - /// @param image_usage The image usage flags. - /// @param aspect_flags The aspect flags. - /// @param sample_count The sample count. - /// @param name The internal debug marker name of the VkImage. - /// @param image_extent The width and height of the image. - Image(const Device &device, VkFormat format, VkImageUsageFlags image_usage, VkImageAspectFlags aspect_flags, - VkSampleCountFlagBits sample_count, const std::string &name, VkExtent2D image_extent); - + /// Default constructor + /// @param device The device wrapper + /// @param img_ci The image create info + /// @param img_view_ci The image view create info + /// @param alloc_ci The allocation create info + /// @param name The internal name of the image and the image view + Image(const Device &device, const VkImageCreateInfo &img_ci, const VmaAllocationCreateInfo &alloc_ci, + const VkImageViewCreateInfo &img_view_ci, std::string name); Image(const Image &) = delete; Image(Image &&) noexcept; - ~Image(); Image &operator=(const Image &) = delete; @@ -44,11 +40,11 @@ class Image { } [[nodiscard]] VkImageView image_view() const { - return m_image_view; + return m_img_view; } - [[nodiscard]] VkImage get() const { - return m_image; + [[nodiscard]] VkImage image() const { + return m_img; } }; diff --git a/include/inexor/vulkan-renderer/wrapper/mesh_buffer.hpp b/include/inexor/vulkan-renderer/wrapper/mesh_buffer.hpp deleted file mode 100644 index c667639c6..000000000 --- a/include/inexor/vulkan-renderer/wrapper/mesh_buffer.hpp +++ /dev/null @@ -1,219 +0,0 @@ -#pragma once - -#include "inexor/vulkan-renderer/wrapper/gpu_memory_buffer.hpp" - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace inexor::vulkan_renderer::wrapper { - -class Device; - -/// @brief RAII wrapper class for mesh buffers. -/// In Inexor engine, a mesh buffer is a vertex buffer with a corresponding index buffer. -/// The following example shows how to create a mesh buffer with a custom vertex structure: -/// @code -/// struct ModelVertex { -/// glm::vec3 position{0.0f, 0.0f, 0.0f}; -/// glm::vec3 color{0.0f, 0.0f, 0.0f}; -/// glm::vec3 normal{0.0f, 0.0f, 0.0f}; -/// glm::vec2 uv{0.0f, 0.0f}; -/// }; -/// -/// MeshBuffer myMeshBufferA(1024); // Create a mesh buffer of 1024 vertices -/// MeshBuffer myMeshBufferB(1024, 128); // Create a mesh buffer of 1024 vertices and 128 -/// indices. -/// -/// @tparam VertexType The vertex type. -/// @tparam IndexType The index type, usually uint32_t. -/// @note IndexType can also be uint16_t in some cases, though that allows a lower number of indices. -template -class MeshBuffer { - const Device &m_device; - const std::string &m_name; - - GPUMemoryBuffer m_vertex_buffer; - - std::optional m_index_buffer{std::nullopt}; - - std::uint32_t m_number_of_vertices{0}; - std::uint32_t m_number_of_indices{0}; - -public: - /// @brief Creates a mesh buffer of type VertexType with a corresponding index buffer of type IndexType by - /// specifying the number of vertices and indices but without already specifying any vertex data or index data. - /// @param device The const reference to a device RAII wrapper instance. - /// @param name The internal name of the mesh buffer, must not be empty. - /// @param vertex_count The number of vertices, must be greater than 0. - /// @param index_count The number of indices, must be greater than 0. - MeshBuffer(const Device &device, const std::string &name, const std::size_t vertex_count, - const std::size_t index_count) - // It's no problem to create the vertex buffer and index buffer before the corresponding staging buffers are - // created!. - : m_vertex_buffer(device, name, sizeof(VertexType) * vertex_count, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_ONLY), - m_index_buffer(std::make_optional( - device, name, sizeof(IndexType) * index_count, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_ONLY)), - m_device(device), m_name(name) { - assert(device.device()); - assert(device.allocator()); - assert(!name.empty()); - assert(vertex_count > 0); - assert(index_count > 0); - - std::size_t vertex_buffer_size = sizeof(VertexType) * vertex_count; - std::size_t index_buffer_size = sizeof(IndexType) * index_count; - - spdlog::trace("Creating vertex buffer of size {} for mesh {}", vertex_buffer_size, name); - spdlog::trace("Creating index buffer of size {} for mesh {}", index_buffer_size, name); - } - - /// @brief Creates a mesh buffer of type VertexType without a corresponding index buffer by specifying the number of - /// vertices but without already specifying any vertex data. - /// @param device The const reference to a device RAII wrapper instance. - /// @param name The internal name of the mesh buffer, must not be empty. - /// @param vertex_count The number of vertices, must be greater than 0. - MeshBuffer(const Device &device, const std::string &name, const std::size_t vertex_count) - // It's no problem to create the vertex buffer and index buffer before the corresponding staging buffers are - // created!. - : m_vertex_buffer(device, name, sizeof(VertexType) * vertex_count, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_ONLY), - m_index_buffer(std::nullopt), m_number_of_vertices(vertex_count), m_device(device), m_name(name) { - assert(device.device()); - assert(device.allocator()); - assert(!name.empty()); - - std::size_t vertex_buffer_size = sizeof(VertexType) * vertex_count; - - spdlog::trace("Creating vertex buffer of size {} for mesh {}", vertex_buffer_size, name); - } - - /// @brief Constructs a mesh buffer of type VertexType with a corresponding index buffer of type IndexType. - /// @param device The const reference to a device RAII wrapper instance. - /// @param name The internal name of the mesh buffer. - /// @param vertices A const std::vector of vertices of type VertexType. - /// @param indices A const std::vector of indices of type IndexType. - MeshBuffer(const Device &device, const std::string &name, std::vector &vertices, - std::vector &indices) - : MeshBuffer(device, name, vertices.size(), indices.size()) { - std::size_t vertex_buffer_size = sizeof(VertexType) * vertices.size(); - std::size_t index_buffer_size = sizeof(IndexType) * indices.size(); - - spdlog::trace("Creating vertex buffer of size {} for mesh {}", vertex_buffer_size, name); - spdlog::trace("Creating index buffer of size {} for mesh {}", index_buffer_size, name); - - // Not using an index buffer can decrease performance drastically! - if (index_buffer_size == 0) { - spdlog::warn("Size of index buffer is 0! Always use an index buffer if possible!"); - spdlog::warn("Not using an index buffer decreases performance drastically!"); - } - - m_device.execute(m_name, [&](const CommandBuffer &cmd_buf) { - cmd_buf.copy_buffer(vertices, m_vertex_buffer.buffer(), m_name); - if (!indices.empty()) { - cmd_buf.copy_buffer(indices, m_index_buffer.value().buffer()); - } else { - spdlog::warn("No index buffer created for mesh {}!", name); - } - }); - - update_vertices(vertices); - update_indices(indices); - } - - /// @brief Constructs a mesh buffer of type VertexType without an index buffer. - /// @warning Not using an index buffer will decrease performance drastically! - /// @param device The const reference to a device RAII wrapper instance. - /// @param name The internal name of the mesh buffer. - /// @param vertices A const std::vector of vertices of type VertexType. - MeshBuffer(const Device &device, const std::string &name, const std::vector &vertices) - : MeshBuffer(device, name, vertices.size()) { - std::size_t size_of_vertex_buffer = sizeof(VertexType) * vertices.size(); - - spdlog::trace("Creating vertex buffer of size {} for mesh {}", size_of_vertex_buffer, name); - - // Not using an index buffer can decrease performance drastically! - spdlog::warn("Creating a vertex buffer without an index buffer!"); - spdlog::warn("Always use an index buffer if possible. The performance will decrease drastically otherwise!"); - - VkDeviceSize vertices_memory_size = sizeof(VertexType) * vertices.size(); - - m_device.execute(m_name, [&](const CommandBuffer &cmd_buf) { - cmd_buf.copy_buffer(vertices, m_vertex_buffer.buffer(), m_name); - }); - - update_vertices(vertices); - } - - MeshBuffer(const MeshBuffer &) = delete; - - MeshBuffer(MeshBuffer &&other) noexcept : m_device(other.m_device), m_name(other.m_name) { - m_vertex_buffer = std::move(other.m_vertex_buffer); - m_index_buffer = std::exchange(other.m_index_buffer, std::nullopt); - m_number_of_vertices = other.m_number_of_vertices; - m_number_of_indices = other.m_number_of_indices; - } - - ~MeshBuffer() = default; - - MeshBuffer &operator=(const MeshBuffer &) = delete; - MeshBuffer &operator=(MeshBuffer &&) = delete; - - [[nodiscard]] VkBuffer get_vertex_buffer() const { - return m_vertex_buffer.buffer(); - } - - [[nodiscard]] bool has_index_buffer() const { - return m_index_buffer.has_value(); - } - - [[nodiscard]] VkBuffer get_index_buffer() const { - return m_index_buffer.value().buffer(); - } - - [[nodiscard]] std::uint32_t get_vertex_count() const { - return m_number_of_vertices; - } - - [[nodiscard]] std::uint32_t get_index_count() const { - return m_number_of_indices; - } - - [[nodiscard]] auto get_vertex_buffer_address() const { - return m_vertex_buffer.allocation_info().pMappedData; - } - - [[nodiscard]] auto get_index_buffer_address() const { - if (!m_index_buffer) { - throw std::runtime_error("Error: No index buffer for mesh available"); - } - - return m_index_buffer.value().allocation_info().pMappedData; - } - - void update_vertices(const std::vector &vertices) { - std::memcpy(m_vertex_buffer.allocation_info().pMappedData, vertices.data(), - sizeof(VertexType) * vertices.size()); - } - - void update_indices(const std::vector &indices) { - if (!m_index_buffer) { - throw std::runtime_error("Error: No index buffer for mesh available"); - } - - std::memcpy(m_index_buffer.value().allocation_info().pMappedData, indices.data(), - sizeof(IndexType) * indices.size()); - } -}; - -} // namespace inexor::vulkan_renderer::wrapper diff --git a/include/inexor/vulkan-renderer/wrapper/pipeline.hpp b/include/inexor/vulkan-renderer/wrapper/pipeline.hpp new file mode 100644 index 000000000..771389793 --- /dev/null +++ b/include/inexor/vulkan-renderer/wrapper/pipeline.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +namespace inexor::vulkan_renderer::wrapper { + +// Forward declaration +class Device; + +// TODO: Compute pipelines + +/// RAII wrapper for VkPipeline +class GraphicsPipeline { +private: + const Device &m_device; + VkPipeline m_pipeline{VK_NULL_HANDLE}; + std::string m_name; + +public: + /// Default constructor + /// @param device The device wrapper + /// @param pipeline_ci The pipeline create info + /// @param name The internal debug name of the graphics pipeline + GraphicsPipeline(const Device &device, const VkGraphicsPipelineCreateInfo &pipeline_ci, std::string name); + GraphicsPipeline(const GraphicsPipeline &) = delete; + GraphicsPipeline(GraphicsPipeline &&) noexcept; + ~GraphicsPipeline(); + + GraphicsPipeline &operator=(const GraphicsPipeline &) = delete; + GraphicsPipeline &operator=(GraphicsPipeline &&) = delete; + + [[nodiscard]] VkPipeline pipeline() const noexcept { + return m_pipeline; + } +}; + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/include/inexor/vulkan-renderer/wrapper/pipeline_builder.hpp b/include/inexor/vulkan-renderer/wrapper/pipeline_builder.hpp new file mode 100644 index 000000000..554d06892 --- /dev/null +++ b/include/inexor/vulkan-renderer/wrapper/pipeline_builder.hpp @@ -0,0 +1,213 @@ +#pragma once + +#include + +#include "inexor/vulkan-renderer/wrapper/make_info.hpp" +#include "inexor/vulkan-renderer/wrapper/pipeline.hpp" + +#include +#include +#include + +namespace inexor::vulkan_renderer::wrapper { + +// Forward declarations +class Device; + +/// Builder class for VkPipelineCreateInfo +class GraphicsPipelineBuilder { +private: + const Device &m_device; + VkPipelineVertexInputStateCreateInfo m_vertex_input_sci{make_info()}; + + VkPipelineInputAssemblyStateCreateInfo m_input_assembly_sci{make_info({ + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE, + })}; + + VkPipelineTessellationStateCreateInfo m_tesselation_sci{make_info()}; + VkPipelineViewportStateCreateInfo m_viewport_sci{make_info()}; + + VkPipelineRasterizationStateCreateInfo m_rasterization_sci{make_info({ + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_BACK_BIT, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .lineWidth = 1.0f, + })}; + + // TODO: Support more options for multisampling + VkPipelineMultisampleStateCreateInfo m_multisample_sci{make_info({ + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .minSampleShading = 1.0f, + })}; + + VkPipelineDepthStencilStateCreateInfo m_depth_stencil_sci{make_info()}; + VkPipelineColorBlendStateCreateInfo m_color_blend_sci{make_info()}; + VkPipelineDynamicStateCreateInfo m_dynamic_states_sci{make_info()}; + + VkPipelineLayout m_pipeline_layout{VK_NULL_HANDLE}; + VkRenderPass m_render_pass{VK_NULL_HANDLE}; + + std::vector m_dynamic_states; + std::vector m_viewports; + std::vector m_scissors; + std::vector m_shader_stages; + std::vector m_vertex_input_binding_descriptions; + std::vector m_vertex_input_attribute_descriptions; + std::vector m_color_blend_attachment_states; + +public: + /// Default constructor + /// @param device The device wrapper + GraphicsPipelineBuilder(const Device &device); + GraphicsPipelineBuilder(const GraphicsPipelineBuilder &) = delete; + GraphicsPipelineBuilder(GraphicsPipelineBuilder &&other) noexcept; + ~GraphicsPipelineBuilder() = default; + + GraphicsPipelineBuilder &operator=(const GraphicsPipelineBuilder &) = delete; + GraphicsPipelineBuilder &operator=(GraphicsPipelineBuilder &&) = delete; + + /// Add a shader stage + /// @param shader The shader stage to add + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &add_shader(const VkPipelineShaderStageCreateInfo &shader); + + /// Add a vertex input attribute description + /// @param description The vertex input attribute description + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder & + add_vertex_input_attribute(const VkVertexInputAttributeDescription &description); + + /// Add a vertex input binding description + /// @param description The vertex input binding descriptions + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &add_vertex_input_binding(const VkVertexInputBindingDescription &description); + + /// Add a color blend attachment + /// @param attachment The color blend attachment + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder & + add_color_blend_attachment(const VkPipelineColorBlendAttachmentState &attachment); + + /// Build the graphics pipeline with specified pipeline create flags + /// @param name The debug name of the graphics pipeline + /// @return The unique pointer instance of ``GraphicsPipeline`` that was created + [[nodiscard]] std::unique_ptr build(std::string name); + + /// Set the color blend manually + /// @param color_blend The color blend + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_color_blend(const VkPipelineColorBlendStateCreateInfo &color_blend); + + /// Set all color blend attachments manually + /// @note You should prefer to use ``add_color_blend_attachment`` instead + /// @param attachments The color blend attachments + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder & + set_color_blend_attachments(const std::vector &attachments); + + /// Enable or disable culling + /// @warning Disabling culling will have a significant performance impact + /// @param culling_enabled ``true`` if culling is enabled + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_culling_mode(VkBool32 culling_enabled); + + /// Set the depth stencil + /// @param depth_stencil The depth stencil + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder & + set_depth_stencil(const VkPipelineDepthStencilStateCreateInfo &depth_stencil); + + /// Set the dynamic states + /// @param dynamic_states The dynamic states + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_dynamic_states(const std::vector &dynamic_states); + + /// Set the input assembly state create info + /// @note If you just want to set the triangle topology, call ``set_triangle_topology`` instead, because this is the + /// most powerful method of this method in case you really need to overwrite it + /// @param input_assembly The pipeline input state create info + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder & + set_input_assembly(const VkPipelineInputAssemblyStateCreateInfo &input_assembly); + + /// Set the line width of rasterization + /// @param line_width The line width of rasterization + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_line_width(float width); + + /// Store the pipeline layout + /// @param layout The pipeline layout + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_pipeline_layout(VkPipelineLayout layout); + + /// Set the triangle topology + /// @param topology the primitive topology + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_primitive_topology(VkPrimitiveTopology topology); + + /// Set the rasterization state of the graphics pipeline manually + /// @param rasterization The rasterization state + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder & + set_rasterization(const VkPipelineRasterizationStateCreateInfo &rasterization); + + /// Set the render pass + /// @param render_pass The render pass + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_render_pass(VkRenderPass render_pass); + + /// Set the scissor data in VkPipelineViewportStateCreateInfo + /// There is another method called set_scissors in case multiple scissors will be used + /// @param scissors The scissors in in VkPipelineViewportStateCreateInfo + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_scissor(const VkRect2D &scissor); + + /// Set the viewport data in VkPipelineViewportStateCreateInfo + /// There is another method called set_scissors in case multiple scissors will be used + /// @param scissor The scissor in in VkPipelineViewportStateCreateInfo + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_scissors(const std::vector &scissors); + + /// Set the shader stage + /// @param shader_stages The shader stages + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_shaders(const std::vector &shaders); + + /// Set the tesselation state create info + /// @param control_points The tesselation control point count + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_tesselation(std::uint32_t control_points); + + /// Set the vertex input attribute descriptions manually + /// You should prefer to use ``add_vertex_input_attribute`` instead + /// @param descriptions The vertex input attribute descriptions + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder & + set_vertex_input_attributes(const std::vector &descriptions); + + /// Set the vertex input binding descriptions manually + /// You should prefer to use ``add_vertex_input_binding`` instead + /// @param descriptions The vertex input binding descriptions + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder & + set_vertex_input_bindings(const std::vector &descriptions); + + /// Set the viewport in VkPipelineViewportStateCreateInfo + /// There is another method called set_viewports in case multiple viewports will be used + /// @param viewport The viewport in VkPipelineViewportStateCreateInfo + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_viewport(const VkViewport &viewport); + + /// Set the viewport in VkPipelineViewportStateCreateInfo + /// @param viewports The viewports in VkPipelineViewportStateCreateInfo + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_viewports(const std::vector &viewports); + + /// Set the wireframe mode + /// @param wireframe ``true`` if wireframe is enabled + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_wireframe(VkBool32 wireframe); +}; + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/include/inexor/vulkan-renderer/wrapper/pipeline_layout.hpp b/include/inexor/vulkan-renderer/wrapper/pipeline_layout.hpp new file mode 100644 index 000000000..9634a7a52 --- /dev/null +++ b/include/inexor/vulkan-renderer/wrapper/pipeline_layout.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "inexor/vulkan-renderer/wrapper/device.hpp" + +namespace inexor::vulkan_renderer::wrapper { + +/// RAII wrapper class for VkPipelineLayout +class PipelineLayout { +private: + const Device &m_device; + std::string m_name; + VkPipelineLayout m_pipeline_layout; + +public: + /// Default constructor + /// @param device The device wrapper + /// @param pipeline_layout_ci The pipeline layout create info + /// @param name The name of the pipeline layout + PipelineLayout(const Device &device, const VkPipelineLayoutCreateInfo &pipeline_layout_ci, std::string name); + + /// Default constructor + /// @param device The device wrapper + /// @param descriptor_set_layouts The descriptor set layouts + /// @param push_constant_ranges The push constant ranges + /// @param name The name of the pipeline layout + PipelineLayout(const Device &device, const std::vector &descriptor_set_layouts, + const std::vector &push_constant_ranges, std::string name); + + PipelineLayout(const PipelineLayout &) = delete; + PipelineLayout(PipelineLayout &&) noexcept; + ~PipelineLayout(); + + PipelineLayout &operator=(const PipelineLayout &) = delete; + PipelineLayout &operator=(PipelineLayout &&other) noexcept = delete; + + [[nodiscard]] VkPipelineLayout pipeline_layout() const noexcept { + return m_pipeline_layout; + } +}; + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/include/inexor/vulkan-renderer/wrapper/renderpass.hpp b/include/inexor/vulkan-renderer/wrapper/renderpass.hpp new file mode 100644 index 000000000..76f6e8ac7 --- /dev/null +++ b/include/inexor/vulkan-renderer/wrapper/renderpass.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include +#include + +namespace inexor::vulkan_renderer::wrapper { + +// Forward declaration +class Device; + +/// RAII wrapper for VkRenderPass +class RenderPass { +private: + const Device &m_device; + VkRenderPass m_render_pass{VK_NULL_HANDLE}; + std::string m_name; + +public: + /// Default constructor + /// @param device The device wrapper + /// @param render_pass_ci The render pass create info + /// @param name The internal debug name of the render pass + RenderPass(const Device &device, const VkRenderPassCreateInfo &render_pass_ci, std::string name); + + /// Overloaded constructor which takes the attachment descriptions, subpass descriptions, and subpass dependencies + /// @param device The device wrapper + /// @param render_pass_ci The render pass create info + /// @param attachments The attachment descriptions + /// @param subpasses The subpass descriptions + /// @param dependencies The dependencies + /// @param name The internal debug name of the render pass + RenderPass(const Device &device, const std::vector &attachments, + const std::vector &subpasses, const std::vector &dependencies, + std::string name); + + RenderPass(const RenderPass &) = delete; + RenderPass(RenderPass &&) noexcept; + ~RenderPass(); + + RenderPass &operator=(const RenderPass &) = delete; + RenderPass &operator=(RenderPass &&) = delete; + + [[nodiscard]] VkRenderPass render_pass() const noexcept { + return m_render_pass; + } +}; + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/include/inexor/vulkan-renderer/wrapper/sampler.hpp b/include/inexor/vulkan-renderer/wrapper/sampler.hpp new file mode 100644 index 000000000..e32ca0641 --- /dev/null +++ b/include/inexor/vulkan-renderer/wrapper/sampler.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +namespace inexor::vulkan_renderer::wrapper { + +// Forward declaration +class Device; + +/// RAII wrapper class for VkSampler +class Sampler { +private: + const Device &m_device; + VkSampler m_sampler{VK_NULL_HANDLE}; + std::string m_name; + +public: + /// Default constructor + /// @param device The device wrapper + /// @param sampler_ci The sampler create info + /// @param name The internal debug name of the sampler + Sampler(const Device &device, const VkSamplerCreateInfo &sampler_ci, std::string name); + + /// Overloaded constructor which creates a default sampler + /// @param device The device wrapper + /// @param name The internal debug name of the sampler + Sampler(const Device &device, std::string name); + ~Sampler(); + + Sampler(const Sampler &) = delete; + Sampler(Sampler &&) noexcept; + + Sampler &operator=(const Sampler &) = delete; + Sampler &operator=(Sampler &&) = delete; + + [[nodiscard]] VkSampler sampler() const noexcept { + return m_sampler; + } +}; + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 026aadd14..9e4d18f23 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,7 +34,12 @@ set(INEXOR_SOURCE_FILES vulkan-renderer/wrapper/gpu_texture.cpp vulkan-renderer/wrapper/image.cpp vulkan-renderer/wrapper/instance.cpp + vulkan-renderer/wrapper/pipeline.cpp + vulkan-renderer/wrapper/pipeline_builder.cpp + vulkan-renderer/wrapper/pipeline_layout.cpp vulkan-renderer/wrapper/make_info.cpp + vulkan-renderer/wrapper/renderpass.cpp + vulkan-renderer/wrapper/sampler.cpp vulkan-renderer/wrapper/semaphore.cpp vulkan-renderer/wrapper/shader.cpp vulkan-renderer/wrapper/swapchain.cpp diff --git a/src/vulkan-renderer/imgui.cpp b/src/vulkan-renderer/imgui.cpp index 0f5533a08..860eae2b0 100644 --- a/src/vulkan-renderer/imgui.cpp +++ b/src/vulkan-renderer/imgui.cpp @@ -62,7 +62,7 @@ ImGUIOverlay::ImGUIOverlay(const wrapper::Device &device, const wrapper::Swapcha spdlog::error("Unable to load font {}. Falling back to error texture", FONT_FILE_PATH); m_imgui_texture = std::make_unique(m_device, wrapper::CpuTexture()); } else { - spdlog::trace("Creating ImGUI font texture"); + spdlog::trace("Creating ImGui font texture"); // Our font textures always have 4 channels and a single mip level by definition. constexpr int FONT_TEXTURE_CHANNELS{4}; @@ -74,7 +74,7 @@ ImGUIOverlay::ImGUIOverlay(const wrapper::Device &device, const wrapper::Swapcha m_imgui_texture = std::make_unique( m_device, font_texture_data, upload_size, font_texture_width, font_texture_height, FONT_TEXTURE_CHANNELS, - FONT_MIP_LEVELS, "ImGUI font texture"); + FONT_MIP_LEVELS, "ImGui font texture"); } // Create an instance of the resource descriptor builder. @@ -84,42 +84,38 @@ ImGUIOverlay::ImGUIOverlay(const wrapper::Device &device, const wrapper::Swapcha // Make use of the builder to create a resource descriptor for the combined image sampler. m_descriptor = std::make_unique( descriptor_builder.add_combined_image_sampler(m_imgui_texture->sampler(), m_imgui_texture->image_view(), 0) - .build("ImGUI")); - - m_index_buffer = render_graph->add("imgui index buffer", BufferUsage::INDEX_BUFFER); - m_vertex_buffer = render_graph->add("imgui vertex buffer", BufferUsage::VERTEX_BUFFER); - m_vertex_buffer->add_vertex_attribute(VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, pos)); - m_vertex_buffer->add_vertex_attribute(VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, uv)); - m_vertex_buffer->add_vertex_attribute(VK_FORMAT_R8G8B8A8_UNORM, offsetof(ImDrawVert, col)); - m_vertex_buffer->set_element_size(sizeof(ImDrawVert)); - - m_stage = render_graph->add("imgui stage"); - m_stage->writes_to(back_buffer); - m_stage->reads_from(m_index_buffer); - m_stage->reads_from(m_vertex_buffer); - m_stage->bind_buffer(m_vertex_buffer, 0); - m_stage->uses_shader(*m_vertex_shader); - m_stage->uses_shader(*m_fragment_shader); - - m_stage->add_descriptor_layout(m_descriptor->descriptor_set_layout()); - - // Setup push constant range for global translation and scale. - m_stage->add_push_constant_range({ - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, - .offset = 0, - .size = sizeof(PushConstBlock), - }); + .build("ImGui")); + + m_index_buffer = render_graph->add("ImGui index buffer", BufferUsage::INDEX_BUFFER); + m_vertex_buffer = render_graph->add("ImGui vertex buffer", BufferUsage::VERTEX_BUFFER); - // Setup blend attachment. - m_stage->set_blend_attachment({ - .blendEnable = VK_TRUE, - .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, - .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, - .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, - .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .alphaBlendOp = VK_BLEND_OP_ADD, + m_vertex_buffer->set_vertex_attributes({ + {.location = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(ImDrawVert, pos)}, + {.location = 1, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(ImDrawVert, uv)}, + {.location = 2, .format = VK_FORMAT_R8G8B8A8_UNORM, .offset = offsetof(ImDrawVert, col)}, }); + + // All aspects of the GraphicsStage + m_stage = render_graph->add("ImGui") + ->bind_buffer(m_vertex_buffer, 0) + ->uses_shader(*m_vertex_shader) + ->uses_shader(*m_fragment_shader) + ->set_blend_attachment({ + .blendEnable = VK_TRUE, + .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + }); + + // All aspects of the RenderStage + m_stage->writes_to(back_buffer) + ->reads_from(m_index_buffer) + ->reads_from(m_vertex_buffer) + ->add_descriptor_layout(m_descriptor->descriptor_set_layout()) + ->add_push_constant_range(); // Setup push constant range for global translation and scale } ImGUIOverlay::~ImGUIOverlay() { diff --git a/src/vulkan-renderer/render_graph.cpp b/src/vulkan-renderer/render_graph.cpp index 9c17a6d7e..dca238e17 100644 --- a/src/vulkan-renderer/render_graph.cpp +++ b/src/vulkan-renderer/render_graph.cpp @@ -2,6 +2,7 @@ #include "inexor/vulkan-renderer/exception.hpp" #include "inexor/vulkan-renderer/wrapper/make_info.hpp" +#include "inexor/vulkan-renderer/wrapper/pipeline_builder.hpp" #include #include @@ -16,75 +17,54 @@ namespace inexor::vulkan_renderer { -void BufferResource::add_vertex_attribute(VkFormat format, std::uint32_t offset) { - m_vertex_attributes.push_back({ - .location = static_cast(m_vertex_attributes.size()), - .format = format, - .offset = offset, - }); -} - -void RenderStage::writes_to(const RenderResource *resource) { +RenderStage *RenderStage::writes_to(const RenderResource *resource) { m_writes.push_back(resource); + return this; } -void RenderStage::reads_from(const RenderResource *resource) { +RenderStage *RenderStage::reads_from(const RenderResource *resource) { m_reads.push_back(resource); + return this; } -void GraphicsStage::bind_buffer(const BufferResource *buffer, const std::uint32_t binding) { +GraphicsStage *GraphicsStage::bind_buffer(const BufferResource *buffer, const std::uint32_t binding) { m_buffer_bindings.emplace(buffer, binding); + return this; } -void GraphicsStage::uses_shader(const wrapper::Shader &shader) { +GraphicsStage *GraphicsStage::uses_shader(const wrapper::Shader &shader) { m_shaders.push_back(wrapper::make_info({ .stage = shader.type(), .module = shader.module(), .pName = shader.entry_point().c_str(), })); + return this; } -PhysicalBuffer::~PhysicalBuffer() { - vmaDestroyBuffer(m_device.allocator(), m_buffer, m_allocation); -} - -PhysicalImage::~PhysicalImage() { - vkDestroyImageView(m_device.device(), m_image_view, nullptr); - vmaDestroyImage(m_device.allocator(), m_image, m_allocation); -} - -PhysicalStage::~PhysicalStage() { - vkDestroyPipeline(m_device.device(), m_pipeline, nullptr); - vkDestroyPipelineLayout(m_device.device(), m_pipeline_layout, nullptr); +GraphicsStage *GraphicsStage::uses_shaders(const std::span shaders) { + for (const auto &shader : shaders) { + uses_shader(shader); + } + return this; } -PhysicalGraphicsStage::~PhysicalGraphicsStage() { - vkDestroyRenderPass(m_device.device(), m_render_pass, nullptr); +PhysicalBuffer::~PhysicalBuffer() { + vmaDestroyBuffer(m_device.allocator(), m_buffer, m_allocation); } void RenderGraph::build_buffer(const BufferResource &buffer_resource, PhysicalBuffer &physical) const { - // TODO: Don't always create mapped. const VmaAllocationCreateInfo alloc_ci{ .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, .usage = VMA_MEMORY_USAGE_CPU_TO_GPU, }; - auto buffer_ci = wrapper::make_info({ + const auto buffer_ci = wrapper::make_info({ .size = buffer_resource.m_data_size, + .usage = buffer_resource.m_usage == BufferUsage::INDEX_BUFFER ? VK_BUFFER_USAGE_INDEX_BUFFER_BIT + : VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }); - switch (buffer_resource.m_usage) { - case BufferUsage::INDEX_BUFFER: - buffer_ci.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - break; - case BufferUsage::VERTEX_BUFFER: - buffer_ci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - break; - default: - assert(false); - } - if (const auto result = vmaCreateBuffer(m_device.allocator(), &buffer_ci, &alloc_ci, &physical.m_buffer, &physical.m_allocation, &physical.m_alloc_info); result != VK_SUCCESS) { @@ -95,9 +75,8 @@ void RenderGraph::build_buffer(const BufferResource &buffer_resource, PhysicalBu vmaSetAllocationName(m_device.allocator(), physical.m_allocation, "rendergraph buffer"); } -void RenderGraph::build_image(const TextureResource &texture_resource, PhysicalImage &physical, - VmaAllocationCreateInfo *alloc_ci) const { - const auto image_ci = wrapper::make_info({ +void RenderGraph::build_image(const TextureResource &texture_resource, PhysicalImage &physical) const { + const auto img_ci = wrapper::make_info({ .imageType = VK_IMAGE_TYPE_2D, .format = texture_resource.m_format, .extent{ @@ -117,21 +96,8 @@ void RenderGraph::build_image(const TextureResource &texture_resource, PhysicalI .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }); - VmaAllocationInfo alloc_info; - // TODO: Assign proper name to this image inside of rendergraph - if (const auto result = vmaCreateImage(m_device.allocator(), &image_ci, alloc_ci, &physical.m_image, - &physical.m_allocation, &alloc_info); - result != VK_SUCCESS) { - throw VulkanException("Error: vkCreateImage failed for rendergraph image", result); - } - - // TODO: Use a better naming system for memory resources inside of rendergraph - vmaSetAllocationName(m_device.allocator(), physical.m_allocation, "rendergraph image"); -} - -void RenderGraph::build_image_view(const TextureResource &texture_resource, PhysicalImage &physical) const { - const auto image_view_ci = wrapper::make_info({ - .image = physical.m_image, + const auto img_view_ci = wrapper::make_info({ + // Note that .image is set by wrapper::Image itself .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = texture_resource.m_format, .subresourceRange{ @@ -143,27 +109,18 @@ void RenderGraph::build_image_view(const TextureResource &texture_resource, Phys }, }); - if (const auto result = vkCreateImageView(m_device.device(), &image_view_ci, nullptr, &physical.m_image_view); - result != VK_SUCCESS) { - throw VulkanException("Error: vkCreateImageView failed for image view " + texture_resource.m_name + "!", - result); - } + const VmaAllocationCreateInfo alloc_ci{ + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, + .usage = VMA_MEMORY_USAGE_GPU_ONLY, + }; + + // TODO: Use a better naming system for memory resources inside of rendergraph + physical.m_img = std::make_unique(m_device, img_ci, alloc_ci, img_view_ci, "rendergraph image"); } void RenderGraph::build_pipeline_layout(const RenderStage *stage, PhysicalStage &physical) const { - const auto pipeline_layout_ci = wrapper::make_info({ - .setLayoutCount = static_cast(stage->m_descriptor_layouts.size()), - .pSetLayouts = stage->m_descriptor_layouts.data(), - .pushConstantRangeCount = static_cast(stage->m_push_constant_ranges.size()), - .pPushConstantRanges = stage->m_push_constant_ranges.data(), - }); - - if (const auto result = - vkCreatePipelineLayout(m_device.device(), &pipeline_layout_ci, nullptr, &physical.m_pipeline_layout); - result != VK_SUCCESS) { - throw VulkanException("Error: vkCreatePipelineLayout failed for pipeline layout " + stage->name() + "!", - result); - } + physical.m_pipeline_layout = std::make_unique( + m_device, stage->m_descriptor_layouts, stage->m_push_constant_ranges, "graphics pipeline"); } void RenderGraph::record_command_buffer(const RenderStage *stage, const wrapper::CommandBuffer &cmd_buf, @@ -183,7 +140,7 @@ void RenderGraph::record_command_buffer(const RenderStage *stage, const wrapper: } cmd_buf.begin_render_pass(wrapper::make_info({ - .renderPass = phys_graphics_stage->m_render_pass, + .renderPass = phys_graphics_stage->m_render_pass->render_pass(), .framebuffer = phys_graphics_stage->m_framebuffers.at(image_index).get(), .renderArea{ .extent = m_swapchain.extent(), @@ -215,7 +172,7 @@ void RenderGraph::record_command_buffer(const RenderStage *stage, const wrapper: cmd_buf.bind_vertex_buffers(vertex_buffers); } - cmd_buf.bind_pipeline(physical.m_pipeline); + cmd_buf.bind_pipeline(physical.m_pipeline->pipeline()); stage->m_on_record(physical, cmd_buf); if (graphics_stage != nullptr) { @@ -273,35 +230,27 @@ void RenderGraph::build_render_pass(const GraphicsStage *stage, PhysicalGraphics attachments.push_back(attachment); } - // Build a simple subpass that just waits for the output colour vector to be written by the fragment shader. In the - // future, we may want to make use of subpasses more. - const VkSubpassDependency subpass_dependency{ - .srcSubpass = VK_SUBPASS_EXTERNAL, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + const std::vector subpasses{ + { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .colorAttachmentCount = static_cast(colour_refs.size()), + .pColorAttachments = colour_refs.data(), + .pDepthStencilAttachment = !depth_refs.empty() ? depth_refs.data() : nullptr, + }, }; - const VkSubpassDescription subpass_description{ - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .colorAttachmentCount = static_cast(colour_refs.size()), - .pColorAttachments = colour_refs.data(), - .pDepthStencilAttachment = !depth_refs.empty() ? depth_refs.data() : nullptr, + // Build a simple subpass that just waits for the output colour vector to be written by the fragment shader + const std::vector dependencies{ + { + .srcSubpass = VK_SUBPASS_EXTERNAL, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + }, }; - const auto render_pass_ci = wrapper::make_info({ - .attachmentCount = static_cast(attachments.size()), - .pAttachments = attachments.data(), - .subpassCount = 1, - .pSubpasses = &subpass_description, - .dependencyCount = 1, - .pDependencies = &subpass_dependency, - }); - - if (const auto result = vkCreateRenderPass(m_device.device(), &render_pass_ci, nullptr, &physical.m_render_pass); - result != VK_SUCCESS) { - throw VulkanException("Error: vkCreateRenderPass failed for renderpass " + stage->name() + " !", result); - } + physical.m_render_pass = + std::make_unique(m_device, attachments, subpasses, dependencies, "renderpass"); } void RenderGraph::build_graphics_pipeline(const GraphicsStage *stage, PhysicalGraphicsStage &physical) const { @@ -322,7 +271,7 @@ void RenderGraph::build_graphics_pipeline(const GraphicsStage *stage, PhysicalGr // We use std::unordered_map::at() here to ensure that a binding value exists for buffer_resource. const std::uint32_t binding = stage->m_buffer_bindings.at(buffer_resource); - for (auto attribute_binding : buffer_resource->m_vertex_attributes) { + for (auto attribute_binding : buffer_resource->m_vert_input_attr_descs) { attribute_binding.binding = binding; attribute_bindings.push_back(attribute_binding); } @@ -334,88 +283,36 @@ void RenderGraph::build_graphics_pipeline(const GraphicsStage *stage, PhysicalGr }); } - const auto vertex_input = wrapper::make_info({ - .vertexBindingDescriptionCount = static_cast(vertex_bindings.size()), - .pVertexBindingDescriptions = vertex_bindings.data(), - .vertexAttributeDescriptionCount = static_cast(attribute_bindings.size()), - .pVertexAttributeDescriptions = attribute_bindings.data(), - }); - - // TODO: Support primitives other than triangles. - const auto input_assembly = wrapper::make_info({ - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - .primitiveRestartEnable = VK_FALSE, - }); - - // TODO: Also allow depth compare func to be changed? - const auto depth_stencil = wrapper::make_info({ - .depthTestEnable = stage->m_depth_test ? VK_TRUE : VK_FALSE, - .depthWriteEnable = stage->m_depth_write ? VK_TRUE : VK_FALSE, - .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, - }); - - // TODO: Allow culling to be disabled. - // TODO: Wireframe rendering. - const auto rasterization_state = wrapper::make_info({ - .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = VK_CULL_MODE_BACK_BIT, - .frontFace = VK_FRONT_FACE_CLOCKWISE, - .lineWidth = 1.0f, - }); - - // TODO(GH-203): Support multisampling again. - const auto multisample_state = wrapper::make_info({ - .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, - .minSampleShading = 1.0f, - }); - auto blend_attachment = stage->m_blend_attachment; blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - const auto blend_state = wrapper::make_info({ - .attachmentCount = 1, - .pAttachments = &blend_attachment, - }); - - const VkRect2D scissor{ - .extent = m_swapchain.extent(), - }; - - const VkViewport viewport{ - .width = static_cast(m_swapchain.extent().width), - .height = static_cast(m_swapchain.extent().height), - .maxDepth = 1.0f, - }; - - // TODO: Custom scissors? - const auto viewport_state = wrapper::make_info({ - .viewportCount = 1, - .pViewports = &viewport, - .scissorCount = 1, - .pScissors = &scissor, - }); - - const auto pipeline_ci = wrapper::make_info({ - .stageCount = static_cast(stage->m_shaders.size()), - .pStages = stage->m_shaders.data(), - .pVertexInputState = &vertex_input, - .pInputAssemblyState = &input_assembly, - .pViewportState = &viewport_state, - .pRasterizationState = &rasterization_state, - .pMultisampleState = &multisample_state, - .pDepthStencilState = &depth_stencil, - .pColorBlendState = &blend_state, - .layout = physical.m_pipeline_layout, - .renderPass = physical.m_render_pass, - }); + // Create a graphics pipeline builder + std::unique_ptr builder = + std::make_unique(m_device); // TODO: Pipeline caching (basically load the render graph from a file) - if (const auto result = - vkCreateGraphicsPipelines(m_device.device(), nullptr, 1, &pipeline_ci, nullptr, &physical.m_pipeline); - result != VK_SUCCESS) { - throw VulkanException("Error: vkCreateGraphicsPipelines failed for pipeline " + stage->name() + " !", result); - } + physical.m_pipeline = builder->set_shaders(stage->m_shaders) + .set_vertex_input_attributes(attribute_bindings) + .set_vertex_input_bindings(vertex_bindings) + .set_depth_stencil(wrapper::make_info({ + .depthTestEnable = stage->m_depth_test ? VK_TRUE : VK_FALSE, + .depthWriteEnable = stage->m_depth_write ? VK_TRUE : VK_FALSE, + .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, + })) + .set_color_blend(wrapper::make_info({ + .attachmentCount = 1, + .pAttachments = &blend_attachment, + })) + .set_viewport({ + .width = static_cast(m_swapchain.extent().width), + .height = static_cast(m_swapchain.extent().height), + .maxDepth = 1.0f, + }) + .set_scissor({.extent = m_swapchain.extent()}) + .set_pipeline_layout(physical.pipeline_layout()) + .set_render_pass(physical.m_render_pass->render_pass()) + .build("graphics pipeline"); } void RenderGraph::compile(const RenderResource *target) { @@ -452,8 +349,8 @@ void RenderGraph::compile(const RenderResource *target) { m_log->trace(" - {}", stage->m_name); } - // Create physical resources. For now, each buffer or texture resource maps directly to either a VkBuffer or VkImage - // respectively. Every physical resource also has a VmaAllocation. + // Create physical resources. For now, each buffer or texture resource maps directly to either a VkBuffer or + // VkImage respectively. Every physical resource also has a VmaAllocation. // TODO: Resource aliasing (i.e. reusing the same physical resource for multiple resources). m_log->trace("Allocating physical resource for buffers:"); @@ -473,18 +370,13 @@ void RenderGraph::compile(const RenderResource *target) { continue; } - // TODO: Use a constexpr bool. - VmaAllocationCreateInfo alloc_ci{}; - alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY; - auto physical = std::make_shared(m_device); texture_resource->m_physical = physical; - build_image(*texture_resource, *physical, &alloc_ci); - build_image_view(*texture_resource, *physical); + build_image(*texture_resource, *physical); } - // Create physical stages. Each render stage maps to a vulkan pipeline (either compute or graphics) and a list of - // command buffers. Each graphics stage also maps to a vulkan render pass. + // Create physical stages. Each render stage maps to a vulkan pipeline (either compute or graphics) and a list + // of command buffers. Each graphics stage also maps to a vulkan render pass. for (auto *stage : m_stage_stack) { if (auto *graphics_stage = stage->as()) { auto physical_ptr = std::make_unique(m_device); @@ -516,10 +408,10 @@ void RenderGraph::compile(const RenderResource *target) { for (auto *const img_view : m_swapchain.image_views()) { std::fill_n(std::back_inserter(image_views), back_buffers.size(), img_view); for (const auto *image : images) { - image_views.push_back(image->m_image_view); + image_views.push_back(image->image_view()); } - physical.m_framebuffers.emplace_back(m_device, physical.m_render_pass, image_views, m_swapchain, - "Framebuffer"); + physical.m_framebuffers.emplace_back(m_device, physical.m_render_pass->render_pass(), image_views, + m_swapchain, "Framebuffer"); image_views.clear(); } } diff --git a/src/vulkan-renderer/renderer.cpp b/src/vulkan-renderer/renderer.cpp index 5b1f7fd72..e42dcbe92 100644 --- a/src/vulkan-renderer/renderer.cpp +++ b/src/vulkan-renderer/renderer.cpp @@ -17,38 +17,39 @@ namespace inexor::vulkan_renderer { void VulkanRenderer::setup_render_graph() { - m_back_buffer = m_render_graph->add("back buffer", TextureUsage::BACK_BUFFER); - m_back_buffer->set_format(m_swapchain->image_format()); + m_back_buffer = + m_render_graph->add("back buffer", TextureUsage::BACK_BUFFER, m_swapchain->image_format()); - auto *depth_buffer = m_render_graph->add("depth buffer", TextureUsage::DEPTH_STENCIL_BUFFER); - depth_buffer->set_format(VK_FORMAT_D32_SFLOAT_S8_UINT); + auto *depth_buffer = m_render_graph->add("depth buffer", TextureUsage::DEPTH_STENCIL_BUFFER, + VK_FORMAT_D32_SFLOAT_S8_UINT); m_index_buffer = m_render_graph->add("index buffer", BufferUsage::INDEX_BUFFER); m_index_buffer->upload_data(m_octree_indices); m_vertex_buffer = m_render_graph->add("vertex buffer", BufferUsage::VERTEX_BUFFER); - m_vertex_buffer->add_vertex_attribute(VK_FORMAT_R32G32B32_SFLOAT, offsetof(OctreeGpuVertex, position)); // NOLINT - m_vertex_buffer->add_vertex_attribute(VK_FORMAT_R32G32B32_SFLOAT, offsetof(OctreeGpuVertex, color)); // NOLINT - m_vertex_buffer->upload_data(m_octree_vertices); - - auto *main_stage = m_render_graph->add("main stage"); - main_stage->writes_to(m_back_buffer); - main_stage->writes_to(depth_buffer); - main_stage->reads_from(m_index_buffer); - main_stage->reads_from(m_vertex_buffer); - main_stage->bind_buffer(m_vertex_buffer, 0); - main_stage->set_clears_screen(true); - main_stage->set_depth_options(true, true); - main_stage->set_on_record([&](const PhysicalStage &physical, const wrapper::CommandBuffer &cmd_buf) { - cmd_buf.bind_descriptor_sets(m_descriptors[0].descriptor_sets(), physical.pipeline_layout()); - cmd_buf.draw_indexed(static_cast(m_octree_indices.size())); - }); - - for (const auto &shader : m_shaders) { - main_stage->uses_shader(shader); - } - main_stage->add_descriptor_layout(m_descriptors[0].descriptor_set_layout()); + m_vertex_buffer + ->set_vertex_attributes({ + {.location = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(OctreeGpuVertex, position)}, + {.location = 1, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(OctreeGpuVertex, color)}, + }) + ->upload_data(m_octree_vertices); + + const auto *main_stage = + m_render_graph->add("Octree") + ->bind_buffer(m_vertex_buffer, 0) + ->uses_shaders(m_shaders) + ->set_clears_screen(true) + ->set_depth_options(true, true) + ->writes_to(m_back_buffer) + ->writes_to(depth_buffer) + ->reads_from(m_index_buffer) + ->reads_from(m_vertex_buffer) + ->set_on_record([&](const PhysicalStage &physical, const wrapper::CommandBuffer &cmd_buf) { + cmd_buf.bind_descriptor_sets(m_descriptors[0].descriptor_sets(), physical.pipeline_layout()); + cmd_buf.draw_indexed(static_cast(m_octree_indices.size())); + }) + ->add_descriptor_layout(m_descriptors[0].descriptor_set_layout()); } void VulkanRenderer::generate_octree_indices() { diff --git a/src/vulkan-renderer/wrapper/device.cpp b/src/vulkan-renderer/wrapper/device.cpp index 8a6fe505e..6c58f5ba3 100644 --- a/src/vulkan-renderer/wrapper/device.cpp +++ b/src/vulkan-renderer/wrapper/device.cpp @@ -569,7 +569,6 @@ void Device::create_command_pool(const VkCommandPoolCreateInfo &command_pool_ci, result != VK_SUCCESS) { throw VulkanException("Error: vkCreateCommandPool failed for command pool " + name + "!", result); } - set_debug_marker_name(&command_pool, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT, name); } @@ -579,7 +578,6 @@ void Device::create_descriptor_pool(const VkDescriptorPoolCreateInfo &descriptor result != VK_SUCCESS) { throw VulkanException("Error: vkCreateDescriptorPool failed for descriptor pool " + name + " !", result); } - set_debug_marker_name(&descriptor_pool, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT, name); } @@ -590,7 +588,6 @@ void Device::create_descriptor_set_layout(const VkDescriptorSetLayoutCreateInfo result != VK_SUCCESS) { throw VulkanException("Error: vkCreateDescriptorSetLayout failed for descriptor " + name + " !", result); } - set_debug_marker_name(&descriptor_set_layout, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT, name); } @@ -598,7 +595,6 @@ void Device::create_fence(const VkFenceCreateInfo &fence_ci, VkFence *fence, con if (const auto result = vkCreateFence(m_device, &fence_ci, nullptr, fence); result != VK_SUCCESS) { throw VulkanException("Error: vkCreateFence failed for fence " + name + "!", result); } - set_debug_marker_name(&fence, VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT, name); } @@ -608,7 +604,6 @@ void Device::create_framebuffer(const VkFramebufferCreateInfo &framebuffer_ci, V result != VK_SUCCESS) { throw VulkanException("Error: vkCreateFramebuffer failed for framebuffer " + name + "!", result); } - set_debug_marker_name(&framebuffer, VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, name); } @@ -618,17 +613,24 @@ void Device::create_graphics_pipeline(const VkGraphicsPipelineCreateInfo &pipeli result != VK_SUCCESS) { throw VulkanException("Error: vkCreateGraphicsPipelines failed for pipeline " + name + " !", result); } - set_debug_marker_name(&pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, name); } -void Device::create_image_view(const VkImageViewCreateInfo &image_view_ci, VkImageView *image_view, +void Device::create_image(const VkImageCreateInfo &img_ci, const VmaAllocationCreateInfo &img_alloc_ci, VkImage *img, + VmaAllocation *alloc, VmaAllocationInfo *alloc_info, const std::string &name) const { + if (const auto result = vmaCreateImage(m_allocator, &img_ci, &img_alloc_ci, img, alloc, alloc_info); + result != VK_SUCCESS) { + throw VulkanException("Error: vmaCreateImage failed for image " + name + "!", result); + } + set_debug_marker_name(&img, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, name); +} + +void Device::create_image_view(const VkImageViewCreateInfo &img_view_ci, VkImageView *img_view, const std::string &name) const { - if (const auto result = vkCreateImageView(m_device, &image_view_ci, nullptr, image_view); result != VK_SUCCESS) { + if (const auto result = vkCreateImageView(m_device, &img_view_ci, nullptr, img_view); result != VK_SUCCESS) { throw VulkanException("Error: vkCreateImageView failed for image view " + name + "!", result); } - - set_debug_marker_name(&image_view, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT, name); + set_debug_marker_name(&img_view, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT, name); } void Device::create_pipeline_layout(const VkPipelineLayoutCreateInfo &pipeline_layout_ci, @@ -637,7 +639,6 @@ void Device::create_pipeline_layout(const VkPipelineLayoutCreateInfo &pipeline_l result != VK_SUCCESS) { throw VulkanException("Error: vkCreatePipelineLayout failed for pipeline layout " + name + "!", result); } - set_debug_marker_name(&pipeline_layout, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT, name); } @@ -646,7 +647,6 @@ void Device::create_render_pass(const VkRenderPassCreateInfo &render_pass_ci, Vk if (const auto result = vkCreateRenderPass(m_device, &render_pass_ci, nullptr, render_pass); result != VK_SUCCESS) { throw VulkanException("Error: vkCreateRenderPass failed for renderpass " + name + " !", result); } - set_debug_marker_name(&render_pass, VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, name); } @@ -654,7 +654,6 @@ void Device::create_sampler(const VkSamplerCreateInfo &sampler_ci, VkSampler *sa if (const auto result = vkCreateSampler(m_device, &sampler_ci, nullptr, sampler); result != VK_SUCCESS) { throw VulkanException("Error: vkCreateSampler failed for sampler " + name + " !", result); } - set_debug_marker_name(&sampler, VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT, name); } @@ -663,7 +662,6 @@ void Device::create_semaphore(const VkSemaphoreCreateInfo &semaphore_ci, VkSemap if (const auto result = vkCreateSemaphore(m_device, &semaphore_ci, nullptr, semaphore); result != VK_SUCCESS) { throw VulkanException("Error: vkCreateSemaphore failed for " + name + " !", result); } - set_debug_marker_name(&semaphore, VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT, name); } @@ -673,7 +671,6 @@ void Device::create_shader_module(const VkShaderModuleCreateInfo &shader_module_ result != VK_SUCCESS) { throw VulkanException("Error: vkCreateShaderModule failed for shader module " + name + "!", result); } - set_debug_marker_name(&shader_module, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, name); } @@ -682,7 +679,6 @@ void Device::create_swapchain(const VkSwapchainCreateInfoKHR &swapchain_ci, VkSw if (const auto result = vkCreateSwapchainKHR(m_device, &swapchain_ci, nullptr, swapchain); result != VK_SUCCESS) { throw VulkanException("Error: vkCreateSwapchainKHR failed for swapchain " + name + "!", result); } - set_debug_marker_name(&swapchain, VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT, name); } diff --git a/src/vulkan-renderer/wrapper/gpu_texture.cpp b/src/vulkan-renderer/wrapper/gpu_texture.cpp index 85b9e31bb..e05cabd02 100644 --- a/src/vulkan-renderer/wrapper/gpu_texture.cpp +++ b/src/vulkan-renderer/wrapper/gpu_texture.cpp @@ -2,53 +2,75 @@ #include "inexor/vulkan-renderer/exception.hpp" #include "inexor/vulkan-renderer/wrapper/cpu_texture.hpp" +#include "inexor/vulkan-renderer/wrapper/device.hpp" #include "inexor/vulkan-renderer/wrapper/make_info.hpp" -#include -#include - #include namespace inexor::vulkan_renderer::wrapper { -GpuTexture::GpuTexture(const wrapper::Device &device, const CpuTexture &cpu_texture) +GpuTexture::GpuTexture(const Device &device, const CpuTexture &cpu_texture) : m_device(device), m_texture_width(cpu_texture.width()), m_texture_height(cpu_texture.height()), m_texture_channels(cpu_texture.channels()), m_mip_levels(cpu_texture.mip_levels()), m_name(cpu_texture.name()) { create_texture(cpu_texture.data(), cpu_texture.data_size()); } -GpuTexture::GpuTexture(const wrapper::Device &device, void *data, const std::size_t data_size, const int texture_width, +GpuTexture::GpuTexture(const Device &device, void *data, const std::size_t data_size, const int texture_width, const int texture_height, const int texture_channels, const int mip_levels, std::string name) : m_device(device), m_texture_width(texture_width), m_texture_height(texture_height), m_texture_channels(texture_channels), m_mip_levels(mip_levels), m_name(std::move(name)) { create_texture(data, data_size); } -GpuTexture::GpuTexture(GpuTexture &&other) noexcept - : m_device(other.m_device), m_texture_image_format(other.m_texture_image_format) { +GpuTexture::GpuTexture(GpuTexture &&other) noexcept : m_device(other.m_device) { m_texture_image = std::exchange(other.m_texture_image, nullptr); m_name = std::move(other.m_name); m_texture_width = other.m_texture_width; m_texture_height = other.m_texture_height; m_texture_channels = other.m_texture_channels; + m_texture_format = other.m_texture_format; m_mip_levels = other.m_mip_levels; m_sampler = std::exchange(other.m_sampler, nullptr); } -GpuTexture::~GpuTexture() { - vkDestroySampler(m_device.device(), m_sampler, nullptr); -} +GpuTexture::~GpuTexture() {} void GpuTexture::create_texture(void *texture_data, const std::size_t texture_size) { - const VkExtent2D extent{ - // Because stb_image stored the texture's width and height as a normal int, we need a cast here - .width = static_cast(m_texture_width), - .height = static_cast(m_texture_height), + const auto img_view = wrapper::make_info({ + .imageType = VK_IMAGE_TYPE_2D, + .format = m_texture_format, + .extent{ + .width = static_cast(m_texture_width), + .height = static_cast(m_texture_height), + .depth = 1, + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }); + + const auto img_view_ci = wrapper::make_info({ + // Note that .image and .format are set by wrapper::Image itself + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }); + + const VmaAllocationCreateInfo alloc_ci{ + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, + .usage = VMA_MEMORY_USAGE_GPU_ONLY, }; - m_texture_image = std::make_unique( - m_device, m_texture_image_format, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - VK_IMAGE_ASPECT_COLOR_BIT, VK_SAMPLE_COUNT_1_BIT, m_name, extent); + m_texture_image = std::make_unique(m_device, img_view, alloc_ci, img_view_ci, "GpuTexture"); const VkBufferImageCopy copy_region{ .bufferOffset = 0, @@ -62,43 +84,16 @@ void GpuTexture::create_texture(void *texture_data, const std::size_t texture_si m_device.execute(m_name, [&](const CommandBuffer &cmd_buf) { cmd_buf - .change_image_layout(m_texture_image->get(), VK_IMAGE_LAYOUT_UNDEFINED, + .change_image_layout(m_texture_image->image(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) - .copy_buffer_to_image(texture_data, static_cast(texture_size), m_texture_image->get(), + .copy_buffer_to_image(texture_data, static_cast(texture_size), m_texture_image->image(), copy_region, m_name) - .change_image_layout(m_texture_image->get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .change_image_layout(m_texture_image->image(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); }); - create_texture_sampler(); -} - -void GpuTexture::create_texture_sampler() { - VkPhysicalDeviceFeatures device_features; - vkGetPhysicalDeviceFeatures(m_device.physical_device(), &device_features); - - VkPhysicalDeviceProperties graphics_card_properties; - vkGetPhysicalDeviceProperties(m_device.physical_device(), &graphics_card_properties); - - const auto sampler_ci = make_info({ - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .mipLodBias = 0.0f, - .anisotropyEnable = VK_FALSE, - .maxAnisotropy = 1.0f, - .compareEnable = VK_FALSE, - .compareOp = VK_COMPARE_OP_ALWAYS, - .minLod = 0.0f, - .maxLod = 0.0f, - .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, - .unnormalizedCoordinates = VK_FALSE, - }); - - m_device.create_sampler(sampler_ci, &m_sampler, m_name); + // Create a default texture sampler + m_sampler = std::make_unique(m_device, m_name); } } // namespace inexor::vulkan_renderer::wrapper diff --git a/src/vulkan-renderer/wrapper/image.cpp b/src/vulkan-renderer/wrapper/image.cpp index a427ea278..a3c896ae8 100644 --- a/src/vulkan-renderer/wrapper/image.cpp +++ b/src/vulkan-renderer/wrapper/image.cpp @@ -1,86 +1,38 @@ #include "inexor/vulkan-renderer/wrapper/image.hpp" -#include "inexor/vulkan-renderer/exception.hpp" #include "inexor/vulkan-renderer/wrapper/device.hpp" #include "inexor/vulkan-renderer/wrapper/make_info.hpp" -#include - +#include #include namespace inexor::vulkan_renderer::wrapper { -Image::Image(const Device &device, const VkFormat format, const VkImageUsageFlags image_usage, - const VkImageAspectFlags aspect_flags, const VkSampleCountFlagBits sample_count, const std::string &name, - const VkExtent2D image_extent) - : m_device(device), m_format(format), m_name(name) { - assert(device.device()); - assert(device.physical_device()); - assert(device.allocator()); - assert(image_extent.width > 0); - assert(image_extent.height > 0); - assert(!name.empty()); - - const auto image_ci = make_info({ - .imageType = VK_IMAGE_TYPE_2D, - .format = format, - .extent{ - .width = image_extent.width, - .height = image_extent.height, - .depth = 1, - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = sample_count, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = image_usage, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }); - - const VmaAllocationCreateInfo vma_allocation_ci{ - .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, - .usage = VMA_MEMORY_USAGE_GPU_ONLY, - }; - - if (const auto result = vmaCreateImage(m_device.allocator(), &image_ci, &vma_allocation_ci, &m_image, &m_allocation, - &m_allocation_info); - result != VK_SUCCESS) { - throw VulkanException("Error: vmaCreateImage failed for image " + m_name + "!", result); - } - - vmaSetAllocationName(m_device.allocator(), m_allocation, m_name.c_str()); - - // Assign an internal name using Vulkan debug markers. - m_device.set_debug_marker_name(m_image, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_name); - - m_device.create_image_view(make_info({ - .image = m_image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = format, - .subresourceRange{ - .aspectMask = aspect_flags, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }), - &m_image_view, m_name); +Image::Image(const Device &device, const VkImageCreateInfo &img_ci, const VmaAllocationCreateInfo &alloc_ci, + const VkImageViewCreateInfo &img_view_ci, std::string name) + : m_device(device), m_format(img_ci.format), m_name(std::move(name)) { + assert(!m_name.empty()); + m_device.create_image(img_ci, alloc_ci, &m_img, &m_alloc, &m_alloc_info, m_name); + + // We need to set the image in the image view create info here + VkImageViewCreateInfo filled_img_view_ci = img_view_ci; + filled_img_view_ci.image = m_img; + filled_img_view_ci.format = img_ci.format; + m_device.create_image_view(filled_img_view_ci, &m_img_view, m_name); } Image::Image(Image &&other) noexcept : m_device(other.m_device) { - m_allocation = other.m_allocation; - m_allocation_info = other.m_allocation_info; - m_image = other.m_image; m_format = other.m_format; - m_image_view = other.m_image_view; + m_alloc = other.m_alloc; + m_alloc_info = other.m_alloc_info; + m_img = std::exchange(other.m_img, VK_NULL_HANDLE); + m_img_view = std::exchange(other.m_img_view, VK_NULL_HANDLE); m_name = std::move(other.m_name); } Image::~Image() { - vkDestroyImageView(m_device.device(), m_image_view, nullptr); - vmaDestroyImage(m_device.allocator(), m_image, m_allocation); + vkDestroyImageView(m_device.device(), m_img_view, nullptr); + vmaDestroyImage(m_device.allocator(), m_img, m_alloc); } } // namespace inexor::vulkan_renderer::wrapper diff --git a/src/vulkan-renderer/wrapper/make_info.cpp b/src/vulkan-renderer/wrapper/make_info.cpp index db9f14cfa..447842048 100644 --- a/src/vulkan-renderer/wrapper/make_info.cpp +++ b/src/vulkan-renderer/wrapper/make_info.cpp @@ -148,6 +148,12 @@ VkPipelineDepthStencilStateCreateInfo make_info(VkPipelineDepthStencilStateCreat return info; } +template <> +VkPipelineDynamicStateCreateInfo make_info(VkPipelineDynamicStateCreateInfo info) { + info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + return info; +} + template <> VkPipelineInputAssemblyStateCreateInfo make_info(VkPipelineInputAssemblyStateCreateInfo info) { info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; @@ -178,6 +184,12 @@ VkPipelineShaderStageCreateInfo make_info(VkPipelineShaderStageCreateInfo info) return info; } +template <> +VkPipelineTessellationStateCreateInfo make_info(VkPipelineTessellationStateCreateInfo info) { + info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + return info; +} + template <> VkPipelineVertexInputStateCreateInfo make_info(VkPipelineVertexInputStateCreateInfo info) { info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; diff --git a/src/vulkan-renderer/wrapper/pipeline.cpp b/src/vulkan-renderer/wrapper/pipeline.cpp new file mode 100644 index 000000000..f0a8b456b --- /dev/null +++ b/src/vulkan-renderer/wrapper/pipeline.cpp @@ -0,0 +1,24 @@ +#include "inexor/vulkan-renderer/wrapper/pipeline.hpp" + +#include "inexor/vulkan-renderer/wrapper/device.hpp" + +#include + +namespace inexor::vulkan_renderer::wrapper { + +GraphicsPipeline::GraphicsPipeline(const Device &device, const VkGraphicsPipelineCreateInfo &pipeline_ci, + std::string name) + : m_device(device), m_name(std::move(name)) { + m_device.create_graphics_pipeline(pipeline_ci, &m_pipeline, m_name); +} + +GraphicsPipeline::GraphicsPipeline(GraphicsPipeline &&other) noexcept : m_device(other.m_device) { + m_pipeline = std::exchange(other.m_pipeline, nullptr); + m_name = std::move(other.m_name); +} + +GraphicsPipeline::~GraphicsPipeline() { + vkDestroyPipeline(m_device.device(), m_pipeline, nullptr); +} + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/src/vulkan-renderer/wrapper/pipeline_builder.cpp b/src/vulkan-renderer/wrapper/pipeline_builder.cpp new file mode 100644 index 000000000..14bdb76fd --- /dev/null +++ b/src/vulkan-renderer/wrapper/pipeline_builder.cpp @@ -0,0 +1,226 @@ +#include "inexor/vulkan-renderer/wrapper/pipeline_builder.hpp" + +#include "inexor/vulkan-renderer/wrapper/device.hpp" + +#include + +#include +#include + +namespace inexor::vulkan_renderer::wrapper { + +GraphicsPipelineBuilder::GraphicsPipelineBuilder(const Device &device) : m_device(device) {} + +GraphicsPipelineBuilder::GraphicsPipelineBuilder(GraphicsPipelineBuilder &&other) noexcept : m_device(other.m_device) { + m_vertex_input_sci = std::move(other.m_vertex_input_sci); + m_input_assembly_sci = std::move(other.m_input_assembly_sci); + m_tesselation_sci = std::move(other.m_tesselation_sci); + m_viewport_sci = std::move(other.m_viewport_sci); + m_rasterization_sci = std::move(m_rasterization_sci); + m_multisample_sci = std::move(other.m_multisample_sci); + m_depth_stencil_sci = std::move(other.m_depth_stencil_sci); + m_color_blend_sci = std::move(other.m_color_blend_sci); + m_dynamic_states_sci = std::move(other.m_dynamic_states_sci); + m_pipeline_layout = std::exchange(other.m_pipeline_layout, VK_NULL_HANDLE); + m_render_pass = std::exchange(other.m_render_pass, VK_NULL_HANDLE); + m_dynamic_states = std::move(other.m_dynamic_states); + m_viewports = std::move(other.m_viewports); + m_scissors = std::move(other.m_scissors); + m_shader_stages = std::move(other.m_shader_stages); + m_vertex_input_binding_descriptions = std::move(other.m_vertex_input_binding_descriptions); + m_vertex_input_attribute_descriptions = std::move(other.m_vertex_input_attribute_descriptions); + m_color_blend_attachment_states = std::move(other.m_color_blend_attachment_states); +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::add_shader(const VkPipelineShaderStageCreateInfo &shader_stage) { + m_shader_stages.push_back(shader_stage); + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::add_color_blend_attachment(const VkPipelineColorBlendAttachmentState &attachment) { + m_color_blend_attachment_states.push_back(attachment); + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::add_vertex_input_attribute(const VkVertexInputAttributeDescription &description) { + m_vertex_input_attribute_descriptions.push_back(description); + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::add_vertex_input_binding(const VkVertexInputBindingDescription &description) { + m_vertex_input_binding_descriptions.push_back(description); + return *this; +} + +std::unique_ptr GraphicsPipelineBuilder::build(std::string name) { + m_vertex_input_sci = make_info({ + .vertexBindingDescriptionCount = static_cast(m_vertex_input_binding_descriptions.size()), + .pVertexBindingDescriptions = m_vertex_input_binding_descriptions.data(), + .vertexAttributeDescriptionCount = static_cast(m_vertex_input_attribute_descriptions.size()), + .pVertexAttributeDescriptions = m_vertex_input_attribute_descriptions.data(), + + }); + + m_viewport_sci = make_info({ + .viewportCount = static_cast(m_viewports.size()), + .pViewports = m_viewports.data(), + .scissorCount = static_cast(m_scissors.size()), + .pScissors = m_scissors.data(), + }); + + m_dynamic_states_sci = make_info({ + .dynamicStateCount = static_cast(m_dynamic_states.size()), + .pDynamicStates = m_dynamic_states.data(), + }); + + return std::make_unique(m_device, + make_info({ + .stageCount = static_cast(m_shader_stages.size()), + .pStages = m_shader_stages.data(), + .pVertexInputState = &m_vertex_input_sci, + .pInputAssemblyState = &m_input_assembly_sci, + .pTessellationState = &m_tesselation_sci, + .pViewportState = &m_viewport_sci, + .pRasterizationState = &m_rasterization_sci, + .pMultisampleState = &m_multisample_sci, + .pDepthStencilState = &m_depth_stencil_sci, + .pColorBlendState = &m_color_blend_sci, + .pDynamicState = &m_dynamic_states_sci, + .layout = m_pipeline_layout, + .renderPass = m_render_pass, + }), + std::move(name)); +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::set_color_blend(const VkPipelineColorBlendStateCreateInfo &color_blend) { + m_color_blend_sci = color_blend; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_color_blend_attachments( + const std::vector &attachments) { + m_color_blend_attachment_states = attachments; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_culling_mode(const VkBool32 culling_enabled) { + spdlog::warn("Culling is disabled, which could have negative effects on the performance!"); + m_rasterization_sci.cullMode = culling_enabled ? VK_CULL_MODE_BACK_BIT : VK_CULL_MODE_NONE; + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::set_depth_stencil(const VkPipelineDepthStencilStateCreateInfo &depth_stencil) { + m_depth_stencil_sci = depth_stencil; + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::set_dynamic_states(const std::vector &dynamic_states) { + assert(!dynamic_states.empty()); + m_dynamic_states = dynamic_states; + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::set_input_assembly(const VkPipelineInputAssemblyStateCreateInfo &input_assembly) { + m_input_assembly_sci = input_assembly; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_line_width(const float width) { + m_rasterization_sci.lineWidth = width; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_pipeline_layout(const VkPipelineLayout layout) { + assert(layout); + m_pipeline_layout = layout; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_primitive_topology(const VkPrimitiveTopology topology) { + m_input_assembly_sci = make_info({ + .topology = topology, + }); + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::set_rasterization(const VkPipelineRasterizationStateCreateInfo &rasterization) { + m_rasterization_sci = rasterization; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_render_pass(const VkRenderPass render_pass) { + assert(render_pass); + m_render_pass = render_pass; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_scissor(const VkRect2D &scissor) { + m_scissors = {scissor}; + m_viewport_sci.scissorCount = 1; + m_viewport_sci.pScissors = m_scissors.data(); + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_scissors(const std::vector &scissors) { + assert(!scissors.empty()); + m_scissors = scissors; + m_viewport_sci.scissorCount = static_cast(scissors.size()); + m_viewport_sci.pScissors = scissors.data(); + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::set_shaders(const std::vector &shader_stages) { + assert(!shader_stages.empty()); + m_shader_stages = shader_stages; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_tesselation(const std::uint32_t control_points) { + m_tesselation_sci.patchControlPoints = control_points; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_vertex_input_attributes( + const std::vector &descriptions) { + assert(!descriptions.empty()); + m_vertex_input_attribute_descriptions = descriptions; + return *this; +} + +GraphicsPipelineBuilder & +GraphicsPipelineBuilder::set_vertex_input_bindings(const std::vector &descriptions) { + assert(!descriptions.empty()); + m_vertex_input_binding_descriptions = descriptions; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_viewport(const VkViewport &viewport) { + m_viewports = {viewport}; + m_viewport_sci.viewportCount = 1; + m_viewport_sci.pViewports = m_viewports.data(); + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_viewports(const std::vector &viewports) { + assert(!viewports.empty()); + m_viewports = viewports; + m_viewport_sci.viewportCount = static_cast(m_viewports.size()); + m_viewport_sci.pViewports = m_viewports.data(); + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_wireframe(const VkBool32 wireframe) { + m_rasterization_sci.polygonMode = (wireframe == VK_TRUE) ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL; + return *this; +} + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/src/vulkan-renderer/wrapper/pipeline_layout.cpp b/src/vulkan-renderer/wrapper/pipeline_layout.cpp new file mode 100644 index 000000000..a41c5d36e --- /dev/null +++ b/src/vulkan-renderer/wrapper/pipeline_layout.cpp @@ -0,0 +1,36 @@ +#include "inexor/vulkan-renderer/wrapper/pipeline_layout.hpp" + +#include "inexor/vulkan-renderer/wrapper/device.hpp" +#include "inexor/vulkan-renderer/wrapper/make_info.hpp" + +#include + +namespace inexor::vulkan_renderer::wrapper { + +PipelineLayout::PipelineLayout(const Device &device, const VkPipelineLayoutCreateInfo &pipeline_layout_ci, + std::string name) + : m_device(device), m_name(std::move(name)) { + m_device.create_pipeline_layout(pipeline_layout_ci, &m_pipeline_layout, m_name); +} + +PipelineLayout::PipelineLayout(const Device &device, const std::vector &descriptor_set_layouts, + const std::vector &push_constant_ranges, std::string name) + : PipelineLayout(device, + wrapper::make_info({ + .setLayoutCount = static_cast(descriptor_set_layouts.size()), + .pSetLayouts = descriptor_set_layouts.data(), + .pushConstantRangeCount = static_cast(push_constant_ranges.size()), + .pPushConstantRanges = push_constant_ranges.data(), + }), + std::move(name)) {} + +PipelineLayout::PipelineLayout(PipelineLayout &&other) noexcept : m_device(other.m_device) { + std::exchange(m_pipeline_layout, std::exchange(other.m_pipeline_layout, VK_NULL_HANDLE)); + m_name = std::move(other.m_name); +} + +PipelineLayout::~PipelineLayout() { + vkDestroyPipelineLayout(m_device.device(), m_pipeline_layout, nullptr); +} + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/src/vulkan-renderer/wrapper/renderpass.cpp b/src/vulkan-renderer/wrapper/renderpass.cpp new file mode 100644 index 000000000..042724a5c --- /dev/null +++ b/src/vulkan-renderer/wrapper/renderpass.cpp @@ -0,0 +1,38 @@ +#include "inexor/vulkan-renderer/wrapper/renderpass.hpp" + +#include "inexor/vulkan-renderer/wrapper/device.hpp" +#include "inexor/vulkan-renderer/wrapper/make_info.hpp" + +#include + +namespace inexor::vulkan_renderer::wrapper { + +RenderPass::RenderPass(const Device &device, const VkRenderPassCreateInfo &render_pass_ci, std::string name) + : m_device(device), m_name(std::move(name)) { + m_device.create_render_pass(render_pass_ci, &m_render_pass, m_name); +} + +RenderPass::RenderPass(const Device &device, const std::vector &attachments, + const std::vector &subpasses, + const std::vector &dependencies, std::string name) + : RenderPass(device, + wrapper::make_info({ + .attachmentCount = static_cast(attachments.size()), + .pAttachments = attachments.data(), + .subpassCount = static_cast(subpasses.size()), + .pSubpasses = subpasses.data(), + .dependencyCount = static_cast(dependencies.size()), + .pDependencies = dependencies.data(), + }), + std::move(name)) {} + +RenderPass::RenderPass(RenderPass &&other) noexcept : m_device(other.m_device) { + m_render_pass = std::exchange(other.m_render_pass, VK_NULL_HANDLE); + m_name = std::move(other.m_name); +} + +RenderPass::~RenderPass() { + vkDestroyRenderPass(m_device.device(), m_render_pass, nullptr); +} + +} // namespace inexor::vulkan_renderer::wrapper diff --git a/src/vulkan-renderer/wrapper/sampler.cpp b/src/vulkan-renderer/wrapper/sampler.cpp new file mode 100644 index 000000000..089c786d0 --- /dev/null +++ b/src/vulkan-renderer/wrapper/sampler.cpp @@ -0,0 +1,41 @@ +#include "inexor/vulkan-renderer/wrapper/sampler.hpp" + +#include "inexor/vulkan-renderer/wrapper/device.hpp" +#include "inexor/vulkan-renderer/wrapper/make_info.hpp" + +#include + +namespace inexor::vulkan_renderer::wrapper { + +Sampler::Sampler(const Device &device, const VkSamplerCreateInfo &sampler_ci, std::string name) + : m_device(device), m_name(std::move(name)) { + m_device.create_sampler(sampler_ci, &m_sampler, m_name); +} + +Sampler::Sampler(const Device &device, std::string name) + : Sampler(device, + // Default sampler settings + make_info({ + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 1.0f, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + }), + std::move(name)) {} + +Sampler::~Sampler() { + vkDestroySampler(m_device.device(), m_sampler, nullptr); +} + +} // namespace inexor::vulkan_renderer::wrapper