Browse Source

Added basic graphics pipeline

Jef Belmans 2 years ago
parent
commit
3cf0dff208

+ 13 - 0
shaders/colored_triangle.frag

@@ -0,0 +1,13 @@
+#version 450
+
+//shader input
+layout (location = 0) in vec3 inColor;
+
+//output write
+layout (location = 0) out vec4 outFragColor;
+
+void main()
+{
+    //return red
+    outFragColor = vec4(inColor,1.0f);
+}

BIN
shaders/colored_triangle.frag.spv


+ 24 - 0
shaders/colored_triangle.vert

@@ -0,0 +1,24 @@
+#version 450
+
+layout (location = 0) out vec3 outColor;
+
+void main()
+{
+    //const array of positions for the triangle
+    const vec3 positions[3] = vec3[3](
+        vec3(1.f,1.f, 0.0f),
+        vec3(-1.f,1.f, 0.0f),
+        vec3(0.f,-1.f, 0.0f)
+    );
+
+    //const array of colors for the triangle
+    const vec3 colors[3] = vec3[3](
+        vec3(1.0f, 0.0f, 0.0f), //red
+        vec3(0.0f, 1.0f, 0.0f), //green
+        vec3(00.f, 0.0f, 1.0f)  //blue
+    );
+
+    //output the position of each vertex
+    gl_Position = vec4(positions[gl_VertexIndex], 1.0f);
+    outColor = colors[gl_VertexIndex];
+}

BIN
shaders/colored_triangle.vert.spv


+ 108 - 4
src/vk_engine.cpp

@@ -116,13 +116,18 @@ void VulkanEngine::draw()
     // Begin recording
     VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
 
-    // Transition draw image to general layout for writing into it
+    // Transition draw image into general layout to draw into it,
+    // overwriting the entire image, not caring about previous layout
     vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
 
     draw_background(cmd);
 
-    // Transition draw image and swapchain image to correct layout for transferring
-    vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+    vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+
+    draw_geometry(cmd);
+
+    // Transition draw and swapchain image into transfer layout
+    vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
     vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
 
     // Copy draw image to swapchain image
@@ -507,6 +512,7 @@ void VulkanEngine::init_descriptors()
 void VulkanEngine::init_pipelines()
 {
     init_background_pipelines();
+    init_triangle_pipeline();
 }
 
 void VulkanEngine::init_background_pipelines()
@@ -709,4 +715,102 @@ void VulkanEngine::init_imgui()
         vkDestroyDescriptorPool(_device, imguiPool, nullptr);
         ImGui_ImplVulkan_Shutdown();
     });
-}
+}
+
+void VulkanEngine::init_triangle_pipeline()
+{
+    VkShaderModule vertModule;
+    if (!vkutil::load_shader_module("../shaders/colored_triangle.vert.spv", _device, &vertModule))
+    {
+        fmt::print("Error when building the triangle vertex shader module");
+    }
+    else
+    {
+        fmt::print("Triangle vertex shader successfully loaded");
+    }
+
+    VkShaderModule fragModule;
+    if (!vkutil::load_shader_module("../shaders/colored_triangle.frag.spv", _device, &fragModule))
+    {
+        fmt::print("Error when building the triangle fragment shader module");
+    }
+    else
+    {
+        fmt::print("Triangle fragment shader successfully loaded");
+    }
+
+    // build the pipeline layout that controls the inputs/outputs of the shader
+    // we are not using descriptor sets or other systems yet, so no need to use anything other than empty default
+    VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info();
+    VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_triangleEffect.layout));
+
+    vkutil::PipelineBuilder pipelineBuilder;
+
+    //use the triangle layout we created
+    pipelineBuilder._pipelineLayout = _triangleEffect.layout;
+    //connecting the vertex and pixel shaders to the pipeline
+    pipelineBuilder.set_shaders(vertModule, fragModule);
+    //it will draw triangles
+    pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
+    //filled triangles
+    pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL);
+    //no backface culling
+    pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
+    //no multisampling
+    pipelineBuilder.disable_multisampling();
+    //no blending
+    pipelineBuilder.disable_blending();
+    //no depth testing
+    pipelineBuilder.disable_depth_test();
+
+    //connect the image format we will draw into, from draw image
+    pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat);
+    pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED);
+
+    //finally build the pipeline
+    _triangleEffect.pipeline = pipelineBuilder.build_pipeline(_device);
+
+    //clean structures
+    vkDestroyShaderModule(_device, vertModule, nullptr);
+    vkDestroyShaderModule(_device, fragModule, nullptr);
+
+    _mainDeletionQueue.push([&]() {
+        vkDestroyPipelineLayout(_device, _triangleEffect.layout, nullptr);
+        vkDestroyPipeline(_device, _triangleEffect.pipeline, nullptr);
+    });
+}
+
+void VulkanEngine::draw_geometry(VkCommandBuffer cmd)
+{
+    //begin a render pass  connected to our draw image
+    VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(_drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_GENERAL);
+
+    VkRenderingInfo renderInfo = vkinit::rendering_info(_drawExtent, &colorAttachment, nullptr);
+    vkCmdBeginRendering(cmd, &renderInfo);
+
+    vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _triangleEffect.pipeline);
+
+    //set dynamic viewport and scissor
+    VkViewport viewport = {};
+    viewport.x = 0;
+    viewport.y = 0;
+    viewport.width = _drawExtent.width;
+    viewport.height = _drawExtent.height;
+    viewport.minDepth = 0.f;
+    viewport.maxDepth = 1.f;
+
+    vkCmdSetViewport(cmd, 0, 1, &viewport);
+
+    VkRect2D scissor = {};
+    scissor.offset.x = 0;
+    scissor.offset.y = 0;
+    scissor.extent.width = _drawExtent.width;
+    scissor.extent.height = _drawExtent.height;
+
+    vkCmdSetScissor(cmd, 0, 1, &scissor);
+
+    //launch a draw command to draw 3 vertices
+    vkCmdDraw(cmd, 3, 1, 0, 0);
+
+    vkCmdEndRendering(cmd);
+}

+ 4 - 1
src/vk_engine.h

@@ -47,9 +47,10 @@ public:
     VkDescriptorSetLayout _drawImageDescriptorLayout;
     VkDescriptorSet _drawImageDescriptors;
 
-    // Pipelines
+    // Effects
     std::vector<ComputeEffect> _backgroundEffects;
     int currentBackgroundEffect{0};
+    Effect _triangleEffect;
 
     // Immediate commands
     ImmediateBuffer _immediateBuffer;
@@ -69,11 +70,13 @@ private:
     void init_descriptors();
     void init_pipelines();
     void init_background_pipelines();
+    void init_triangle_pipeline();
     void init_imgui();
 
     void create_swapchain(uint32_t width, uint32_t height);
 
     void draw_background(VkCommandBuffer cmd);
+    void draw_geometry(VkCommandBuffer cmd);
     void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView);
     void immediate_submit(std::function<void(VkCommandBuffer cmd)>&& function);
 

+ 155 - 1
src/vk_pipelines.cpp

@@ -51,4 +51,158 @@ bool vkutil::load_shader_module(const char* filePath,
 
     *outShaderModule = shaderModule;
     return true;
-}
+}
+
+VkPipeline vkutil::PipelineBuilder::build_pipeline(VkDevice device)
+{
+    // make viewport state from our stored viewport and scissor.
+    // at the moment we wont support multiple viewports or scissors
+    VkPipelineViewportStateCreateInfo viewportState = {};
+    viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+    viewportState.pNext = nullptr;
+
+    viewportState.viewportCount = 1;
+    viewportState.scissorCount =1;
+
+    // setup dummy color blending. We are not using transparent objects yet
+    // the blending is just "no blend", but we do write to the color attachment
+    VkPipelineColorBlendStateCreateInfo colorBlending = {};
+    colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+    colorBlending.pNext = nullptr;
+
+    colorBlending.logicOpEnable = VK_FALSE;
+    colorBlending.logicOp = VK_LOGIC_OP_COPY;
+    colorBlending.attachmentCount = 1;
+    colorBlending.pAttachments = &_colorBlendAttachment;
+
+    //completely clear VertexInputStateCreateInfo, as we have no need for it
+    VkPipelineVertexInputStateCreateInfo _vertexInputInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
+
+    // build the actual pipeline
+    // we now use all of the info structs we have been writing into this one
+    // to create the pipeline
+    VkGraphicsPipelineCreateInfo pipelineInfo = {.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
+    //connect the renderInfo to the pNext extension mechanism
+    pipelineInfo.pNext = &_renderInfo;
+
+    pipelineInfo.stageCount = (uint32_t)_shaderStages.size();
+    pipelineInfo.pStages = _shaderStages.data();
+    pipelineInfo.pVertexInputState = &_vertexInputInfo;
+    pipelineInfo.pInputAssemblyState = &_inputAssembly;
+    pipelineInfo.pViewportState = &viewportState;
+    pipelineInfo.pRasterizationState = &_rasterizer;
+    pipelineInfo.pMultisampleState = &_multisampling;
+    pipelineInfo.pColorBlendState = &colorBlending;
+    pipelineInfo.pDepthStencilState = &_depthStencil;
+    pipelineInfo.layout = _pipelineLayout;
+
+    VkDynamicState state[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
+
+    VkPipelineDynamicStateCreateInfo dynamicInfo = {.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
+    dynamicInfo.pDynamicStates = &state[0];
+    dynamicInfo.dynamicStateCount = 2;
+
+    pipelineInfo.pDynamicState = &dynamicInfo;
+
+    // its easy to error out on create graphics pipeline, so we handle it a bit
+    // better than the common VK_CHECK case
+    VkPipeline newPipeline;
+    if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo,
+                                  nullptr, &newPipeline) != VK_SUCCESS)
+    {
+        fmt::println("failed to create pipeline");
+        return VK_NULL_HANDLE; // failed to create graphics pipeline
+    }
+    else
+    {
+        return newPipeline;
+    }
+}
+
+void vkutil::PipelineBuilder::clear()
+{
+    _inputAssembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
+    _rasterizer = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
+    _colorBlendAttachment = {};
+    _multisampling = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
+    _pipelineLayout = {};
+    _depthStencil = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
+    _renderInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
+    _shaderStages.clear();
+}
+
+void vkutil::PipelineBuilder::set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader)
+{
+    _shaderStages.clear();
+
+    _shaderStages.push_back(
+            vkinit::pipeline_shader_stage_create_info(VK_SHADER_STAGE_VERTEX_BIT, vertexShader));
+
+    _shaderStages.push_back(
+            vkinit::pipeline_shader_stage_create_info(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader));
+}
+
+void vkutil::PipelineBuilder::set_input_topology(VkPrimitiveTopology topology)
+{
+    _inputAssembly.topology = topology;
+    // Used for triangle and line strips
+    _inputAssembly.primitiveRestartEnable = VK_FALSE;
+}
+
+void vkutil::PipelineBuilder::set_polygon_mode(VkPolygonMode mode)
+{
+    _rasterizer.polygonMode = mode;
+    _rasterizer.lineWidth = 1.f;
+}
+
+void vkutil::PipelineBuilder::set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace)
+{
+    _rasterizer.cullMode = cullMode;
+    _rasterizer.frontFace = frontFace;
+}
+
+void vkutil::PipelineBuilder::disable_multisampling()
+{
+    _multisampling.sampleShadingEnable = VK_FALSE;
+    // multisampling defaulted to no multisampling (1 sample per pixel)
+    _multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+    _multisampling.minSampleShading = 1.0f;
+    _multisampling.pSampleMask = nullptr;
+    //no alpha to coverage either
+    _multisampling.alphaToCoverageEnable = VK_FALSE;
+    _multisampling.alphaToOneEnable = VK_FALSE;
+}
+
+void vkutil::PipelineBuilder::disable_blending()
+{
+    //default write mask
+    _colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+    //no blending
+    _colorBlendAttachment.blendEnable = VK_FALSE;
+}
+
+void vkutil::PipelineBuilder::set_color_attachment_format(VkFormat format)
+{
+    _colorAttachmentFormat = format;
+    //connect the format to the renderInfo  structure
+    _renderInfo.colorAttachmentCount = 1;
+    _renderInfo.pColorAttachmentFormats = &_colorAttachmentFormat;
+}
+
+void vkutil::PipelineBuilder::set_depth_format(VkFormat format)
+{
+    _renderInfo.depthAttachmentFormat = format;
+}
+
+void vkutil::PipelineBuilder::disable_depth_test()
+{
+    _depthStencil.depthTestEnable = VK_FALSE;
+    _depthStencil.depthWriteEnable = VK_FALSE;
+    _depthStencil.depthCompareOp = VK_COMPARE_OP_NEVER;
+    _depthStencil.depthBoundsTestEnable = VK_FALSE;
+    _depthStencil.stencilTestEnable = VK_FALSE;
+    _depthStencil.front = {};
+    _depthStencil.back = {};
+    _depthStencil.minDepthBounds = 0.f;
+    _depthStencil.maxDepthBounds= 1.f;
+}

+ 30 - 0
src/vk_pipelines.h

@@ -6,4 +6,34 @@ namespace vkutil
     bool load_shader_module(const char* filePath,
                                     VkDevice device,
                                     VkShaderModule* outShaderModule);
+
+    class PipelineBuilder
+    {
+    public:
+        std::vector<VkPipelineShaderStageCreateInfo> _shaderStages{};
+
+        VkPipelineInputAssemblyStateCreateInfo _inputAssembly{};
+        VkPipelineRasterizationStateCreateInfo _rasterizer{};
+        VkPipelineColorBlendAttachmentState _colorBlendAttachment{};
+        VkPipelineMultisampleStateCreateInfo _multisampling{};
+        VkPipelineLayout _pipelineLayout{};
+        VkPipelineDepthStencilStateCreateInfo _depthStencil{};
+        VkPipelineRenderingCreateInfo _renderInfo{};
+        VkFormat _colorAttachmentFormat{};
+
+        PipelineBuilder() { clear(); }
+
+        VkPipeline build_pipeline(VkDevice device);
+        void clear();
+
+        void set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader);
+        void set_input_topology(VkPrimitiveTopology topology);
+        void set_polygon_mode(VkPolygonMode mode);
+        void set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace);
+        void disable_multisampling();
+        void disable_blending();
+        void set_color_attachment_format(VkFormat format);
+        void set_depth_format(VkFormat format);
+        void disable_depth_test();
+    };
 };

+ 8 - 0
src/vk_types.h

@@ -73,6 +73,14 @@ struct ComputeEffect
     ComputePushConstants data;
 };
 
+struct Effect
+{
+    const char* name;
+
+    VkPipeline pipeline;
+    VkPipelineLayout layout;
+};
+
 struct ImmediateBuffer
 {
     VkFence _fence;