|
|
|
@ -35,6 +35,11 @@ typedef struct SwapchainDetailsStruct {
|
|
|
|
|
uint32_t present_modes_count;
|
|
|
|
|
} SwapchainDetails;
|
|
|
|
|
|
|
|
|
|
typedef struct SwapchainImagesStruct {
|
|
|
|
|
VkImage* images;
|
|
|
|
|
uint32_t count;
|
|
|
|
|
} SwapchainImages;
|
|
|
|
|
|
|
|
|
|
typedef struct VulkanContextStruct {
|
|
|
|
|
VkInstance instance;
|
|
|
|
|
VkDebugUtilsMessengerEXT debug_messenger;
|
|
|
|
@ -48,6 +53,27 @@ typedef struct VulkanContextStruct {
|
|
|
|
|
|
|
|
|
|
SwapchainDetails swapchain_details;
|
|
|
|
|
VkSwapchainKHR swapchain;
|
|
|
|
|
|
|
|
|
|
VkSurfaceFormatKHR swapchain_format;
|
|
|
|
|
VkPresentModeKHR swapchain_present_mode;
|
|
|
|
|
VkExtent2D swapchain_extent;
|
|
|
|
|
|
|
|
|
|
uint32_t swapchain_image_count;
|
|
|
|
|
VkImage* swapchain_images;
|
|
|
|
|
VkImageView* swapchain_image_views;
|
|
|
|
|
VkFramebuffer* swapchain_framebuffers;
|
|
|
|
|
|
|
|
|
|
VkRenderPass render_pass;
|
|
|
|
|
|
|
|
|
|
VkCommandPool graphics_command_pool;
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer triangle_command_buffer;
|
|
|
|
|
VkPipelineLayout triangle_pipeline_layout;
|
|
|
|
|
VkPipeline triangle_pipeline;
|
|
|
|
|
|
|
|
|
|
VkSemaphore image_available_semaphore;
|
|
|
|
|
VkSemaphore render_finished_semaphore;
|
|
|
|
|
VkFence in_flight_fence;
|
|
|
|
|
} VulkanContext;
|
|
|
|
|
|
|
|
|
|
const char * validation_layers[] = {
|
|
|
|
@ -433,13 +459,9 @@ VkExtent2D choose_swapchain_extent(SwapchainDetails swapchain_details) {
|
|
|
|
|
return swapchain_details.capabilities.currentExtent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_details, VkSurfaceKHR surface, QueueIndices indices, VkSwapchainKHR old_swapchain) {
|
|
|
|
|
VkSurfaceFormatKHR format = choose_swapchain_format(swapchain_details);
|
|
|
|
|
VkPresentModeKHR present_mode = choose_present_mode(swapchain_details);
|
|
|
|
|
VkExtent2D extent = choose_swapchain_extent(swapchain_details);
|
|
|
|
|
|
|
|
|
|
uint32_t image_count = swapchain_details.capabilities.minImageCount + 1;
|
|
|
|
|
uint32_t max_images = swapchain_details.capabilities.maxImageCount;
|
|
|
|
|
VkSwapchainKHR create_swapchain(VkDevice device, VkSurfaceFormatKHR format, VkPresentModeKHR present_mode, VkExtent2D extent, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR capabilities, QueueIndices indices, VkSwapchainKHR old_swapchain) {
|
|
|
|
|
uint32_t image_count = capabilities.minImageCount + 1;
|
|
|
|
|
uint32_t max_images = capabilities.maxImageCount;
|
|
|
|
|
if((max_images > 0) && (image_count > max_images)) {
|
|
|
|
|
image_count = max_images;
|
|
|
|
|
}
|
|
|
|
@ -465,7 +487,7 @@ VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_deta
|
|
|
|
|
swapchain_info.pQueueFamilyIndices = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
swapchain_info.preTransform = swapchain_details.capabilities.currentTransform;
|
|
|
|
|
swapchain_info.preTransform = capabilities.currentTransform;
|
|
|
|
|
swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
|
|
|
|
swapchain_info.presentMode = present_mode;
|
|
|
|
|
swapchain_info.clipped = VK_TRUE;
|
|
|
|
@ -482,6 +504,430 @@ VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_deta
|
|
|
|
|
return swapchain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SwapchainImages get_swapchain_images(VkDevice device, VkSwapchainKHR swapchain) {
|
|
|
|
|
SwapchainImages images;
|
|
|
|
|
images.images = 0;
|
|
|
|
|
images.count = 0;
|
|
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, 0);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
images.count = 0;
|
|
|
|
|
return images;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
images.images = malloc(sizeof(VkImage)*images.count);
|
|
|
|
|
if(images.images == 0) {
|
|
|
|
|
images.count = 0;
|
|
|
|
|
return images;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, images.images);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
images.count = 0;
|
|
|
|
|
return images;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return images;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkImageView* create_image_views(VkDevice device, uint32_t image_count, VkImage* images, VkSurfaceFormatKHR format) {
|
|
|
|
|
VkImageView* image_views = malloc(sizeof(VkImageView)*image_count);
|
|
|
|
|
if(image_views == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < image_count; i++) {
|
|
|
|
|
VkImageViewCreateInfo view_info = {};
|
|
|
|
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
|
|
|
view_info.image = images[i];
|
|
|
|
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
|
view_info.format = format.format;
|
|
|
|
|
|
|
|
|
|
view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
|
view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
|
view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
|
view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
|
|
|
|
|
|
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
view_info.subresourceRange.baseMipLevel = 0;
|
|
|
|
|
view_info.subresourceRange.levelCount = 1;
|
|
|
|
|
view_info.subresourceRange.baseArrayLayer = 0;
|
|
|
|
|
view_info.subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
VkResult result = vkCreateImageView(device, &view_info, 0, &image_views[i]);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
free(image_views);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return image_views;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkFramebuffer* create_swapchain_framebuffers(VkDevice device, uint32_t image_count, VkImageView* image_views, VkRenderPass render_pass, VkExtent2D extent) {
|
|
|
|
|
VkFramebuffer* framebuffers = malloc(sizeof(VkFramebuffer)*image_count);
|
|
|
|
|
if(framebuffers == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < image_count; i++) {
|
|
|
|
|
VkImageView attachments[] = {
|
|
|
|
|
image_views[i],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
VkFramebufferCreateInfo framebuffer_info = {};
|
|
|
|
|
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
|
|
|
framebuffer_info.renderPass = render_pass;
|
|
|
|
|
framebuffer_info.attachmentCount = 1;
|
|
|
|
|
framebuffer_info.pAttachments = attachments;
|
|
|
|
|
framebuffer_info.width = extent.width;
|
|
|
|
|
framebuffer_info.height = extent.height;
|
|
|
|
|
framebuffer_info.layers = 1;
|
|
|
|
|
|
|
|
|
|
VkResult result = vkCreateFramebuffer(device, &framebuffer_info, 0, &framebuffers[i]);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
free(framebuffers);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return framebuffers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkShaderModule create_shader_module(VkDevice device, const char * code, uint32_t code_size) {
|
|
|
|
|
VkShaderModuleCreateInfo shader_info = {};
|
|
|
|
|
shader_info.sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
|
|
|
|
|
shader_info.codeSize = code_size;
|
|
|
|
|
shader_info.pCode = (uint32_t*)code;
|
|
|
|
|
|
|
|
|
|
VkShaderModule shader;
|
|
|
|
|
VkResult result;
|
|
|
|
|
result = vkCreateShaderModule(device, &shader_info, 0, &shader);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return shader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkShaderModule load_shader_file(uint32_t buffer_size, const char* path, VkDevice device) {
|
|
|
|
|
FILE* file;
|
|
|
|
|
file = fopen(path, "r");
|
|
|
|
|
if(file == 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);
|
|
|
|
|
|
|
|
|
|
VkShaderModule shader = create_shader_module(device, buffer, read);
|
|
|
|
|
free(buffer);
|
|
|
|
|
return shader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkRenderPass create_render_pass(VkDevice device, VkSurfaceFormatKHR format) {
|
|
|
|
|
VkAttachmentDescription color_attachment;
|
|
|
|
|
color_attachment.format = format.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_CLEAR;
|
|
|
|
|
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
|
|
|
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
|
|
|
|
|
|
|
|
VkAttachmentReference color_attachment_ref = {};
|
|
|
|
|
color_attachment_ref.attachment = 0;
|
|
|
|
|
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
|
|
|
|
|
|
VkSubpassDescription subpass = {};
|
|
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
|
|
|
subpass.colorAttachmentCount = 1;
|
|
|
|
|
subpass.pColorAttachments = &color_attachment_ref;
|
|
|
|
|
|
|
|
|
|
VkSubpassDependency dependency = {};
|
|
|
|
|
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
|
|
|
|
dependency.dstSubpass = 0;
|
|
|
|
|
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
|
dependency.srcAccessMask = 0;
|
|
|
|
|
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
|
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
|
|
|
|
|
|
VkRenderPassCreateInfo render_info = {};
|
|
|
|
|
render_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
|
|
|
render_info.attachmentCount = 1;
|
|
|
|
|
render_info.pAttachments = &color_attachment;
|
|
|
|
|
render_info.subpassCount = 1;
|
|
|
|
|
render_info.pSubpasses = &subpass;
|
|
|
|
|
render_info.dependencyCount = 1;
|
|
|
|
|
render_info.pDependencies = &dependency;
|
|
|
|
|
|
|
|
|
|
VkRenderPass render_pass;
|
|
|
|
|
VkResult result = vkCreateRenderPass(device, &render_info, 0, &render_pass);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return render_pass;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkPipelineLayout create_pipeline_layout(VkDevice device, uint32_t set_count, VkDescriptorSetLayout* sets, uint32_t pcr_count, VkPushConstantRange* pcrs) {
|
|
|
|
|
VkPipelineLayout layout;
|
|
|
|
|
VkPipelineLayoutCreateInfo layout_info = {};
|
|
|
|
|
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
|
|
|
layout_info.setLayoutCount = set_count;
|
|
|
|
|
layout_info.pSetLayouts = sets;
|
|
|
|
|
layout_info.pushConstantRangeCount = pcr_count;
|
|
|
|
|
layout_info.pPushConstantRanges = pcrs;
|
|
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
result = vkCreatePipelineLayout(device, &layout_info, 0, &layout);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return layout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkPipeline create_graphics_pipeline(VkDevice device, VkExtent2D extent, VkPipelineLayout layout, VkRenderPass render_pass) {
|
|
|
|
|
VkShaderModule vert_shader = load_shader_file(2048, "vert.spv", device);
|
|
|
|
|
fprintf(stderr, "Loaded vert_shader: %p\n", vert_shader);
|
|
|
|
|
VkShaderModule frag_shader = load_shader_file(2048, "frag.spv", device);
|
|
|
|
|
fprintf(stderr, "Loaded frag_shader: %p\n", frag_shader);
|
|
|
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo shader_stages[2] = {};
|
|
|
|
|
shader_stages[0].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
|
|
|
|
|
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
|
shader_stages[0].module = vert_shader;
|
|
|
|
|
shader_stages[0].pName = "main";
|
|
|
|
|
|
|
|
|
|
shader_stages[1].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
|
|
|
|
|
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
|
|
|
shader_stages[1].module = frag_shader;
|
|
|
|
|
shader_stages[1].pName = "main";
|
|
|
|
|
|
|
|
|
|
VkDynamicState dynamic_states[] = {
|
|
|
|
|
VK_DYNAMIC_STATE_VIEWPORT,
|
|
|
|
|
VK_DYNAMIC_STATE_SCISSOR,
|
|
|
|
|
};
|
|
|
|
|
uint32_t dynamic_state_count = sizeof(dynamic_states)/sizeof(VkDynamicState);
|
|
|
|
|
|
|
|
|
|
VkPipelineDynamicStateCreateInfo dynamic_info = {};
|
|
|
|
|
dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
|
|
|
|
dynamic_info.dynamicStateCount = dynamic_state_count;
|
|
|
|
|
dynamic_info.pDynamicStates = dynamic_states;
|
|
|
|
|
|
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
|
|
|
|
|
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
|
|
|
|
vertex_input_info.vertexBindingDescriptionCount = 0;
|
|
|
|
|
vertex_input_info.pVertexBindingDescriptions = 0;
|
|
|
|
|
vertex_input_info.vertexAttributeDescriptionCount = 0;
|
|
|
|
|
vertex_input_info.pVertexAttributeDescriptions = 0;
|
|
|
|
|
|
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo input_assemvly_info = {};
|
|
|
|
|
input_assemvly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
|
|
|
|
input_assemvly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
|
|
|
input_assemvly_info.primitiveRestartEnable = VK_FALSE;
|
|
|
|
|
|
|
|
|
|
VkViewport viewport = {};
|
|
|
|
|
viewport.x = 0.0f;
|
|
|
|
|
viewport.y = 0.0f;
|
|
|
|
|
viewport.width = (float)(extent.width);
|
|
|
|
|
viewport.height = (float)(extent.height);
|
|
|
|
|
viewport.minDepth = 0.0f;
|
|
|
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
|
|
|
|
|
|
VkRect2D scissor = {};
|
|
|
|
|
VkOffset2D scissor_offset = {.x = 0, .y = 0};
|
|
|
|
|
scissor.offset = scissor_offset;
|
|
|
|
|
scissor.extent = extent;
|
|
|
|
|
|
|
|
|
|
VkPipelineViewportStateCreateInfo viewport_state = {};
|
|
|
|
|
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
|
|
|
|
viewport_state.viewportCount = 1;
|
|
|
|
|
viewport_state.pViewports = &viewport;
|
|
|
|
|
viewport_state.scissorCount = 1;
|
|
|
|
|
viewport_state.pScissors = &scissor;
|
|
|
|
|
|
|
|
|
|
VkPipelineRasterizationStateCreateInfo raster_info = {};
|
|
|
|
|
raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
|
|
|
|
raster_info.depthClampEnable = VK_FALSE;
|
|
|
|
|
raster_info.rasterizerDiscardEnable = VK_FALSE;
|
|
|
|
|
raster_info.polygonMode = VK_POLYGON_MODE_FILL;
|
|
|
|
|
raster_info.lineWidth = 1.0f;
|
|
|
|
|
raster_info.cullMode = VK_CULL_MODE_BACK_BIT;
|
|
|
|
|
raster_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
|
|
|
|
raster_info.depthBiasEnable = VK_FALSE;
|
|
|
|
|
raster_info.depthBiasConstantFactor = 0.0f;
|
|
|
|
|
raster_info.depthBiasClamp = 0.0f;
|
|
|
|
|
raster_info.depthBiasSlopeFactor = 0.0f;
|
|
|
|
|
|
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisample_info = {};
|
|
|
|
|
multisample_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
|
|
|
multisample_info.sampleShadingEnable = VK_FALSE;
|
|
|
|
|
multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
|
multisample_info.minSampleShading = 1.0f;
|
|
|
|
|
multisample_info.pSampleMask = 0;
|
|
|
|
|
multisample_info.alphaToCoverageEnable = VK_FALSE;
|
|
|
|
|
multisample_info.alphaToOneEnable = VK_FALSE;
|
|
|
|
|
|
|
|
|
|
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
|
|
|
|
|
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_TRUE;
|
|
|
|
|
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
|
|
|
|
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
|
|
|
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
|
|
|
|
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
|
|
|
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
|
|
|
|
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
|
|
|
|
|
|
|
|
|
VkPipelineColorBlendStateCreateInfo color_blend_info = {};
|
|
|
|
|
color_blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
|
|
|
|
color_blend_info.logicOpEnable = VK_FALSE;
|
|
|
|
|
color_blend_info.logicOp = VK_LOGIC_OP_COPY;
|
|
|
|
|
color_blend_info.attachmentCount = 1;
|
|
|
|
|
color_blend_info.pAttachments = &color_blend_attachment;
|
|
|
|
|
color_blend_info.blendConstants[0] = 0.0f;
|
|
|
|
|
color_blend_info.blendConstants[1] = 0.0f;
|
|
|
|
|
color_blend_info.blendConstants[2] = 0.0f;
|
|
|
|
|
color_blend_info.blendConstants[3] = 0.0f;
|
|
|
|
|
|
|
|
|
|
VkGraphicsPipelineCreateInfo pipeline_info = {};
|
|
|
|
|
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
|
|
|
|
pipeline_info.stageCount = 2;
|
|
|
|
|
pipeline_info.pStages = shader_stages;
|
|
|
|
|
pipeline_info.pVertexInputState = &vertex_input_info;
|
|
|
|
|
pipeline_info.pInputAssemblyState = &input_assemvly_info;
|
|
|
|
|
pipeline_info.pViewportState = &viewport_state;
|
|
|
|
|
pipeline_info.pRasterizationState = &raster_info;
|
|
|
|
|
pipeline_info.pDepthStencilState = 0;
|
|
|
|
|
pipeline_info.pColorBlendState = &color_blend_info;
|
|
|
|
|
pipeline_info.pDynamicState = &dynamic_info;
|
|
|
|
|
pipeline_info.layout = layout;
|
|
|
|
|
pipeline_info.renderPass = render_pass;
|
|
|
|
|
pipeline_info.subpass = 0;
|
|
|
|
|
|
|
|
|
|
pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
|
|
|
|
|
pipeline_info.basePipelineIndex = -1;
|
|
|
|
|
|
|
|
|
|
VkPipeline pipeline;
|
|
|
|
|
VkResult result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_info, 0, &pipeline);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pipeline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkCommandPool create_command_pool(VkDevice device, uint32_t queue_family) {
|
|
|
|
|
VkCommandPoolCreateInfo pool_info = {};
|
|
|
|
|
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
|
|
|
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
|
|
|
pool_info.queueFamilyIndex = queue_family;
|
|
|
|
|
|
|
|
|
|
VkCommandPool command_pool;
|
|
|
|
|
VkResult result = vkCreateCommandPool(device, &pool_info, 0, &command_pool);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return command_pool;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkResult record_command_buffer_triangle(VkCommandBuffer command_buffer, uint32_t image_index, VkRenderPass render_pass, VkFramebuffer* framebuffers, VkExtent2D extent, VkPipeline graphics_pipeline) {
|
|
|
|
|
VkCommandBufferBeginInfo begin_info = {};
|
|
|
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
|
begin_info.flags = 0;
|
|
|
|
|
begin_info.pInheritanceInfo = 0;
|
|
|
|
|
|
|
|
|
|
VkResult result = vkBeginCommandBuffer(command_buffer, &begin_info);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkRenderPassBeginInfo render_pass_info = {};
|
|
|
|
|
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
|
|
|
render_pass_info.renderPass = render_pass;
|
|
|
|
|
render_pass_info.framebuffer = framebuffers[image_index];
|
|
|
|
|
VkOffset2D render_offset = {.x = 0, .y = 0};
|
|
|
|
|
render_pass_info.renderArea.offset = render_offset;
|
|
|
|
|
render_pass_info.renderArea.extent = extent;
|
|
|
|
|
VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
|
|
|
|
render_pass_info.clearValueCount = 1;
|
|
|
|
|
render_pass_info.pClearValues = &clear_color;
|
|
|
|
|
|
|
|
|
|
vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
|
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
|
|
|
|
|
|
|
|
|
VkViewport viewport = {};
|
|
|
|
|
viewport.x = 0.0f;
|
|
|
|
|
viewport.y = 0.0f;
|
|
|
|
|
viewport.width = (float)(extent.width);
|
|
|
|
|
viewport.height = (float)(extent.height);
|
|
|
|
|
viewport.minDepth = 0.0f;
|
|
|
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
|
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
|
|
|
|
|
|
|
|
|
|
VkRect2D scissor = {};
|
|
|
|
|
VkOffset2D scissor_offset = {.x = 0.0f, .y = 0.0f};
|
|
|
|
|
scissor.offset = scissor_offset;
|
|
|
|
|
scissor.extent = extent;
|
|
|
|
|
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
|
|
|
|
|
|
|
|
|
vkCmdDraw(command_buffer, 3, 1, 0, 0);
|
|
|
|
|
vkCmdEndRenderPass(command_buffer);
|
|
|
|
|
|
|
|
|
|
return vkEndCommandBuffer(command_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer create_command_buffer(VkDevice device, VkCommandPool command_pool) {
|
|
|
|
|
VkCommandBufferAllocateInfo alloc_info = {};
|
|
|
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
|
|
|
alloc_info.commandPool = command_pool;
|
|
|
|
|
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
|
alloc_info.commandBufferCount = 1;
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer command_buffer;
|
|
|
|
|
VkResult result = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return command_buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkSemaphore create_semaphore(VkDevice device, VkSemaphoreCreateFlags flags) {
|
|
|
|
|
VkSemaphoreCreateInfo semaphore_info = {};
|
|
|
|
|
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
|
|
|
semaphore_info.flags = flags;
|
|
|
|
|
|
|
|
|
|
VkSemaphore semaphore;
|
|
|
|
|
VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphore);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return semaphore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkFence create_fence(VkDevice device, VkFenceCreateFlags flags) {
|
|
|
|
|
VkFenceCreateInfo fence_info = {};
|
|
|
|
|
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
|
|
|
fence_info.flags = flags;
|
|
|
|
|
|
|
|
|
|
VkFence fence;
|
|
|
|
|
VkResult result = vkCreateFence(device, &fence_info, 0, &fence);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
return fence;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VulkanContext* init_vulkan(GLFWwindow* window) {
|
|
|
|
|
VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext));
|
|
|
|
|
|
|
|
|
@ -543,19 +989,170 @@ VulkanContext* init_vulkan(GLFWwindow* window) {
|
|
|
|
|
context->swapchain_details = maybe_details.details;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_details, context->surface, context->queue_indices, VK_NULL_HANDLE);
|
|
|
|
|
context->swapchain_format = choose_swapchain_format(context->swapchain_details);
|
|
|
|
|
context->swapchain_present_mode = choose_present_mode(context->swapchain_details);
|
|
|
|
|
context->swapchain_extent = choose_swapchain_extent(context->swapchain_details);
|
|
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_format, context->swapchain_present_mode, context->swapchain_extent, context->surface, context->swapchain_details.capabilities, context->queue_indices, VK_NULL_HANDLE);
|
|
|
|
|
if(swapchain == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan swapchain\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->swapchain = swapchain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SwapchainImages swapchain_images = get_swapchain_images(context->device, context->swapchain);
|
|
|
|
|
if(swapchain_images.count == 0) {
|
|
|
|
|
fprintf(stderr, "failed to get vulkan swapchain images\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->swapchain_image_count = swapchain_images.count;
|
|
|
|
|
context->swapchain_images = swapchain_images.images;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkImageView* image_views = create_image_views(context->device, context->swapchain_image_count, context->swapchain_images, context->swapchain_format);
|
|
|
|
|
if(image_views == 0) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan image views\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->swapchain_image_views = image_views;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkRenderPass render_pass = create_render_pass(context->device, context->swapchain_format);
|
|
|
|
|
if(render_pass == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan render pass\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->render_pass = render_pass;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkFramebuffer* framebuffers = create_swapchain_framebuffers(context->device, context->swapchain_image_count, context->swapchain_image_views, context->render_pass, context->swapchain_extent);
|
|
|
|
|
if(framebuffers == 0) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan framebuffers\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->swapchain_framebuffers = framebuffers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkSemaphore ia_semaphore = create_semaphore(context->device, 0);
|
|
|
|
|
if(ia_semaphore == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan image available semaphore\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->image_available_semaphore = ia_semaphore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkSemaphore rf_semaphore = create_semaphore(context->device, 0);
|
|
|
|
|
if(rf_semaphore == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan render finished semaphore\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->render_finished_semaphore = rf_semaphore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkFence if_fence = create_fence(context->device, VK_FENCE_CREATE_SIGNALED_BIT);
|
|
|
|
|
if(if_fence == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan in flight fence\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->in_flight_fence = if_fence;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkCommandPool command_pool = create_command_pool(context->device, context->queue_indices.graphics_family);
|
|
|
|
|
if(command_pool == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan graphics command pool");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->graphics_command_pool = command_pool;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkPipelineLayout triangle_pipeline_layout = create_pipeline_layout(device, 0, 0, 0, 0);
|
|
|
|
|
if(triangle_pipeline_layout == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan pipeline layout\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->triangle_pipeline_layout = triangle_pipeline_layout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkPipeline triangle_pipeline = create_graphics_pipeline(context->device, context->swapchain_extent, context->triangle_pipeline_layout, context->render_pass);
|
|
|
|
|
if(triangle_pipeline == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan graphics pipeline\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->triangle_pipeline = triangle_pipeline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer triangle_command_buffer = create_command_buffer(context->device, context->graphics_command_pool);
|
|
|
|
|
if(triangle_command_buffer == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan triangle command buffer\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
context->triangle_command_buffer = triangle_command_buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void main_loop(GLFWwindow* window) {
|
|
|
|
|
VkResult draw_frame(VulkanContext* context) {
|
|
|
|
|
VkResult result;
|
|
|
|
|
result = vkWaitForFences(context->device, 1, &context->in_flight_fence, VK_TRUE, UINT64_MAX);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = vkResetFences(context->device, 1, &context->in_flight_fence);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t image_index;
|
|
|
|
|
result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphore, VK_NULL_HANDLE, &image_index);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = vkResetCommandBuffer(context->triangle_command_buffer, 0);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = record_command_buffer_triangle(context->triangle_command_buffer, image_index, context->render_pass, context->swapchain_framebuffers, context->swapchain_extent, context->triangle_pipeline);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
|
|
|
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
|
|
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
|
submit_info.waitSemaphoreCount = 1;
|
|
|
|
|
submit_info.pWaitSemaphores = &context->image_available_semaphore;
|
|
|
|
|
submit_info.pWaitDstStageMask = wait_stages;
|
|
|
|
|
submit_info.commandBufferCount = 1;
|
|
|
|
|
submit_info.pCommandBuffers = &context->triangle_command_buffer;
|
|
|
|
|
submit_info.signalSemaphoreCount = 1;
|
|
|
|
|
submit_info.pSignalSemaphores = &context->render_finished_semaphore;
|
|
|
|
|
|
|
|
|
|
result = vkQueueSubmit(context->queues.graphics, 1, &submit_info, context->in_flight_fence);
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkPresentInfoKHR present_info = {};
|
|
|
|
|
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
|
|
|
|
present_info.waitSemaphoreCount = 1;
|
|
|
|
|
present_info.pWaitSemaphores = &context->render_finished_semaphore;
|
|
|
|
|
present_info.swapchainCount = 1;
|
|
|
|
|
present_info.pSwapchains = &context->swapchain;
|
|
|
|
|
present_info.pImageIndices = &image_index;
|
|
|
|
|
present_info.pResults = 0;
|
|
|
|
|
|
|
|
|
|
return vkQueuePresentKHR(context->queues.present, &present_info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void main_loop(GLFWwindow* window, VulkanContext* context) {
|
|
|
|
|
while(!glfwWindowShouldClose(window)) {
|
|
|
|
|
glfwPollEvents();
|
|
|
|
|
draw_frame(context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -603,7 +1200,7 @@ int main() {
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main_loop(window);
|
|
|
|
|
main_loop(window, context);
|
|
|
|
|
|
|
|
|
|
cleanup(window, context);
|
|
|
|
|
|
|
|
|
|