Browse Source

Create graphics pipeline.

master
Amber 2 years ago
parent
commit
a78281e8bc
  1. 15
      meson.build
  2. 9
      src/fragment_simple.frag
  3. 272
      src/main.c
  4. 20
      src/vertex_simple.vert

15
meson.build

@ -2,4 +2,19 @@ project('vulkan learning', 'c') @@ -2,4 +2,19 @@ project('vulkan learning', 'c')
deps = [dependency('glfw3'), dependency('vulkan'), dependency('dl'), dependency('threads'), dependency('X11'), dependency('xxf86vm'), dependency('xrandr'), dependency('xi'), dependency('cglm')]
glslc = find_program('glslc')
shaders = ['src/vertex_simple.vert', 'src/fragment_simple.frag']
foreach shader : shaders
out = (shader.split('.')[-2] + '.spv').split('/')[-1]
target = custom_target(out,
output : out,
input : shader,
command : [glslc, '@INPUT@', '-o', '@OUTPUT@'],
install : true,
install_dir : 'shaders')
endforeach
executable('triangle', 'src/main.c', dependencies: deps)

9
src/fragment_simple.frag

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}

272
src/main.c

@ -53,6 +53,13 @@ typedef struct ApplicationState { @@ -53,6 +53,13 @@ typedef struct ApplicationState {
VkExtent2D swapchain_extent;
//describes how to access an image and allows access to it
VkImageView* swapchain_image_views;
//shaders
VkShaderModule vertex_shader_module;
VkShaderModule fragment_shader_module;
VkRenderPass render_pass;
VkPipelineLayout pipeline_layout;
VkPipeline graphics_pipeline;
} ApplicationState;
typedef struct QueueFamilyIndices {
@ -515,6 +522,267 @@ void create_image_views(ApplicationState* state) { @@ -515,6 +522,267 @@ void create_image_views(ApplicationState* state) {
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) {
create_instance(state);
create_surface(state);
@ -522,6 +790,8 @@ void init_vulkan(ApplicationState* state) { @@ -522,6 +790,8 @@ void init_vulkan(ApplicationState* state) {
create_logical_device(state);
create_swap_chain(state);
create_image_views(state);
create_render_pass(state);
create_graphics_pipeline(state);
}
void main_loop(ApplicationState* state) {
@ -531,6 +801,8 @@ void main_loop(ApplicationState* state) { @@ -531,6 +801,8 @@ void main_loop(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++) {
vkDestroyImageView(state->device, state->swapchain_image_views[i], NULL);
}

20
src/vertex_simple.vert

@ -0,0 +1,20 @@ @@ -0,0 +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)
);
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];
}
Loading…
Cancel
Save