diff --git a/samples/sample/main.cpp b/samples/sample/main.cpp index 2820b84..ee021f2 100644 --- a/samples/sample/main.cpp +++ b/samples/sample/main.cpp @@ -2,6 +2,7 @@ #include #include "window.h" #include "vulkan_instance.h" +#include "vulkan_shader_module.h" VulkanInstance* instance; VulkanDevice* device; @@ -57,6 +58,157 @@ VkRenderPass CreateRenderPass() { return renderPass; } +VkPipelineLayout CreatePipelineLayout() { + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 0; + pipelineLayoutInfo.pSetLayouts = nullptr; + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = 0; + + VkPipelineLayout pipelineLayout; + if (vkCreatePipelineLayout(device->GetVulkanDevice(), &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline layout"); + } + + return pipelineLayout; +} + +VkPipeline CreatePipeline(VkPipelineLayout pipelineLayout, VkRenderPass renderPass, unsigned int subpass) { + VkShaderModule vertShaderModule = createShaderModule("sample/shaders/shader.vert.spv", device->GetVulkanDevice()); + VkShaderModule fragShaderModule = createShaderModule("sample/shaders/shader.frag.spv", device->GetVulkanDevice()); + + // Assign each shader module to the appropriate stage in the pipeline + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + + // --- Set up fixed-function stages --- + + // Vertex input + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = nullptr; + + // Input assembly + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + // Viewports and Scissors (rectangles that define in which regions pixels are stored) + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(swapchain->GetExtent().width); + viewport.height = static_cast(swapchain->GetExtent().height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = swapchain->GetExtent(); + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + // Rasterizer + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + // Multisampling (turned off here) + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + // Color blending (turned off here, but showing options for learning) + // --> Configuration per attached framebuffer + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + // --> Global color blending settings + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + // --- Create graphics pipeline --- + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = pipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = subpass; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + VkPipeline pipeline; + if (vkCreateGraphicsPipelines(device->GetVulkanDevice(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline"); + } + + // No need for the shader modules anymore + vkDestroyShaderModule(device->GetVulkanDevice(), vertShaderModule, nullptr); + vkDestroyShaderModule(device->GetVulkanDevice(), fragShaderModule, nullptr); + + return pipeline; +} + + std::vector CreateFrameBuffers(VkRenderPass renderPass) { std::vector frameBuffers(swapchain->GetCount()); for (uint32_t i = 0; i < swapchain->GetCount(); i++) { @@ -79,7 +231,7 @@ std::vector CreateFrameBuffers(VkRenderPass renderPass) { } int main(int argc, char** argv) { - static constexpr char* applicationName = "Hello Window"; + static constexpr char* applicationName = "Hello Triangle"; InitializeWindow(640, 480, applicationName); unsigned int glfwExtensionCount = 0; @@ -108,6 +260,8 @@ int main(int argc, char** argv) { } VkRenderPass renderPass = CreateRenderPass(); + VkPipelineLayout pipelineLayout = CreatePipelineLayout(); + VkPipeline pipeline = CreatePipeline(pipelineLayout, renderPass, 0); // Create one framebuffer for each frame of the swap chain std::vector frameBuffers = CreateFrameBuffers(renderPass); @@ -120,7 +274,7 @@ int main(int argc, char** argv) { commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; commandBufferAllocateInfo.commandPool = commandPool; commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - commandBufferAllocateInfo.commandBufferCount = static_cast(commandBuffers.size()); + commandBufferAllocateInfo.commandBufferCount = (uint32_t)commandBuffers.size(); if (vkAllocateCommandBuffers(device->GetVulkanDevice(), &commandBufferAllocateInfo, commandBuffers.data()) != VK_SUCCESS) { throw std::runtime_error("Failed to allocate command buffers"); @@ -148,6 +302,12 @@ int main(int argc, char** argv) { vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + // Bind the graphics pipeline + vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + // Draw + vkCmdDraw(commandBuffers[i], 3, 1, 0, 0); + vkCmdEndRenderPass(commandBuffers[i]); if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { @@ -157,28 +317,28 @@ int main(int argc, char** argv) { while (!ShouldQuit()) { swapchain->Acquire(); - + // Submit the command buffer VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - + VkSemaphore waitSemaphores[] = { swapchain->GetImageAvailableSemaphore() }; VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; - + submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffers[swapchain->GetIndex()]; - + VkSemaphore signalSemaphores[] = { swapchain->GetRenderFinishedSemaphore() }; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; - + if (vkQueueSubmit(device->GetQueue(QueueFlags::Graphics), 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { throw std::runtime_error("Failed to submit draw command buffer"); } - + swapchain->Present(); glfwPollEvents(); @@ -187,6 +347,8 @@ int main(int argc, char** argv) { // Wait for the device to finish executing before cleanup vkDeviceWaitIdle(device->GetVulkanDevice()); + vkDestroyPipeline(device->GetVulkanDevice(), pipeline, nullptr); + vkDestroyPipelineLayout(device->GetVulkanDevice(), pipelineLayout, nullptr); vkDestroyRenderPass(device->GetVulkanDevice(), renderPass, nullptr); vkFreeCommandBuffers(device->GetVulkanDevice(), commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); for (size_t i = 0; i < frameBuffers.size(); i++) { @@ -200,4 +362,4 @@ int main(int argc, char** argv) { delete instance; DestroyWindow(); -} \ No newline at end of file +} diff --git a/samples/sample/shaders/shader.frag b/samples/sample/shaders/shader.frag new file mode 100644 index 0000000..1730b4f --- /dev/null +++ b/samples/sample/shaders/shader.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/samples/sample/shaders/shader.vert b/samples/sample/shaders/shader.vert new file mode 100644 index 0000000..dbf785e --- /dev/null +++ b/samples/sample/shaders/shader.vert @@ -0,0 +1,25 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +out gl_PerVertex { + vec4 gl_Position; +}; + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} \ No newline at end of file diff --git a/samples/sample/vulkan_shader_module.cpp b/samples/sample/vulkan_shader_module.cpp new file mode 100644 index 0000000..de281ef --- /dev/null +++ b/samples/sample/vulkan_shader_module.cpp @@ -0,0 +1,40 @@ +#include +#include "vulkan_shader_module.h" + +namespace { + std::vector readFile(const std::string& filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + + if (!file.is_open()) { + throw std::runtime_error("Failed to open file"); + } + + size_t fileSize = (size_t)file.tellg(); + std::vector buffer(fileSize); + + file.seekg(0); + file.read(buffer.data(), fileSize); + + file.close(); + return buffer; + } +} + +// Wrap the shaders in shader modules +VkShaderModule createShaderModule(const std::vector& code, VkDevice logicalDevice) { + VkShaderModuleCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + if (vkCreateShaderModule(logicalDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw std::runtime_error("Failed to create shader module"); + } + + return shaderModule; +} + +VkShaderModule createShaderModule(const std::string& filename, VkDevice logicalDevice) { + return createShaderModule(readFile(filename), logicalDevice); +} \ No newline at end of file diff --git a/samples/sample/vulkan_shader_module.h b/samples/sample/vulkan_shader_module.h new file mode 100644 index 0000000..77043d0 --- /dev/null +++ b/samples/sample/vulkan_shader_module.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include + +VkShaderModule createShaderModule(const std::vector& code, VkDevice logicalDevice); +VkShaderModule createShaderModule(const std::string& filename, VkDevice logicalDevice); \ No newline at end of file