From 5115eb592d1477b355770eee9d6b913481d4859f Mon Sep 17 00:00:00 2001 From: kartofen Date: Fri, 20 Oct 2023 19:31:31 +0300 Subject: graphics pipeline --- Makefile | 2 +- files/shaders/shader1.frag | 6 +- files/shaders/shader1.vert | 12 +- src/common.h | 30 +++++ src/device.c | 328 ++++++++++++++++++++++++++++++++++++++++++--- src/device.h | 21 ++- 6 files changed, 376 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 0009173..f4d58ba 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ $(BIND)/$(NAME): $(COBJS) # Extra Tools -RUN_CMD := $(BIND)/$(NAME) +RUN_CMD := cd $(BIND) && ./$(NAME) UNITY := unity $(BIND)/$(UNITY): $(CSRCS) diff --git a/files/shaders/shader1.frag b/files/shaders/shader1.frag index f30417b..7c5b0e7 100644 --- a/files/shaders/shader1.frag +++ b/files/shaders/shader1.frag @@ -1,7 +1,9 @@ #version 450 -layout (location = 0) out vec4 outColor; +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(1.0, 0.0, 0.0, 1.0); + outColor = vec4(fragColor, 1.0); } diff --git a/files/shaders/shader1.vert b/files/shaders/shader1.vert index c5d117f..f5b2f8d 100644 --- a/files/shaders/shader1.vert +++ b/files/shaders/shader1.vert @@ -1,10 +1,20 @@ #version 450 +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)); + 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]; } diff --git a/src/common.h b/src/common.h index 1c80ab8..27d8404 100644 --- a/src/common.h +++ b/src/common.h @@ -27,4 +27,34 @@ void *xmalloc(size_t size); void *xcalloc(size_t nmemb, size_t size); void *xrealloc(void *ptr, size_t size); +#define F_LOAD_FILE_ALIGNED(T) \ + static int load_file_##T##_aligned( \ + char *path, size_t *size, u32 *buf) \ + { \ + int ret = 1; \ + \ + FILE *fp = fopen(path, "rb"); \ + if(!fp) { \ + err("fopen: %s: %s", path, strerror(errno)); \ + return 1; \ + } \ + \ + fseek(fp, 0, SEEK_END); \ + *size = (size_t)ftell(fp); \ + *size += (*size % sizeof(u32)); \ + \ + if(buf == NULL) { \ + ret = 0; \ + goto exit; \ + } \ + \ + fseek(fp, 0, SEEK_SET); \ + fread(buf, sizeof(u32), *size/sizeof(u32), fp); \ + \ + return 0; \ + exit: \ + fclose(fp); \ + return ret; \ + } + #endif diff --git a/src/device.c b/src/device.c index 75a727d..a200e5e 100644 --- a/src/device.c +++ b/src/device.c @@ -1,15 +1,20 @@ #include +#include #include #include "device.h" #include "common.h" // TODO: rename device.c/.h -// TODO: add error checking +// TODO: add more error checking // TODO: add log output +// TODO: check for memory leaks #define CLAMP(val, min, max) (((val) < (min)) ? (min) : ((val) > (max) ? (max) : (val))) +#define ECHECK(f, ...) if(f(__VA_ARGS__) != 0) { err(#f ": failed"); goto exit; } +#define FCHECK(f, ...) if(f(__VA_ARGS__) != 0) { err(#f ": failed"); goto fail; } + #ifdef DEBUG char *validation_layers[] = { "VK_LAYER_KHRONOS_validation" @@ -34,7 +39,7 @@ struct swap_chain_support_details { VkSurfaceFormatKHR *formats; u32 nformats; - VkPresentModeKHR *present_modes; + VkPresentModeKHR *present_modes; u32 npresent_modes; }; @@ -47,18 +52,29 @@ static int create_surface(device_t device, struct device_info *info); static int create_physical_device(device_t device, struct device_info *info); static int create_logical_device(device_t device, struct device_info *info); static int create_swap_chain(device_t device, struct device_info *info); +static int create_render_pass(device_t device, struct device_info *info); +static int create_pipeline(device_t device, struct device_info *info); + +static void destroy_swap_chain(device_t device); +static void destroy_pipeline(device_t device); // --- Helper Function --- static bool device_is_suitable(VkPhysicalDevice phy_device, VkSurfaceKHR surface); static bool device_has_extension_support(VkPhysicalDevice phy_device); static int device_queue_families(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct queue_family_idx *queue_family); +#define SWCH device->swap_chain static int swap_chain_support(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct swap_chain_support_details *details); static void swap_chain_free_support_details(struct swap_chain_support_details *details); static int swap_chain_choose_format(VkSurfaceFormatKHR* formats, u32 nformats, VkSurfaceFormatKHR *format); static int swap_chain_choose_present_mode(VkPresentModeKHR *modes, u32 nmodes, VkPresentModeKHR *mode); static int swap_chain_get_extent(VkSurfaceCapabilitiesKHR capabilities, VkExtent2D *extent); +#define PPLN device->pipeline +static int pipeline_load_shader_module(VkDevice device, char *path, VkShaderModule *module); + +F_LOAD_FILE_ALIGNED(u32) // from config.h + // --- Debug Functions --- #ifdef DEBUG static bool instance_has_validation_layers(const char * const *layers, u32 nlayers); @@ -84,23 +100,30 @@ void DestroyDebugUtilsMessengerEXT( const VkAllocationCallbacks* pAllocator); #endif -#define CHECK(func) if(func(device, info)) { goto fail; } +#define CCHECK(f) FCHECK(f, device, info) device_t device_create(struct device_info *info) { device_t device = xmalloc(sizeof(struct device)); device->instance = VK_NULL_HANDLE; device->logical_device = VK_NULL_HANDLE; - device->swap_chain = VK_NULL_HANDLE; + + PPLN.pipeline = VK_NULL_HANDLE; + PPLN.layout = VK_NULL_HANDLE; + + SWCH.swap_chain = VK_NULL_HANDLE; + SWCH.nimages = 0; - CHECK(create_instance); - CHECK(create_surface); + CCHECK(create_instance); + CCHECK(create_surface); #ifdef DEBUG - CHECK(create_debug_messenger); + CCHECK(create_debug_messenger); #endif - CHECK(create_physical_device); - CHECK(create_logical_device); - CHECK(create_swap_chain) + CCHECK(create_physical_device); + CCHECK(create_logical_device); + CCHECK(create_swap_chain); + CCHECK(create_render_pass); + CCHECK(create_pipeline); return device; fail: @@ -112,8 +135,8 @@ void device_destroy(device_t device) { if(!device) return; - free(device->swap_chain_images); - vkDestroySwapchainKHR(device->logical_device, device->swap_chain, NULL); + destroy_pipeline(device); + destroy_swap_chain(device); vkDestroyDevice(device->logical_device, NULL); @@ -315,6 +338,7 @@ exit: static int create_swap_chain(device_t device, struct device_info *info) { + (void)info; int ret = 1; @@ -370,14 +394,46 @@ static int create_swap_chain(device_t device, struct device_info *info) create_info.oldSwapchain = NULL; - if (vkCreateSwapchainKHR(device->logical_device, &create_info, NULL, &device->swap_chain) != VK_SUCCESS) { + if (vkCreateSwapchainKHR(device->logical_device, &create_info, NULL, &SWCH.swap_chain) != VK_SUCCESS) { + err("Couldn't create a Swapchain"); goto exit; } - vkGetSwapchainImagesKHR(device->logical_device, device->swap_chain, &device->nimages, NULL); + SWCH.image_format = surface_format.format; + SWCH.extent = extent; + + vkGetSwapchainImagesKHR(device->logical_device, SWCH.swap_chain, &SWCH.nimages, NULL); - device->swap_chain_images = xcalloc(device->nimages, sizeof(*device->swap_chain_images)); - vkGetSwapchainImagesKHR(device->logical_device, device->swap_chain, &device->nimages, device->swap_chain_images); + SWCH.images = xcalloc(SWCH.nimages, sizeof(*SWCH.images)); + SWCH.image_views = xcalloc(SWCH.nimages, sizeof(SWCH.image_views)); + + vkGetSwapchainImagesKHR(device->logical_device, SWCH.swap_chain, &SWCH.nimages, SWCH.images); + + for(u32 i = 0; i < SWCH.nimages; i++) + { + VkImageViewCreateInfo create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.image = SWCH.images[i]; + + create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + create_info.format = surface_format.format; + + create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + create_info.subresourceRange.baseMipLevel = 0; + create_info.subresourceRange.levelCount = 1; + create_info.subresourceRange.baseArrayLayer = 0; + create_info.subresourceRange.layerCount = 1; + + if(vkCreateImageView(device->logical_device, &create_info, NULL, &SWCH.image_views[i]) != VK_SUCCESS) { + err("Couldn't create image view"); + goto exit; + } + } ret = 0; exit: @@ -385,6 +441,219 @@ exit: return ret; } +static void destroy_swap_chain(device_t device) +{ + for(u32 i = 0; i < SWCH.nimages; i++) { + vkDestroyImageView(device->logical_device, SWCH.image_views[i], NULL); + } + if(SWCH.nimages != 0) { + free(SWCH.images); + free(SWCH.image_views); + } + + vkDestroySwapchainKHR(device->logical_device, SWCH.swap_chain, NULL); +} + +static int create_render_pass(device_t device, struct device_info *info) +{ + (void)info; + int ret = 1; + + VkAttachmentDescription color_attachment = {0}; + color_attachment.format = SWCH.image_format; + color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference color_attachment_ref = {0}; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {0}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment_ref; + + VkRenderPassCreateInfo render_pass_info = {0}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.attachmentCount = 1; + render_pass_info.pAttachments = &color_attachment; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass; + + if (vkCreateRenderPass(device->logical_device, &render_pass_info, NULL, &PPLN.render_pass) != VK_SUCCESS) { + err("Couldnt create a render pass"); + goto exit; + } + + ret = 0; +exit: + return ret; +} + +static int create_pipeline(device_t device, struct device_info *info) +{ + (void)info; + int ret = 1; + + VkShaderModule vert_shader = VK_NULL_HANDLE; + VkShaderModule frag_shader = VK_NULL_HANDLE; + + pipeline_load_shader_module(device->logical_device, "shaders/shader1.vert.spv", &vert_shader); + pipeline_load_shader_module(device->logical_device, "shaders/shader1.frag.spv", &frag_shader); + + VkPipelineShaderStageCreateInfo vert_stage = {0}; + vert_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vert_stage.stage = VK_SHADER_STAGE_VERTEX_BIT; + vert_stage.module = vert_shader; + vert_stage.pName = "main"; + + VkPipelineShaderStageCreateInfo frag_stage = {0}; + frag_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + frag_stage.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + frag_stage.module = frag_shader; + frag_stage.pName = "main"; + + VkPipelineShaderStageCreateInfo shader_stages[2] = { vert_stage, frag_stage }; + + VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + + VkPipelineDynamicStateCreateInfo dynamic_state = {0}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = ARR_SIZE(dynamic_states); + dynamic_state.pDynamicStates = dynamic_states; + + // vertex things - hard coded (for now) + VkPipelineVertexInputStateCreateInfo vertex_input = {0}; + vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input.vertexBindingDescriptionCount = 0; + vertex_input.pVertexBindingDescriptions = NULL; + vertex_input.vertexAttributeDescriptionCount = 0; + vertex_input.pVertexAttributeDescriptions = NULL; + + VkPipelineInputAssemblyStateCreateInfo input_assembly = {0}; + input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + // VkViewport viewport = {0}; + // viewport.x = 0.0f; + // viewport.y = 0.0f; + // viewport.width = (float) SWCH.extent.width; + // viewport.height = (float) SWCH.extent.height; + // viewport.minDepth = 0.0f; + // viewport.maxDepth = 1.0f; + + // VkRect2D scissor = {0}; + // scissor.offset = {0, 0}; + // scissor.extent = SWCH.extent; + + VkPipelineViewportStateCreateInfo viewport_state = {0}; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterizer = {0}; + 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; // Optional + rasterizer.depthBiasClamp = 0.0f; // Optional + rasterizer.depthBiasSlopeFactor = 0.0f; // Optional + + VkPipelineMultisampleStateCreateInfo multisampling = {0}; + 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; // Optional + multisampling.pSampleMask = NULL; // Optional + multisampling.alphaToCoverageEnable = VK_FALSE; // Optional + multisampling.alphaToOneEnable = VK_FALSE; // Optional + + VkPipelineColorBlendAttachmentState color_blend_attachment = {0}; + color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + color_blend_attachment.blendEnable = VK_FALSE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional + color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional + + VkPipelineColorBlendStateCreateInfo color_blend = {0}; + color_blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blend.logicOpEnable = VK_FALSE; + color_blend.logicOp = VK_LOGIC_OP_COPY; // Optional + color_blend.attachmentCount = 1; + color_blend.pAttachments = &color_blend_attachment; + color_blend.blendConstants[0] = 0.0f; // Optional + color_blend.blendConstants[1] = 0.0f; // Optional + color_blend.blendConstants[2] = 0.0f; // Optional + color_blend.blendConstants[3] = 0.0f; // Optional + + VkPipelineLayoutCreateInfo pipeline_layout_info = {0}; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 0; // Optional + pipeline_layout_info.pSetLayouts = NULL; // Optional + pipeline_layout_info.pushConstantRangeCount = 0; // Optional + pipeline_layout_info.pPushConstantRanges = NULL; // Optional + + if(vkCreatePipelineLayout(device->logical_device, &pipeline_layout_info, NULL, &PPLN.layout) != VK_SUCCESS) { + goto exit; + } + + VkGraphicsPipelineCreateInfo pipeline_info = {0}; + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + + pipeline_info.pVertexInputState = &vertex_input; + pipeline_info.pInputAssemblyState = &input_assembly; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterizer; + pipeline_info.pMultisampleState = &multisampling; + pipeline_info.pDepthStencilState = NULL; + pipeline_info.pColorBlendState = &color_blend; + pipeline_info.pDynamicState = &dynamic_state; + + pipeline_info.layout = PPLN.layout; + pipeline_info.renderPass = PPLN.render_pass; + pipeline_info.subpass = 0; + + pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // Optional + pipeline_info.basePipelineIndex = -1; // Optional + + if(vkCreateGraphicsPipelines(device->logical_device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &PPLN.pipeline) != VK_SUCCESS) { + err("Couldnt create a graphics pipeline"); + goto exit; + } + + ret = 0; +exit: + if(frag_shader != VK_NULL_HANDLE) + vkDestroyShaderModule(device->logical_device, frag_shader, NULL); + if(vert_shader != VK_NULL_HANDLE) + vkDestroyShaderModule(device->logical_device, vert_shader, NULL); + return ret; +} + +static void destroy_pipeline(device_t device) +{ + vkDestroyPipeline(device->logical_device, PPLN.pipeline, NULL); + vkDestroyPipelineLayout(device->logical_device, PPLN.layout, NULL); + vkDestroyRenderPass(device->logical_device, PPLN.render_pass, NULL); +} + static int device_queue_families(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct queue_family_idx *queue_family) { u32 count = 0; @@ -516,6 +785,33 @@ static int swap_chain_get_extent(VkSurfaceCapabilitiesKHR capabilities, VkExtent return 1; } +static int pipeline_load_shader_module(VkDevice device, char *path, VkShaderModule *module) +{ + int ret = 1; + + size_t size = 0; + u32 *buf = NULL; + + ECHECK(load_file_u32_aligned, path, &size, NULL); + buf = xmalloc(size); + ECHECK(load_file_u32_aligned, path, &size, buf); + + VkShaderModuleCreateInfo create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = size; + create_info.pCode = buf; + + if (vkCreateShaderModule(device, &create_info, NULL, module) != VK_SUCCESS) { + err("Couldnt create shader module"); + goto exit; + } + + ret = 0; +exit: + if(buf) free(buf); + return ret; +} + #ifdef DEBUG static bool instance_has_validation_layers(const char * const *layers, u32 nlayers) diff --git a/src/device.h b/src/device.h index 3491b8a..05bdbc1 100644 --- a/src/device.h +++ b/src/device.h @@ -19,14 +19,29 @@ typedef struct device { VkPhysicalDevice physical_device; VkDevice logical_device; + VkQueue graphics_queue; VkQueue present_queue; - VkSwapchainKHR swap_chain; VkSurfaceKHR surface; + + struct swap_chain { + VkSwapchainKHR swap_chain; + + VkImage *images; + VkImageView *image_views; + u32 nimages; + + VkFormat image_format; + VkExtent2D extent; + } swap_chain; + + struct pipeline { + VkRenderPass render_pass; + VkPipelineLayout layout; + VkPipeline pipeline; + } pipeline; - VkImage *swap_chain_images; - u32 nimages; } * device_t; struct device_info { -- cgit v1.2.3