diff --git a/include/inexor/vulkan-renderer/render_graph.hpp b/include/inexor/vulkan-renderer/render_graph.hpp index 5335a0c70..489ac700f 100644 --- a/include/inexor/vulkan-renderer/render_graph.hpp +++ b/include/inexor/vulkan-renderer/render_graph.hpp @@ -4,6 +4,7 @@ #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/swapchain.hpp" @@ -390,6 +391,8 @@ class RenderGraph { // Stage execution order. std::vector m_stage_stack; + mutable wrapper::GraphicsPipelineBuilder m_graphics_pipeline_builder; + void update_dynamic_buffers(const wrapper::CommandBuffer &cmd_buf); // Functions for building stage related vulkan objects. @@ -403,7 +406,7 @@ class RenderGraph { public: RenderGraph(wrapper::Device &device, const wrapper::Swapchain &swapchain) - : m_device(device), m_swapchain(swapchain) {} + : m_device(device), m_swapchain(swapchain), m_graphics_pipeline_builder(device) {} /// @brief Adds either a render resource or render stage to the render graph. /// @return A mutable reference to the just-added resource or stage 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..9cf98256a --- /dev/null +++ b/include/inexor/vulkan-renderer/wrapper/pipeline_builder.hpp @@ -0,0 +1,245 @@ +#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 +/// @note that all members are initialized in the method ``reset()`` (This method is also called by the constructor) +/// Calling ``reset()`` allows you to re-use this builder for the next graphics pipeline you want to build, so you don't +/// need to create one builder per graphics pipeline you build +class GraphicsPipelineBuilder { +private: + const Device &m_device; + + std::vector m_vertex_input_binding_descriptions; + std::vector m_vertex_input_attribute_descriptions; + // With the builder we can fill vertex binding descriptions and vertex attribute descriptions in here + VkPipelineVertexInputStateCreateInfo m_vertex_input_sci; + + // With the builder we can set topology in here + VkPipelineInputAssemblyStateCreateInfo m_input_assembly_sci; + + // With the builder we can set the patch control point count in here + VkPipelineTessellationStateCreateInfo m_tesselation_sci; + + std::vector m_viewports; + std::vector m_scissors; + // With the builder we can set viewport(s) and scissor(s) in here + VkPipelineViewportStateCreateInfo m_viewport_sci; + + // With the builder we can set polygon mode, cull mode, front face, and line width + // TODO: Implement methods to enable depth bias and for setting the depth bias parameters + VkPipelineRasterizationStateCreateInfo m_rasterization_sci; + + // With the builder we can set rasterization samples and min sample shading + // TODO: Expose more multisampling parameters if desired + VkPipelineMultisampleStateCreateInfo m_multisample_sci; + + // With the builder we can't set individial fields of this struct, + // since it's easier to specify an entire VkPipelineDepthStencilStateCreateInfo struct to the builder instead + VkPipelineDepthStencilStateCreateInfo m_depth_stencil_sci; + + // With the builder we can't set individial fields of this struct, + // since it's easier to specify an entire VkPipelineColorBlendStateCreateInfo struct to the builder instead + VkPipelineColorBlendStateCreateInfo m_color_blend_sci; + + std::vector m_dynamic_states; + // This will be filled in the build() method + // With the builder we can specify a std::vector + VkPipelineDynamicStateCreateInfo m_dynamic_states_sci; + + VkPipelineLayout m_pipeline_layout{VK_NULL_HANDLE}; + VkRenderPass m_render_pass{VK_NULL_HANDLE}; + + // With the builder we can either call add_shader or set_shaders + std::vector m_shader_stages; + // With the builder we can either call add_color_blend_attachment or set_color_blend_attachments + std::vector m_color_blend_attachment_states; + +public: + /// Default constructor + /// @param device The device wrapper + explicit 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); + + /// Reset all data in this class so the builder can be re-used + /// @note This is called by the constructor + void reset(); + + /// 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); + + /// Set the most important MSAA settings + /// @param sample_count The number of samples used in rasterization + /// @param min_sample_shading A minimum fraction of sample shading + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_multisampling(VkSampleCountFlagBits sample_count, + float min_sample_shading); + + /// 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 scissor data in VkPipelineViewportStateCreateInfo (convert VkExtent2D to VkRect2D) + /// @param extent The extent of the scissor + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_scissor(const VkExtent2D &extent); + + /// 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 control point count + /// @param control_point_count The tesselation control point count + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_tesselation_control_point_count(std::uint32_t control_point_count); + + /// 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 (convert VkExtent2D to VkViewport) + /// @param extent The extent of the viewport + /// @return A reference to the dereferenced this pointer (allows method calls to be chained) + [[nodiscard]] GraphicsPipelineBuilder &set_viewport(const VkExtent2D &extent); + + /// 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/src/CMakeLists.txt b/src/CMakeLists.txt index 2b80d9599..d27f442f9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ set(INEXOR_SOURCE_FILES 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/sampler.cpp diff --git a/src/vulkan-renderer/render_graph.cpp b/src/vulkan-renderer/render_graph.cpp index 9fb3c36a5..54829e58c 100644 --- a/src/vulkan-renderer/render_graph.cpp +++ b/src/vulkan-renderer/render_graph.cpp @@ -224,86 +224,34 @@ 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, - }); - + // Note that at the beginning of this method, m_graphics_pipeline_builder is always in reset state 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, - }); - - // TODO: Pipeline caching (basically load the render graph from a file) - physical.m_pipeline = std::make_unique( - m_device, - 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->pipeline_layout(), - .renderPass = physical.m_render_pass, - }), - // TODO: Apply internal debug name to the graphics pipeline - "graphics pipeline"); + // Build the graphics pipeline with the help of the graphics pipeline builder + physical.m_pipeline = m_graphics_pipeline_builder + .set_color_blend({ + .attachmentCount = 1, + .pAttachments = &blend_attachment, + }) + .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_pipeline_layout(physical.m_pipeline_layout->pipeline_layout()) + .set_render_pass(physical.m_render_pass) + .set_scissor(m_swapchain.extent()) + .set_shaders(stage->m_shaders) + .set_vertex_input_attributes(attribute_bindings) + .set_vertex_input_bindings(vertex_bindings) + .set_viewport(m_swapchain.extent()) + // TODO: Apply internal debug name to the graphics pipeline + .build("graphics pipeline"); + + // After using the graphics pipeline builder, reset it so it can be re-used when building the next pipeline + m_graphics_pipeline_builder.reset(); } void RenderGraph::compile(const RenderResource *target) { diff --git a/src/vulkan-renderer/wrapper/pipeline_builder.cpp b/src/vulkan-renderer/wrapper/pipeline_builder.cpp new file mode 100644 index 000000000..c1fd3db4e --- /dev/null +++ b/src/vulkan-renderer/wrapper/pipeline_builder.cpp @@ -0,0 +1,301 @@ +#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) { + reset(); +} + +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(), + }); + + if (!m_dynamic_states.empty()) { + 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)); +} + +void GraphicsPipelineBuilder::reset() { + m_vertex_input_binding_descriptions.clear(); + m_vertex_input_attribute_descriptions.clear(); + m_vertex_input_sci = { + make_info(), + }; + + m_input_assembly_sci = { + make_info({ + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE, + }), + }; + + m_tesselation_sci = { + make_info(), + }; + + m_viewports.clear(); + m_scissors.clear(); + m_viewport_sci = { + make_info(), + }; + + m_rasterization_sci = { + make_info({ + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_BACK_BIT, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .lineWidth = 1.0f, + }), + }; + + m_multisample_sci = { + make_info({ + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .sampleShadingEnable = VK_FALSE, + .minSampleShading = 1.0f, + }), + }; + + m_depth_stencil_sci = { + make_info(), + }; + + m_color_blend_sci = { + make_info(), + }; + + m_dynamic_states.clear(); + m_dynamic_states_sci = { + make_info(), + }; +} + +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_line_width(const float width) { + m_rasterization_sci.lineWidth = width; + return *this; +} + +GraphicsPipelineBuilder &GraphicsPipelineBuilder::set_multisampling(const VkSampleCountFlagBits sample_count, + const float min_sample_shading) { + m_multisample_sci.rasterizationSamples = sample_count; + m_multisample_sci.minSampleShading = min_sample_shading; + 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.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_scissor(const VkExtent2D &extent) { + return set_scissor({ + // Convert VkExtent2D to VkRect2D + .extent = extent, + }); +} + +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_control_point_count(const std::uint32_t control_point_count) { + m_tesselation_sci.patchControlPoints = control_point_count; + 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_viewport(const VkExtent2D &extent) { + return set_viewport({ + // Convert VkExtent2D to VkViewport + .width = static_cast(extent.width), + .height = static_cast(extent.height), + .maxDepth = 1.0f, + }); +} + +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