Updated gitignore, added pipeline creation code and basic render loop

main
noah metz 2024-10-09 14:31:30 -06:00
parent bf3a2c33df
commit 1fa7b38624
9 changed files with 381 additions and 10 deletions

2
.gitignore vendored

@ -28,3 +28,5 @@ compile_commands.json
roleplay roleplay
.cache .cache
*.o

@ -6,7 +6,7 @@ CPP = /opt/homebrew/opt/llvm/bin/clang++
DSYM = /opt/homebrew/opt/llvm/bin/dsymutil DSYM = /opt/homebrew/opt/llvm/bin/dsymutil
GDB = /opt/homebrew/opt/llvm/bin/lldb GDB = /opt/homebrew/opt/llvm/bin/lldb
SOURCES = src/main.c src/render.c src/vma.cpp SOURCES = src/main.c src/render.c src/vma.cpp src/pipeline.c
OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
VERT_SPV = $(addsuffix .vert.spv, $(basename $(wildcard shader_src/*.vert))) VERT_SPV = $(addsuffix .vert.spv, $(basename $(wildcard shader_src/*.vert)))
FRAG_SPV = $(addsuffix .frag.spv, $(basename $(wildcard shader_src/*.frag))) FRAG_SPV = $(addsuffix .frag.spv, $(basename $(wildcard shader_src/*.frag)))

@ -0,0 +1,20 @@
#ifndef PIPELINE_H
#define PIPELINE_H
#include "vulkan/vulkan_core.h"
#include "cglm/types.h"
typedef struct GraphicsPipelineStruct {
VkDescriptorPool descriptor_pool;
VkDescriptorSet* descriptors;
VkPipelineLayout layout;
VkPipeline pipeline;
} GraphicsPipeline;
struct Vertex2 {
vec2 pos;
vec3 color;
};
#endif

@ -24,6 +24,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
extern const uint32_t MAX_FRAMES_IN_FLIGHT;
typedef struct QueueStruct { typedef struct QueueStruct {
VkQueue handle; VkQueue handle;
uint32_t family; uint32_t family;
@ -83,9 +85,12 @@ typedef struct RenderContextStruct {
VkPipeline ui_pipeline_rect; VkPipeline ui_pipeline_rect;
VkPipeline ui_pipeline_text; VkPipeline ui_pipeline_text;
uint32_t current_frame;
} RenderContext; } RenderContext;
GLFWwindow* init_window(); GLFWwindow* init_window();
VkResult init_vulkan(GLFWwindow* window, RenderContext* context); VkResult init_vulkan(GLFWwindow* window, RenderContext* context);
VkResult draw_frame(RenderContext* context);
#endif #endif

@ -1,8 +1,15 @@
#include "render.h" #include "render.h"
#include "vulkan/vk_enum_string_helper.h"
int render_thread(GLFWwindow* window, RenderContext* render_context) { VkResult render_thread(GLFWwindow* window, RenderContext* render_context) {
while(glfwWindowShouldClose(window) == 0) { while(glfwWindowShouldClose(window) == 0) {
glfwPollEvents(); glfwPollEvents();
VkResult result = draw_frame(render_context);
if(result != VK_SUCCESS) {
fprintf(stderr, "draw_frame error: %s\n", string_VkResult(result));
return result;
}
} }
return 0; return 0;
} }
@ -26,7 +33,7 @@ int main() {
return 2; return 2;
} }
if(render_thread(window, &render_context) != 0) { if(render_thread(window, &render_context) != VK_SUCCESS) {
return 3; return 3;
} }

Binary file not shown.

@ -0,0 +1,249 @@
#include "pipeline.h"
#include "stdio.h"
#include "stdlib.h"
VkShaderModule load_shader_file(const char* path, VkDevice device) {
FILE* file;
file = fopen(path, "rb");
if(file == 0) {
return VK_NULL_HANDLE;
}
int result = fseek(file, 0, SEEK_END);
if(result != 0) {
return VK_NULL_HANDLE;
}
long buffer_size = ftell(file);
result = fseek(file, 0, SEEK_SET);
if(result != 0) {
return VK_NULL_HANDLE;
}
char * buffer = malloc(buffer_size);
if(buffer == 0) {
return VK_NULL_HANDLE;
}
size_t read = fread(buffer, 1, buffer_size, file);
VkShaderModuleCreateInfo shader_info = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = read,
.pCode = (uint32_t*)buffer,
};
VkShaderModule shader;
result = vkCreateShaderModule(device, &shader_info, 0, &shader);
free(buffer);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return shader;
}
VkResult create_ui_rect_pipeline(VkDevice device, VkRenderPass render_pass, GraphicsPipeline* out) {
if(out == NULL) {
return VK_ERROR_VALIDATION_FAILED_EXT;
}
VkShaderModule vert_shader = load_shader_file("shader_src/ui_rect.vert.spv", device);
VkShaderModule frag_shader = load_shader_file("shader_src/ui_rect.frag.spv", device);
VkPipelineShaderStageCreateInfo shader_stages[2] = {
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vert_shader,
.pName = "main",
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = frag_shader,
.pName = "main",
},
};
VkVertexInputBindingDescription bindings[1] = {
{
.binding = 0, // Which buffer 'binding' to use
.stride = sizeof(struct Vertex2), // How many bytes to increase the index between instance
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX, // Whether an instance is a vertex or an index
},
};
VkVertexInputAttributeDescription attributes[3] = {
{
.binding = 0, // Which buffer 'binding' to use
.location = 0, // Which 'location' to export as to shader
.format = VK_FORMAT_R32G32_SFLOAT, // What format to interpret as for shader
.offset = offsetof(struct Vertex2, pos), // What offset from instance start
},
{
.binding = 0,
.location = 1,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(struct Vertex2, color),
},
};
VkPipelineVertexInputStateCreateInfo input_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pVertexBindingDescriptions = bindings,
.vertexBindingDescriptionCount = sizeof(bindings)/sizeof(VkVertexInputBindingDescription),
.pVertexAttributeDescriptions = attributes,
.vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription),
};
VkPushConstantRange pcr = {
.stageFlags = VK_SHADER_STAGE_ALL,
.offset = 0,
.size = sizeof(VkDeviceAddress),
};
VkPipelineLayoutCreateInfo layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pcr,
};
VkResult result = vkCreatePipelineLayout(device, &layout_info, 0, &out->layout);
if(result != VK_SUCCESS) {
return result;
}
VkDynamicState dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
uint32_t dynamic_state_count = sizeof(dynamic_states)/sizeof(VkDynamicState);
VkPipelineDynamicStateCreateInfo dynamic_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = dynamic_state_count,
.pDynamicStates = dynamic_states,
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
.width = (float)(100),
.height = (float)(100),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
VkRect2D scissor = {
.offset = {
.x = 0,
.y = 0,
},
.extent = {
.width = 100,
.height = 100,
},
};
VkPipelineViewportStateCreateInfo viewport_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.pViewports = &viewport,
.scissorCount = 1,
.pScissors = &scissor,
};
VkPipelineRasterizationStateCreateInfo raster_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
.cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
};
VkPipelineMultisampleStateCreateInfo multisample_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = VK_FALSE,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.minSampleShading = 1.0f,
.pSampleMask = 0,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
VkPipelineDepthStencilStateCreateInfo depth_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_LESS,
.depthBoundsTestEnable = VK_FALSE,
.maxDepthBounds = 1.0f,
.minDepthBounds = 0.0f,
.stencilTestEnable = VK_FALSE,
.front = {},
.back = {},
};
VkPipelineColorBlendAttachmentState color_blend_attachment = {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
};
VkPipelineColorBlendStateCreateInfo color_blend_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &color_blend_attachment,
.blendConstants[0] = 0.0f,
.blendConstants[1] = 0.0f,
.blendConstants[2] = 0.0f,
.blendConstants[3] = 0.0f,
};
VkGraphicsPipelineCreateInfo draw_pipeline_info = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = shader_stages,
.pVertexInputState = &input_info,
.pInputAssemblyState = &input_assembly_info,
.pViewportState = &viewport_state,
.pRasterizationState = &raster_info,
.pColorBlendState = &color_blend_info,
.pDynamicState = &dynamic_info,
.pDepthStencilState = &depth_info,
.pMultisampleState = &multisample_info,
.layout = out->layout,
.renderPass = render_pass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1,
};
result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &draw_pipeline_info, 0, &out->pipeline);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}

@ -1,5 +1,5 @@
#include "render.h" #include "render.h"
#include "vk_mem_alloc.h" #include "vulkan/vulkan_core.h"
const uint32_t MAX_FRAMES_IN_FLIGHT = 2; const uint32_t MAX_FRAMES_IN_FLIGHT = 2;
@ -44,7 +44,7 @@ GLFWwindow* init_window() {
glfwSetErrorCallback(glfw_error); glfwSetErrorCallback(glfw_error);
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWwindow* window = glfwCreateWindow(800, 500, "roleplay", 0, 0); GLFWwindow* window = glfwCreateWindow(800, 500, "roleplay", 0, 0);
return window; return window;
@ -755,6 +755,8 @@ VkResult create_swapchain_framebuffers(VkDevice device, uint32_t image_count, Vk
return 0; return 0;
} }
VkFramebuffer* framebuffer_ptr = *framebuffers;
for(uint32_t i = 0; i < image_count; i++) { for(uint32_t i = 0; i < image_count; i++) {
VkImageView attachments[] = { VkImageView attachments[] = {
image_views[i], image_views[i],
@ -771,7 +773,7 @@ VkResult create_swapchain_framebuffers(VkDevice device, uint32_t image_count, Vk
.layers = 1, .layers = 1,
}; };
VkResult result = vkCreateFramebuffer(device, &framebuffer_info, 0, *framebuffers + sizeof(VkFramebuffer)*i); VkResult result = vkCreateFramebuffer(device, &framebuffer_info, 0, &framebuffer_ptr[i]);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
free(*framebuffers); free(*framebuffers);
return result; return result;
@ -868,7 +870,7 @@ VkResult create_depth_image(VkDevice device, VkFormat depth_format, VkExtent2D s
}; };
VmaAllocationCreateInfo allocation_info = { VmaAllocationCreateInfo allocation_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY, .usage = VMA_MEMORY_USAGE_AUTO,
}; };
VkResult result = vmaCreateImage(allocator, &depth_image_info, &allocation_info, depth_image, depth_image_memory, NULL); VkResult result = vmaCreateImage(allocator, &depth_image_info, &allocation_info, depth_image, depth_image_memory, NULL);
@ -1031,7 +1033,6 @@ VkResult init_vulkan(GLFWwindow* window, RenderContext* context) {
return result; return result;
} }
// TODO: create and allocate the depth image
result = create_depth_image(context->device, context->depth_format, context->swapchain_extent, context->allocator, context->extra_graphics_pool, context->graphics_queue, &context->depth_image, &context->depth_image_memory, &context->depth_image_view); result = create_depth_image(context->device, context->depth_format, context->swapchain_extent, context->allocator, context->extra_graphics_pool, context->graphics_queue, &context->depth_image, &context->depth_image_memory, &context->depth_image_view);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return result; return result;
@ -1042,9 +1043,96 @@ VkResult init_vulkan(GLFWwindow* window, RenderContext* context) {
return result; return result;
} }
// TODO: create pipelines context->current_frame = 0;
return VK_SUCCESS; return VK_SUCCESS;
} }
VkResult draw_frame(RenderContext* context) {
VkResult result;
result = vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
if(result != VK_SUCCESS) {
return result;
}
result = vkResetFences(context->device, 1, &context->in_flight_fences[context->current_frame]);
if(result != VK_SUCCESS) {
return result;
}
uint32_t image_index;
result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphores[context->current_frame], VK_NULL_HANDLE, &image_index);
if(result != VK_SUCCESS) {
return result;
}
result = vkResetCommandBuffer(context->swapchain_command_buffers[context->current_frame], 0);
if(result != VK_SUCCESS) {
return result;
}
VkCommandBufferBeginInfo begin_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
};
result = vkBeginCommandBuffer(context->swapchain_command_buffers[context->current_frame], &begin_info);
if(result != VK_SUCCESS) {
return result;
}
VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkRenderPassBeginInfo render_pass_begin = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = context->render_pass,
.framebuffer = context->swapchain_framebuffers[image_index],
.renderArea.offset = {0, 0},
.renderArea.extent = context->swapchain_extent,
.clearValueCount = 1,
.pClearValues = &clear_color,
};
vkCmdBeginRenderPass(context->swapchain_command_buffers[context->current_frame], &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdEndRenderPass(context->swapchain_command_buffers[context->current_frame]);
result = vkEndCommandBuffer(context->swapchain_command_buffers[context->current_frame]);
if(result != VK_SUCCESS) {
return result;
}
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
VkSubmitInfo submit_info = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &context->image_available_semaphores[context->current_frame],
.pWaitDstStageMask = wait_stages,
.commandBufferCount = 1,
.pCommandBuffers = &context->swapchain_command_buffers[context->current_frame],
.signalSemaphoreCount = 1,
.pSignalSemaphores = &context->render_finished_semaphores[context->current_frame],
};
result = vkQueueSubmit(context->graphics_queue.handle, 1, &submit_info, context->in_flight_fences[context->current_frame]);
if(result != VK_SUCCESS) {
return result;
}
VkPresentInfoKHR present_info = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &context->render_finished_semaphores[context->current_frame],
.swapchainCount = 1,
.pSwapchains = &context->swapchain,
.pImageIndices = &image_index,
.pResults = 0,
};
result = vkQueuePresentKHR(context->present_queue.handle, &present_info);
if(result != VK_SUCCESS) {
return result;
}
context->current_frame = (context->current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
return VK_SUCCESS;
}

Binary file not shown.