Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 171 additions & 9 deletions samples/sample/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <vulkan/vulkan.h>
#include "window.h"
#include "vulkan_instance.h"
#include "vulkan_shader_module.h"

VulkanInstance* instance;
VulkanDevice* device;
Expand Down Expand Up @@ -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<float>(swapchain->GetExtent().width);
viewport.height = static_cast<float>(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<VkFramebuffer> CreateFrameBuffers(VkRenderPass renderPass) {
std::vector<VkFramebuffer> frameBuffers(swapchain->GetCount());
for (uint32_t i = 0; i < swapchain->GetCount(); i++) {
Expand All @@ -79,7 +231,7 @@ std::vector<VkFramebuffer> 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;
Expand Down Expand Up @@ -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<VkFramebuffer> frameBuffers = CreateFrameBuffers(renderPass);
Expand All @@ -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<uint32_t>(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");
Expand Down Expand Up @@ -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) {
Expand All @@ -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();
Expand All @@ -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<uint32_t>(commandBuffers.size()), commandBuffers.data());
for (size_t i = 0; i < frameBuffers.size(); i++) {
Expand All @@ -200,4 +362,4 @@ int main(int argc, char** argv) {
delete instance;

DestroyWindow();
}
}
10 changes: 10 additions & 0 deletions samples/sample/shaders/shader.frag
Original file line number Diff line number Diff line change
@@ -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);
}
25 changes: 25 additions & 0 deletions samples/sample/shaders/shader.vert
Original file line number Diff line number Diff line change
@@ -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];
}
40 changes: 40 additions & 0 deletions samples/sample/vulkan_shader_module.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <fstream>
#include "vulkan_shader_module.h"

namespace {
std::vector<char> 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<char> 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<char>& code, VkDevice logicalDevice) {
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = code.size();
createInfo.pCode = reinterpret_cast<const uint32_t*>(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);
}
8 changes: 8 additions & 0 deletions samples/sample/vulkan_shader_module.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <vulkan/vulkan.h>
#include <string>
#include <vector>

VkShaderModule createShaderModule(const std::vector<char>& code, VkDevice logicalDevice);
VkShaderModule createShaderModule(const std::string& filename, VkDevice logicalDevice);