diff --git a/meson.build b/meson.build index bbeb95c..423e2f3 100644 --- a/meson.build +++ b/meson.build @@ -85,6 +85,7 @@ exe = executable('vr-compositor', 'src/Logger.cpp', 'src/DescriptorLayoutBuilder.cpp', 'src/DescriptorAllocator.cpp', + 'src/GraphicsPipelineBuilder.cpp', 'src/VulkanRenderer.cpp', 'src/Application.cpp', ], diff --git a/shaders/meson.build b/shaders/meson.build index 3fb211f..ba0169b 100644 --- a/shaders/meson.build +++ b/shaders/meson.build @@ -15,11 +15,13 @@ endif shader_sources = files( 'gradient.comp', + 'triangle.frag', + 'triangle.vert', ) spirv_shaders = [] foreach shader : shader_sources - shader_name = fs.stem(shader) + shader_name = fs.stem(shader.full_path().replace('.', '_')) spirv_shaders += custom_target( shader_name + '_spv', input : shader, diff --git a/shaders/triangle.frag b/shaders/triangle.frag new file mode 100644 index 0000000..e6a8fae --- /dev/null +++ b/shaders/triangle.frag @@ -0,0 +1,10 @@ +#version 450 + +layout (location = 0) in vec3 in_color; + +layout (location = 0) out vec4 out_frag_color; + +void main() { + out_frag_color = vec4(in_color, 1.0f); +} + diff --git a/shaders/triangle.vert b/shaders/triangle.vert new file mode 100644 index 0000000..50bf94b --- /dev/null +++ b/shaders/triangle.vert @@ -0,0 +1,21 @@ +#version 450 + +layout (location = 0) out vec3 out_color; + +void main() { + const vec3 positions[3] = vec3[3]( + vec3( 1.0f, 1.0f, 0.0f), + vec3(-1.0f, 1.0f, 0.0f), + vec3( 0.0f, -1.0f, 0.0f) + ); + + const vec3 colors[3] = vec3[3]( + vec3(1.0f, 0.0f, 0.0f), + vec3(0.0f, 1.0f, 0.0f), + vec3(0.0f, 0.0f, 1.0f) + ); + + gl_Position = vec4(positions[gl_VertexIndex], 1.0f); + out_color = colors[gl_VertexIndex]; +} + diff --git a/src/GraphicsPipelineBuilder.cpp b/src/GraphicsPipelineBuilder.cpp new file mode 100644 index 0000000..46f50e1 --- /dev/null +++ b/src/GraphicsPipelineBuilder.cpp @@ -0,0 +1,207 @@ +#include "GraphicsPipelineBuilder.h" + +#include + +#include "Util.h" + +namespace Lunar { + +auto GraphicsPipelineBuilder::clear() -> GraphicsPipelineBuilder & +{ + m_input_assembly = {}; + m_input_assembly.sType + = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + + m_rasterizer = {}; + m_rasterizer.sType + = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + + m_color_blend_attachment = {}; + + m_multisampling = {}; + m_multisampling.sType + = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + + m_pipeline_layout = {}; + + m_depth_stencil = {}; + m_depth_stencil.sType + = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + + m_render_info = {}; + m_render_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; + + m_shader_stages.clear(); + + return *this; +} + +auto GraphicsPipelineBuilder::set_shaders(VkShaderModule vs, VkShaderModule fs) + -> GraphicsPipelineBuilder & +{ + m_shader_stages.clear(); + + m_shader_stages.emplace_back( + vkinit::pipeline_shader_stage(VK_SHADER_STAGE_VERTEX_BIT, vs)); + m_shader_stages.emplace_back( + vkinit::pipeline_shader_stage(VK_SHADER_STAGE_FRAGMENT_BIT, fs)); + + return *this; +} + +auto GraphicsPipelineBuilder::set_input_topology(VkPrimitiveTopology topology, + VkBool32 primitive_restart_enable) -> GraphicsPipelineBuilder & +{ + m_input_assembly.topology = topology; + m_input_assembly.primitiveRestartEnable = primitive_restart_enable; + + return *this; +} + +auto GraphicsPipelineBuilder::set_polygon_mode(VkPolygonMode mode) + -> GraphicsPipelineBuilder & +{ + m_rasterizer.polygonMode = mode; + m_rasterizer.lineWidth = 1.0f; + + return *this; +} + +auto GraphicsPipelineBuilder::set_cull_mode(VkCullModeFlags cull_mode, + VkFrontFace front_face) -> GraphicsPipelineBuilder & +{ + m_rasterizer.cullMode = cull_mode; + m_rasterizer.frontFace = front_face; + + return *this; +} + +auto GraphicsPipelineBuilder::set_multisampling_none() + -> GraphicsPipelineBuilder & +{ + m_multisampling.sampleShadingEnable = VK_FALSE; + m_multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + m_multisampling.minSampleShading = 1.0f; + m_multisampling.pSampleMask = nullptr; + m_multisampling.alphaToCoverageEnable = VK_FALSE; + m_multisampling.alphaToOneEnable = VK_FALSE; + + return *this; +} + +auto GraphicsPipelineBuilder::disable_blending() -> GraphicsPipelineBuilder & +{ + m_color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT + | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT + | VK_COLOR_COMPONENT_A_BIT; + m_color_blend_attachment.blendEnable = VK_FALSE; + + return *this; +} + +auto GraphicsPipelineBuilder::set_color_attachment_format(VkFormat format) + -> GraphicsPipelineBuilder & +{ + m_color_attachment_format = format; + + m_render_info.colorAttachmentCount = 1; + m_render_info.pColorAttachmentFormats = &m_color_attachment_format; + + return *this; +} + +auto GraphicsPipelineBuilder::set_depth_format(VkFormat format) + -> GraphicsPipelineBuilder & +{ + m_render_info.depthAttachmentFormat = format; + + return *this; +} + +auto GraphicsPipelineBuilder::set_pipeline_layout(VkPipelineLayout layout) + -> GraphicsPipelineBuilder & +{ + m_pipeline_layout = layout; + + return *this; +} + +auto GraphicsPipelineBuilder::disable_depth_testing() + -> GraphicsPipelineBuilder & +{ + m_depth_stencil.depthTestEnable = VK_FALSE; + m_depth_stencil.depthWriteEnable = VK_FALSE; + m_depth_stencil.depthCompareOp = VK_COMPARE_OP_NEVER; + m_depth_stencil.depthBoundsTestEnable = VK_FALSE; + m_depth_stencil.stencilTestEnable = VK_FALSE; + m_depth_stencil.front = {}; + m_depth_stencil.back = {}; + m_depth_stencil.minDepthBounds = 0.f; + m_depth_stencil.maxDepthBounds = 1.f; + + return *this; +} + +auto GraphicsPipelineBuilder::build(VkDevice dev) -> VkPipeline +{ + VkPipelineViewportStateCreateInfo viewport_state_ci {}; + viewport_state_ci.sType + = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state_ci.pNext = nullptr; + + viewport_state_ci.viewportCount = 1; + viewport_state_ci.scissorCount = 1; + + VkPipelineColorBlendStateCreateInfo color_blending_ci {}; + color_blending_ci.sType + = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blending_ci.pNext = nullptr; + + color_blending_ci.logicOpEnable = VK_FALSE; + color_blending_ci.logicOp = VK_LOGIC_OP_COPY; + color_blending_ci.attachmentCount = 1; + color_blending_ci.pAttachments = &m_color_blend_attachment; + + VkPipelineVertexInputStateCreateInfo vertex_input_ci {}; + vertex_input_ci.sType + = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + VkGraphicsPipelineCreateInfo pipeline_ci {}; + pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_ci.pNext = &m_render_info; + + pipeline_ci.stageCount = static_cast(m_shader_stages.size()); + pipeline_ci.pStages = m_shader_stages.data(); + pipeline_ci.pVertexInputState = &vertex_input_ci; + pipeline_ci.pInputAssemblyState = &m_input_assembly; + pipeline_ci.pViewportState = &viewport_state_ci; + pipeline_ci.pRasterizationState = &m_rasterizer; + pipeline_ci.pMultisampleState = &m_multisampling; + pipeline_ci.pColorBlendState = &color_blending_ci; + pipeline_ci.pDepthStencilState = &m_depth_stencil; + pipeline_ci.layout = m_pipeline_layout; + + std::array state { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + + VkPipelineDynamicStateCreateInfo dynamic_ci {}; + dynamic_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_ci.pDynamicStates = state.data(); + dynamic_ci.dynamicStateCount = state.size(); + + pipeline_ci.pDynamicState = &dynamic_ci; + + VkPipeline pip {}; + if (vkCreateGraphicsPipelines( + dev, VK_NULL_HANDLE, 1, &pipeline_ci, nullptr, &pip) + != VK_SUCCESS) { + m_logger.err("Failed to create graphics pipeline"); + return VK_NULL_HANDLE; + } + + return pip; +} + +} // namespace Lunar diff --git a/src/GraphicsPipelineBuilder.h b/src/GraphicsPipelineBuilder.h new file mode 100644 index 0000000..27833a7 --- /dev/null +++ b/src/GraphicsPipelineBuilder.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include + +#include "Logger.h" + +namespace Lunar { + +struct GraphicsPipelineBuilder { + GraphicsPipelineBuilder(Logger &logger) + : m_logger(logger) + { + clear(); + } + + auto clear() -> GraphicsPipelineBuilder &; + auto set_shaders(VkShaderModule vs, VkShaderModule fs) + -> GraphicsPipelineBuilder &; + auto set_input_topology(VkPrimitiveTopology topology, + VkBool32 primitive_restart_enable = VK_FALSE) + -> GraphicsPipelineBuilder &; + auto set_polygon_mode(VkPolygonMode mode) -> GraphicsPipelineBuilder &; + auto set_cull_mode(VkCullModeFlags cull_mode, VkFrontFace front_face) + -> GraphicsPipelineBuilder &; + auto set_multisampling_none() -> GraphicsPipelineBuilder &; + auto disable_blending() -> GraphicsPipelineBuilder &; + auto set_color_attachment_format(VkFormat format) + -> GraphicsPipelineBuilder &; + auto set_depth_format(VkFormat format) -> GraphicsPipelineBuilder &; + auto set_pipeline_layout(VkPipelineLayout layout) + -> GraphicsPipelineBuilder &; + auto disable_depth_testing() -> GraphicsPipelineBuilder &; + auto build(VkDevice dev) -> VkPipeline; + +private: + VkPipelineInputAssemblyStateCreateInfo m_input_assembly {}; + VkPipelineRasterizationStateCreateInfo m_rasterizer {}; + VkPipelineColorBlendAttachmentState m_color_blend_attachment {}; + VkPipelineMultisampleStateCreateInfo m_multisampling {}; + VkPipelineLayout m_pipeline_layout {}; + VkPipelineDepthStencilStateCreateInfo m_depth_stencil {}; + VkPipelineRenderingCreateInfo m_render_info {}; + VkFormat m_color_attachment_format {}; + + std::vector m_shader_stages {}; + + Logger &m_logger; +}; + +} // namespace Lunar diff --git a/src/Util.cpp b/src/Util.cpp index 877b609..6766580 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -209,4 +209,32 @@ auto attachment_info(VkImageView view, VkClearValue *clear, return color_at; } +auto pipeline_shader_stage(VkShaderStageFlagBits stage, VkShaderModule module) + -> VkPipelineShaderStageCreateInfo +{ + VkPipelineShaderStageCreateInfo stage_ci {}; + stage_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage_ci.pNext = nullptr; + stage_ci.stage = stage; + stage_ci.module = module; + stage_ci.pName = "main"; + return stage_ci; +} + +auto render_info(VkExtent2D extent, VkRenderingAttachmentInfo const *color_att, + VkRenderingAttachmentInfo const *depth_att) -> VkRenderingInfo +{ + + VkRenderingInfo render_info {}; + render_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + render_info.pNext = nullptr; + render_info.flags = 0; + render_info.renderArea = { {}, extent }; + render_info.layerCount = 1; + render_info.colorAttachmentCount = color_att ? 1 : 0; + render_info.pColorAttachments = color_att; + render_info.pDepthAttachment = depth_att; + return render_info; +} + } // namespace vkinit diff --git a/src/Util.h b/src/Util.h index 91ac7d8..20b7e77 100644 --- a/src/Util.h +++ b/src/Util.h @@ -63,5 +63,9 @@ auto submit_info2(VkCommandBufferSubmitInfo *cmd_info, auto attachment_info(VkImageView view, VkClearValue *clear, VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) -> VkRenderingAttachmentInfo; +auto pipeline_shader_stage(VkShaderStageFlagBits stage, VkShaderModule module) + -> VkPipelineShaderStageCreateInfo; +auto render_info(VkExtent2D extent, VkRenderingAttachmentInfo const *color_att, + VkRenderingAttachmentInfo const *depth_att) -> VkRenderingInfo; } // namespace vkinit diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp index 5e75ca2..b70ad0c 100644 --- a/src/VulkanRenderer.cpp +++ b/src/VulkanRenderer.cpp @@ -14,6 +14,7 @@ #include #include "DescriptorLayoutBuilder.h" +#include "GraphicsPipelineBuilder.h" #include "Util.h" namespace Lunar { @@ -316,7 +317,11 @@ auto VulkanRenderer::descriptors_init() -> void }); } -auto VulkanRenderer::pipelines_init() -> void { background_pipelines_init(); } +auto VulkanRenderer::pipelines_init() -> void +{ + background_pipelines_init(); + triangle_pipeline_init(); +} auto VulkanRenderer::background_pipelines_init() -> void { @@ -331,7 +336,7 @@ auto VulkanRenderer::background_pipelines_init() -> void m_vkb.dev, &layout_ci, nullptr, &m_vk.gradient_pipeline_layout)); uint8_t compute_draw_shader_data[] { -#embed "gradient.spv" +#embed "gradient_comp.spv" }; VkShaderModule compute_draw_shader {}; if (!vkutil::load_shader_module(std::span(compute_draw_shader_data, @@ -340,12 +345,8 @@ auto VulkanRenderer::background_pipelines_init() -> void m_logger.err("Failed to load gradient compute shader"); } - VkPipelineShaderStageCreateInfo stage_ci {}; - stage_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stage_ci.pNext = nullptr; - stage_ci.stage = VK_SHADER_STAGE_COMPUTE_BIT; - stage_ci.module = compute_draw_shader; - stage_ci.pName = "main"; + auto stage_ci { vkinit::pipeline_shader_stage( + VK_SHADER_STAGE_COMPUTE_BIT, compute_draw_shader) }; VkComputePipelineCreateInfo compute_pip_ci {}; compute_pip_ci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; @@ -365,6 +366,61 @@ auto VulkanRenderer::background_pipelines_init() -> void }); } +auto VulkanRenderer::triangle_pipeline_init() -> void +{ + uint8_t triangle_vert_shader_data[] { +#embed "triangle_vert.spv" + }; + VkShaderModule triangle_vert_shader {}; + if (!vkutil::load_shader_module( + std::span( + triangle_vert_shader_data, sizeof(triangle_vert_shader_data)), + m_vkb.dev, &triangle_vert_shader)) { + m_logger.err("Failed to load triangle vert shader"); + } + + uint8_t triangle_frag_shader_data[] { +#embed "triangle_frag.spv" + }; + VkShaderModule triangle_frag_shader {}; + if (!vkutil::load_shader_module( + std::span( + triangle_frag_shader_data, sizeof(triangle_frag_shader_data)), + m_vkb.dev, &triangle_frag_shader)) { + m_logger.err("Failed to load triangle frag shader"); + } + + VkPipelineLayoutCreateInfo layout_ci {}; + layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_ci.pNext = nullptr; + + VK_CHECK(m_logger, + vkCreatePipelineLayout( + m_vkb.dev, &layout_ci, nullptr, &m_vk.triangle_pipeline_layout)); + + auto pip { GraphicsPipelineBuilder { m_logger } + .set_pipeline_layout(m_vk.triangle_pipeline_layout) + .set_shaders(triangle_vert_shader, triangle_frag_shader) + .set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) + .set_polygon_mode(VK_POLYGON_MODE_FILL) + .set_multisampling_none() + .disable_blending() + .disable_depth_testing() + .set_color_attachment_format(m_vk.draw_image.format) + .set_depth_format(VK_FORMAT_UNDEFINED) + .build(m_vkb.dev) }; + m_vk.triangle_pipeline = pip; + + vkDestroyShaderModule(m_vkb.dev, triangle_vert_shader, nullptr); + vkDestroyShaderModule(m_vkb.dev, triangle_frag_shader, nullptr); + + m_vk.deletion_queue.emplace([&]() { + vkDestroyPipelineLayout( + m_vkb.dev, m_vk.triangle_pipeline_layout, nullptr); + vkDestroyPipeline(m_vkb.dev, m_vk.triangle_pipeline, nullptr); + }); +} + auto VulkanRenderer::imgui_init() -> void { VkDescriptorPoolSize pool_sizes[] = { @@ -388,9 +444,9 @@ auto VulkanRenderer::imgui_init() -> void pool_info.poolSizeCount = (uint32_t)std::size(pool_sizes); pool_info.pPoolSizes = pool_sizes; - VK_CHECK(m_logger, vkCreateDescriptorPool( - m_vkb.dev, &pool_info, nullptr, - &m_vk.imgui_descriptor_pool)); + VK_CHECK(m_logger, + vkCreateDescriptorPool( + m_vkb.dev, &pool_info, nullptr, &m_vk.imgui_descriptor_pool)); ImGui::CreateContext(); @@ -479,7 +535,13 @@ auto VulkanRenderer::render() -> void draw_background(cmd); vkutil::transition_image(cmd, m_vk.draw_image.image, - VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + draw_geometry(cmd); + + vkutil::transition_image(cmd, m_vk.draw_image.image, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); vkutil::transition_image(cmd, m_vk.swapchain_images.at(swapchain_image_idx), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); @@ -552,19 +614,45 @@ auto VulkanRenderer::draw_background(VkCommandBuffer cmd) -> void static_cast(std::ceil(m_vk.draw_extent.height / 16.0)), 1); } +auto VulkanRenderer::draw_geometry(VkCommandBuffer cmd) -> void +{ + auto color_att { vkinit::attachment_info(m_vk.draw_image.image_view, + nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) }; + auto const render_info { vkinit::render_info( + m_vk.draw_extent, &color_att, nullptr) }; + + vkCmdBeginRendering(cmd, &render_info); + + vkCmdBindPipeline( + cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vk.triangle_pipeline); + + VkViewport viewport {}; + viewport.x = 0; + viewport.y = 0; + viewport.width = static_cast(m_vk.draw_extent.width); + viewport.height = static_cast(m_vk.draw_extent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(cmd, 0, 1, &viewport); + + VkRect2D scissor {}; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent = m_vk.draw_extent; + vkCmdSetScissor(cmd, 0, 1, &scissor); + + vkCmdDraw(cmd, 3, 1, 0, 0); + + vkCmdEndRendering(cmd); +} + auto VulkanRenderer::draw_imgui( VkCommandBuffer cmd, VkImageView target_image_view) -> void { auto const color_attachment { vkinit::attachment_info( target_image_view, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) }; - VkRenderingInfo render_info {}; - render_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; - render_info.pNext = nullptr; - render_info.flags = 0; - render_info.renderArea = { {}, m_vk.draw_extent }; - render_info.layerCount = 1; - render_info.colorAttachmentCount = 1; - render_info.pColorAttachments = &color_attachment; + auto const render_info { vkinit::render_info( + m_vk.draw_extent, &color_attachment, nullptr) }; vkCmdBeginRendering(cmd, &render_info); diff --git a/src/VulkanRenderer.h b/src/VulkanRenderer.h index fa99e20..65f3fe1 100644 --- a/src/VulkanRenderer.h +++ b/src/VulkanRenderer.h @@ -45,9 +45,11 @@ private: auto descriptors_init() -> void; auto pipelines_init() -> void; auto background_pipelines_init() -> void; + auto triangle_pipeline_init() -> void; auto imgui_init() -> void; auto draw_background(VkCommandBuffer cmd) -> void; + auto draw_geometry(VkCommandBuffer cmd) -> void; auto draw_imgui(VkCommandBuffer cmd, VkImageView target_image_view) -> void; auto create_swapchain(uint32_t width, uint32_t height) -> void; @@ -95,6 +97,9 @@ private: VkPipeline gradient_pipeline {}; VkPipelineLayout gradient_pipeline_layout {}; + VkPipeline triangle_pipeline {}; + VkPipelineLayout triangle_pipeline_layout {}; + VkDescriptorPool imgui_descriptor_pool { VK_NULL_HANDLE }; DeletionQueue deletion_queue;