|
|
@ -53,6 +53,13 @@ typedef struct ApplicationState { |
|
|
|
VkExtent2D swapchain_extent; |
|
|
|
VkExtent2D swapchain_extent; |
|
|
|
//describes how to access an image and allows access to it
|
|
|
|
//describes how to access an image and allows access to it
|
|
|
|
VkImageView* swapchain_image_views; |
|
|
|
VkImageView* swapchain_image_views; |
|
|
|
|
|
|
|
//shaders
|
|
|
|
|
|
|
|
VkShaderModule vertex_shader_module; |
|
|
|
|
|
|
|
VkShaderModule fragment_shader_module; |
|
|
|
|
|
|
|
VkRenderPass render_pass; |
|
|
|
|
|
|
|
VkPipelineLayout pipeline_layout; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipeline graphics_pipeline; |
|
|
|
} ApplicationState; |
|
|
|
} ApplicationState; |
|
|
|
|
|
|
|
|
|
|
|
typedef struct QueueFamilyIndices { |
|
|
|
typedef struct QueueFamilyIndices { |
|
|
@ -515,6 +522,267 @@ void create_image_views(ApplicationState* state) { |
|
|
|
printf("created image views\n"); |
|
|
|
printf("created image views\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char* load_file(const char* name, size_t* size) { |
|
|
|
|
|
|
|
FILE *fileptr; |
|
|
|
|
|
|
|
char *buffer; |
|
|
|
|
|
|
|
size_t filelen; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fileptr = fopen(name, "rb"); |
|
|
|
|
|
|
|
fseek(fileptr, 0, SEEK_END); |
|
|
|
|
|
|
|
filelen = ftell(fileptr); |
|
|
|
|
|
|
|
rewind(fileptr); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
buffer = (char*)malloc(filelen * sizeof(char)); |
|
|
|
|
|
|
|
fread(buffer, filelen, 1, fileptr); |
|
|
|
|
|
|
|
fclose(fileptr); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*size = filelen; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return buffer; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkShaderModule create_shader_module(ApplicationState* state, const char* code, size_t size) { |
|
|
|
|
|
|
|
VkShaderModuleCreateInfo create_info = {0}; |
|
|
|
|
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
|
|
|
|
|
|
|
create_info.codeSize = size; |
|
|
|
|
|
|
|
create_info.pCode = (uint32_t*)code; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkShaderModule ret; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (vkCreateShaderModule(state->device, &create_info, NULL, &ret) != VK_SUCCESS) { |
|
|
|
|
|
|
|
printf("shader creation failed\n"); |
|
|
|
|
|
|
|
exit(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create_render_pass(ApplicationState* state) { |
|
|
|
|
|
|
|
VkAttachmentDescription color_attachment = {0}; |
|
|
|
|
|
|
|
color_attachment.format = state->swapchain_image_format; |
|
|
|
|
|
|
|
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
|
|
|
|
//clear the attachment to a default color that we set
|
|
|
|
|
|
|
|
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
|
|
|
|
|
|
|
//actually store rendered content to the framebuffer
|
|
|
|
|
|
|
|
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
|
|
|
|
|
|
|
//we don't use the stencil buffer
|
|
|
|
|
|
|
|
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
|
|
|
|
|
|
|
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//we are clearing the attachment before rendering, so we don't care about the first format
|
|
|
|
|
|
|
|
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
|
|
|
|
|
|
|
//images to be presented
|
|
|
|
|
|
|
|
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//there may be multiple subpasses, subpasses depend on the previous framebuffer contents
|
|
|
|
|
|
|
|
//they can be used for things sych as postprocessing
|
|
|
|
|
|
|
|
//every subpass references one or more attachment
|
|
|
|
|
|
|
|
VkAttachmentReference color_attachment_reference = {0}; |
|
|
|
|
|
|
|
color_attachment_reference.attachment = 0; |
|
|
|
|
|
|
|
color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//now we create the subpass
|
|
|
|
|
|
|
|
//layout(location = 0) out vec4 outColor;
|
|
|
|
|
|
|
|
VkSubpassDescription subpass = {0}; |
|
|
|
|
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
|
|
|
|
|
|
|
subpass.colorAttachmentCount = 1; |
|
|
|
|
|
|
|
subpass.pColorAttachments = &color_attachment_reference; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//let's create the render pass
|
|
|
|
|
|
|
|
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(state->device, &render_pass_info, NULL, &state->render_pass) != VK_SUCCESS) { |
|
|
|
|
|
|
|
printf("failed to create render pass"); |
|
|
|
|
|
|
|
exit(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
printf("created render pass\n"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//now we need to setup a graphics pipeline
|
|
|
|
|
|
|
|
//the usual stages of a pipeline are:
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//input assembler: gets data from buffers (can have an index buffer)
|
|
|
|
|
|
|
|
//vertex shader : runs for every vertex and applies transformations, also generates per-vertex data
|
|
|
|
|
|
|
|
//tesselation : does geoemtry subdivision
|
|
|
|
|
|
|
|
//geometry shader: runs on every primitive and is able to discard it or generate more
|
|
|
|
|
|
|
|
//rasterization : turns primitives into fragments, which are pixel elements, fragments outside the screen are discarded
|
|
|
|
|
|
|
|
// and attributes are interpolated
|
|
|
|
|
|
|
|
//fragment shader: runs on every fragment, determines which framebuffer it's written to and the color and depth values
|
|
|
|
|
|
|
|
//color blending : applies operations to mix different fragments that map to the same pixel
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create_graphics_pipeline(ApplicationState* state) { |
|
|
|
|
|
|
|
size_t vertex_shader_size; |
|
|
|
|
|
|
|
size_t fragment_shader_size; |
|
|
|
|
|
|
|
char* vertex_shader = load_file("./vertex_simple.spv", &vertex_shader_size); |
|
|
|
|
|
|
|
char* fragment_shader = load_file("./fragment_simple.spv", &fragment_shader_size); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
state->vertex_shader_module = create_shader_module(state, vertex_shader, vertex_shader_size); |
|
|
|
|
|
|
|
state->fragment_shader_module = create_shader_module(state, fragment_shader, fragment_shader_size); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo vertex_shader_stage_info = {0}; |
|
|
|
|
|
|
|
vertex_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
|
|
|
|
|
|
|
vertex_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; |
|
|
|
|
|
|
|
vertex_shader_stage_info.module = state->vertex_shader_module; |
|
|
|
|
|
|
|
vertex_shader_stage_info.pName = "main"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo fragment_shader_stage_info = {0}; |
|
|
|
|
|
|
|
fragment_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
|
|
|
|
|
|
|
fragment_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; |
|
|
|
|
|
|
|
fragment_shader_stage_info.module = state->fragment_shader_module; |
|
|
|
|
|
|
|
fragment_shader_stage_info.pName = "main"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo shader_stages[] = {vertex_shader_stage_info, fragment_shader_stage_info}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//now we setup the fixed function parts of the pipeline
|
|
|
|
|
|
|
|
//despite what is said in the notes, *some* state can actually be changed dynamically
|
|
|
|
|
|
|
|
//it is aptly named dynamic state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkDynamicState dynamic_states[] = {
|
|
|
|
|
|
|
|
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 = ARRSIZE(dynamic_states); |
|
|
|
|
|
|
|
dynamic_state.pDynamicStates = dynamic_states; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//vertex input format
|
|
|
|
|
|
|
|
//bindings: spacing between data, what data is per-vertex and what data is per instance
|
|
|
|
|
|
|
|
//attribute types, which bindings to load them from and at which offset
|
|
|
|
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertex_input_info = {0}; |
|
|
|
|
|
|
|
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
vertex_input_info.vertexBindingDescriptionCount = 0; |
|
|
|
|
|
|
|
vertex_input_info.pVertexBindingDescriptions = NULL; |
|
|
|
|
|
|
|
vertex_input_info.vertexAttributeDescriptionCount = 0; |
|
|
|
|
|
|
|
vertex_input_info.pVertexAttributeDescriptions = NULL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//input assembly
|
|
|
|
|
|
|
|
//type of geometry and primitive restart, which allows one to break lines and triangles
|
|
|
|
|
|
|
|
//in strip topologies
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//viewports and scissors
|
|
|
|
|
|
|
|
VkViewport viewport = {0}; |
|
|
|
|
|
|
|
viewport.x = 0.0f; |
|
|
|
|
|
|
|
viewport.y = 0.0f; |
|
|
|
|
|
|
|
viewport.width = (float)state->swapchain_extent.width; |
|
|
|
|
|
|
|
viewport.height = (float)state->swapchain_extent.height; |
|
|
|
|
|
|
|
viewport.minDepth = 0.0f; |
|
|
|
|
|
|
|
viewport.maxDepth = 1.0f; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//we want to draw all of the framebuffer
|
|
|
|
|
|
|
|
VkRect2D scissor = {0}; |
|
|
|
|
|
|
|
scissor.offset.x = 0; |
|
|
|
|
|
|
|
scissor.offset.y = 0; |
|
|
|
|
|
|
|
scissor.extent = state->swapchain_extent; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//we're using dynamic viewports and scissors
|
|
|
|
|
|
|
|
VkPipelineViewportStateCreateInfo viewport_state = {0}; |
|
|
|
|
|
|
|
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
viewport_state.viewportCount = 1; |
|
|
|
|
|
|
|
viewport_state.scissorCount = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//rasterizer setup
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//enable multisampling antialiasing
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//configure color blending
|
|
|
|
|
|
|
|
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_blending = {0}; |
|
|
|
|
|
|
|
color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
color_blending.logicOpEnable = VK_FALSE; |
|
|
|
|
|
|
|
color_blending.logicOp = VK_LOGIC_OP_COPY; // Optional
|
|
|
|
|
|
|
|
color_blending.attachmentCount = 1; |
|
|
|
|
|
|
|
color_blending.pAttachments = &color_blend_attachment; |
|
|
|
|
|
|
|
color_blending.blendConstants[0] = 0.0f; // Optional
|
|
|
|
|
|
|
|
color_blending.blendConstants[1] = 0.0f; // Optional
|
|
|
|
|
|
|
|
color_blending.blendConstants[2] = 0.0f; // Optional
|
|
|
|
|
|
|
|
color_blending.blendConstants[3] = 0.0f; // Optional
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//used for things such as uniforms
|
|
|
|
|
|
|
|
VkPipelineLayoutCreateInfo pipeline_layout_create_info = {0}; |
|
|
|
|
|
|
|
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
|
|
|
|
|
|
|
pipeline_layout_create_info.setLayoutCount = 0; |
|
|
|
|
|
|
|
pipeline_layout_create_info.pSetLayouts = NULL; |
|
|
|
|
|
|
|
pipeline_layout_create_info.pushConstantRangeCount = 0; |
|
|
|
|
|
|
|
pipeline_layout_create_info.pPushConstantRanges = NULL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (vkCreatePipelineLayout(state->device, &pipeline_layout_create_info, NULL, &state->pipeline_layout) != VK_SUCCESS) { |
|
|
|
|
|
|
|
printf("failed to create pipeline layout"); |
|
|
|
|
|
|
|
exit(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
printf("created pipeline layout\n"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkGraphicsPipelineCreateInfo pipeline_info = {0}; |
|
|
|
|
|
|
|
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; |
|
|
|
|
|
|
|
//shaders
|
|
|
|
|
|
|
|
pipeline_info.stageCount = 2; |
|
|
|
|
|
|
|
pipeline_info.pStages = shader_stages; |
|
|
|
|
|
|
|
//fixed function parts
|
|
|
|
|
|
|
|
pipeline_info.pVertexInputState = &vertex_input_info; |
|
|
|
|
|
|
|
pipeline_info.pInputAssemblyState = &input_assembly; |
|
|
|
|
|
|
|
pipeline_info.pViewportState = &viewport_state; |
|
|
|
|
|
|
|
pipeline_info.pRasterizationState = &rasterizer; |
|
|
|
|
|
|
|
pipeline_info.pMultisampleState = &multisampling; |
|
|
|
|
|
|
|
pipeline_info.pDepthStencilState = NULL; // Optional
|
|
|
|
|
|
|
|
pipeline_info.pColorBlendState = &color_blending; |
|
|
|
|
|
|
|
pipeline_info.pDynamicState = &dynamic_state; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pipeline_info.layout = state->pipeline_layout; |
|
|
|
|
|
|
|
pipeline_info.renderPass = state->render_pass; |
|
|
|
|
|
|
|
pipeline_info.subpass = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//for basing pipelines on others ones
|
|
|
|
|
|
|
|
pipeline_info.basePipelineHandle = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
pipeline_info.basePipelineIndex = -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (vkCreateGraphicsPipelines(state->device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &state->graphics_pipeline) != VK_SUCCESS) { |
|
|
|
|
|
|
|
printf("failed to create graphics pipeline\n"); |
|
|
|
|
|
|
|
exit(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
printf("created graphics pipeline\n"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void init_vulkan(ApplicationState* state) { |
|
|
|
void init_vulkan(ApplicationState* state) { |
|
|
|
create_instance(state); |
|
|
|
create_instance(state); |
|
|
|
create_surface(state); |
|
|
|
create_surface(state); |
|
|
@ -522,6 +790,8 @@ void init_vulkan(ApplicationState* state) { |
|
|
|
create_logical_device(state); |
|
|
|
create_logical_device(state); |
|
|
|
create_swap_chain(state); |
|
|
|
create_swap_chain(state); |
|
|
|
create_image_views(state); |
|
|
|
create_image_views(state); |
|
|
|
|
|
|
|
create_render_pass(state); |
|
|
|
|
|
|
|
create_graphics_pipeline(state); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void main_loop(ApplicationState* state) { |
|
|
|
void main_loop(ApplicationState* state) { |
|
|
@ -531,6 +801,8 @@ void main_loop(ApplicationState* state) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void terminate(ApplicationState* state) { |
|
|
|
void terminate(ApplicationState* state) { |
|
|
|
|
|
|
|
vkDestroyRenderPass(state->device, state->render_pass, NULL); |
|
|
|
|
|
|
|
vkDestroyPipelineLayout(state->device, state->pipeline_layout, NULL); |
|
|
|
for (int i = 0; i < state->swapchain_image_count; i++) { |
|
|
|
for (int i = 0; i < state->swapchain_image_count; i++) { |
|
|
|
vkDestroyImageView(state->device, state->swapchain_image_views[i], NULL); |
|
|
|
vkDestroyImageView(state->device, state->swapchain_image_views[i], NULL); |
|
|
|
} |
|
|
|
} |
|
|
|