feat: finished up triangle
parent
3b4541477a
commit
672962c2d7
85
src/main.c
85
src/main.c
|
@ -2,13 +2,14 @@
|
|||
#include <SDL2/SDL_vulkan.h>
|
||||
#include <stdlib.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "render_context.h"
|
||||
|
||||
#define APPNAME "VulkanApp"
|
||||
const char* appName = APPNAME;
|
||||
const VkApplicationInfo appInfo = {
|
||||
static const char *appName = APPNAME;
|
||||
static const VkApplicationInfo appInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pApplicationName = APPNAME,
|
||||
.applicationVersion = VK_MAKE_VERSION(0, 0, 1),
|
||||
|
@ -17,6 +18,79 @@ const VkApplicationInfo appInfo = {
|
|||
.apiVersion = VK_API_VERSION_1_0,
|
||||
};
|
||||
|
||||
static void RecordCommandBuffer(RenderContext *context, VkCommandBuffer buffer, uint32_t imageIndex) {
|
||||
VkCommandBufferBeginInfo bufferBegin = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
if(vkBeginCommandBuffer(context->commandBuffer, &bufferBegin) != VK_SUCCESS) {
|
||||
RETURN_ERROR(,"Failed to start command buffer");
|
||||
}
|
||||
|
||||
VkClearValue clearColor = {.color={.float32 = {0.f, 0.f, 0.f, 1.0f}}};
|
||||
VkRenderPassBeginInfo passBegin = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = context->renderPass,
|
||||
.framebuffer = *list_at_as(VkFramebuffer, &context->swapchainBuffers, imageIndex),
|
||||
.renderArea = {
|
||||
.offset = {0,0},
|
||||
.extent = context->swapchainExtent
|
||||
},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clearColor,
|
||||
};
|
||||
vkCmdBeginRenderPass(context->commandBuffer, &passBegin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
vkCmdBindPipeline(context->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, context->graphicsPipeline);
|
||||
VkViewport viewport = {
|
||||
.x = 0.f, .y = 0.f,
|
||||
.width = context->swapchainExtent.width,
|
||||
.height = context->swapchainExtent.height,
|
||||
.minDepth = 0.f,
|
||||
.maxDepth = 1.f,
|
||||
};
|
||||
vkCmdSetViewport(context->commandBuffer, 0, 1, &viewport);
|
||||
VkRect2D scissor = {
|
||||
.offset = {0,0},
|
||||
.extent = context->swapchainExtent
|
||||
};
|
||||
vkCmdSetScissor(context->commandBuffer, 0, 1, &scissor);
|
||||
vkCmdDraw(context->commandBuffer, 3, 1, 0, 0);
|
||||
vkCmdEndRenderPass(context->commandBuffer);
|
||||
if(vkEndCommandBuffer(context->commandBuffer) != VK_SUCCESS) {
|
||||
RETURN_ERROR(, "Failed to finish command recording");
|
||||
}
|
||||
}
|
||||
|
||||
static void Draw(RenderContext *context) {
|
||||
vkWaitForFences(context->device, 1, &context->inFlight, VK_TRUE, UINT64_MAX);
|
||||
vkResetFences(context->device, 1, &context->inFlight);
|
||||
vkResetCommandBuffer(context->commandBuffer, 0);
|
||||
uint32_t imageIndex = 0;
|
||||
vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->imageAvailable, VK_NULL_HANDLE, &imageIndex);
|
||||
RecordCommandBuffer(context, context->commandBuffer, imageIndex);
|
||||
VkSemaphore waits[] = { context->imageAvailable };
|
||||
VkPipelineStageFlags stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||
VkSemaphore signals[] = { context->renderFinished };
|
||||
VkSubmitInfo submit = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = waits,
|
||||
.pWaitDstStageMask = stages,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &context->commandBuffer,
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = signals,
|
||||
};
|
||||
if(vkQueueSubmit(context->graphicsQueue, 1, &submit, context->inFlight) != VK_SUCCESS) {
|
||||
RETURN_ERROR(, "Failed to submit graphics queue to GPU");
|
||||
}
|
||||
VkPresentInfoKHR present = {
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = signals,
|
||||
.swapchainCount = 1,
|
||||
.pSwapchains = &context->swapchain,
|
||||
.pImageIndices = &imageIndex
|
||||
};
|
||||
vkQueuePresentKHR(context->graphicsQueue, &present);
|
||||
}
|
||||
|
||||
void Quit(RenderContext *context) {
|
||||
RenderContextDestroy(context);
|
||||
|
@ -26,11 +100,12 @@ void Quit(RenderContext *context) {
|
|||
|
||||
void Loop(RenderContext *context) {
|
||||
SDL_Event event;
|
||||
while(1) {
|
||||
do {
|
||||
SDL_PollEvent(&event);
|
||||
if(event.type == SDL_QUIT)
|
||||
Draw(context);
|
||||
} while(event.type != SDL_QUIT);
|
||||
vkDeviceWaitIdle(context->device);
|
||||
Quit(context);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "render_context.h"
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include <stdio.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include "debug.h"
|
||||
#include "list.h"
|
||||
|
@ -15,6 +16,20 @@ static const List requiredVulkanExtensions = {
|
|||
.element_size = sizeof(char*)
|
||||
};
|
||||
|
||||
static List LoadFileBytes(const char* filename) {
|
||||
FILE* file = fopen(filename, "rb");
|
||||
ASSERT_RETURN(file != NULL, (List){0}, "Failed to load file %s", filename);
|
||||
fseek(file, 0, SEEK_END);
|
||||
long len = ftell(file)+1;
|
||||
List buffer = list_with_len(sizeof(char), len-1);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
fread(buffer.data, sizeof(char), len, file);
|
||||
ASSERT_RETURN(feof(file), buffer, "Did not read to end of file while loading data from %s", filename);
|
||||
ASSERT_RETURN(!ferror(file), buffer, "Encountered file error while loading data from %s", filename);
|
||||
fclose(file);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// get the list of supported vulkan protocol extensions
|
||||
static List RenderContextGetExtensions(const RenderContext *self) {
|
||||
// get the number of available extensions
|
||||
|
@ -62,7 +77,7 @@ static VkExtent2D SelectSwapExtent(RenderContext *self, const VkSurfaceCapabilit
|
|||
return (VkExtent2D) {
|
||||
.width = CLAMP((uint32_t)width, capabilities->minImageExtent.width, capabilities->maxImageExtent.width),
|
||||
.height = CLAMP((uint32_t)height, capabilities->minImageExtent.height, capabilities->maxImageExtent.height)
|
||||
};;
|
||||
};
|
||||
}
|
||||
|
||||
static int CheckExtensionSupport(VkPhysicalDevice device) {
|
||||
|
@ -132,7 +147,7 @@ static void RenderContextInitSdlWindow(RenderContext* self, const VkApplicationI
|
|||
1920, 1080,
|
||||
SDL_WINDOW_VULKAN | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_SHOWN);
|
||||
if(!self->window) {
|
||||
LOG_ERROR("Failed to create window: %s", SDL_GetError());
|
||||
RETURN_ERROR(,"Failed to create window: %s", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,14 +155,19 @@ static void RenderContextInitSdlWindow(RenderContext* self, const VkApplicationI
|
|||
// initializes self->vulkanInstance
|
||||
static void RenderContextInitVulkanInstance(RenderContext *self, const VkApplicationInfo *appinfo) {
|
||||
List extensions = RenderContextGetExtensions(self);
|
||||
const char* validationLayers[] = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
VkInstanceCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pApplicationInfo = appinfo,
|
||||
.enabledExtensionCount = extensions.len,
|
||||
.ppEnabledExtensionNames = extensions.data,
|
||||
.enabledLayerCount = 1,
|
||||
.ppEnabledLayerNames = validationLayers
|
||||
};
|
||||
if(vkCreateInstance(&createInfo, NULL, &self->vulkanInstance) != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to initialize vulkan instance");
|
||||
RETURN_ERROR(,"Failed to initialize vulkan instance");
|
||||
}
|
||||
list_empty(&extensions);
|
||||
}
|
||||
|
@ -208,14 +228,17 @@ static QueueFamilyIndexes RenderContextFindQueueFamilies(RenderContext *self) {
|
|||
static List QueueCreateInfoListFrom(QueueFamilyIndexes indexes) {
|
||||
// return empty list if indexes is not initialized
|
||||
if(!indexes.init)
|
||||
return list_from_type(VkDeviceQueueCreateInfo);
|
||||
RETURN_ERROR(list_from_type(VkDeviceQueueCreateInfo), "Failed to init queue family indeces");
|
||||
// filter duplicates from indexes
|
||||
List uniqueFamilies = list_from_type(uint32_t);
|
||||
for(size_t i = 0; i < QueueFamilyIndexes_Count; ++i) {
|
||||
if(!list_contains(&uniqueFamilies, indexes.as_array + i)) {
|
||||
int isUnique = 1;
|
||||
list_foreach(uint32_t *, family, &uniqueFamilies)
|
||||
if((*family) == indexes.as_array[i])
|
||||
isUnique = 0;
|
||||
if(isUnique)
|
||||
list_add(&uniqueFamilies, indexes.as_array + i);
|
||||
}
|
||||
}
|
||||
// compose list of create infos without priories assigned
|
||||
List createInfo_ = list_from_type_with_len(VkDeviceQueueCreateInfo, uniqueFamilies.len);
|
||||
// for simplicities sake get the direct pointers to underlying arrays
|
||||
|
@ -224,7 +247,7 @@ static List QueueCreateInfoListFrom(QueueFamilyIndexes indexes) {
|
|||
for(size_t i = 0; i < uniqueFamilies.len; ++i) {
|
||||
createInfo[i] = (VkDeviceQueueCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = families[0],
|
||||
.queueFamilyIndex = families[i],
|
||||
.queueCount = 1,
|
||||
};
|
||||
}
|
||||
|
@ -244,14 +267,14 @@ static void RenderContextCreateLogicalDevice(RenderContext *self) {
|
|||
List extensions = list_copy(&requiredVulkanExtensions);
|
||||
VkDeviceCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.queueCreateInfoCount = (uint32_t)queueCreateInfo.len,
|
||||
.pQueueCreateInfos = queueCreateInfo.data,
|
||||
.queueCreateInfoCount = queueCreateInfo.len,
|
||||
.enabledExtensionCount = (uint32_t)extensions.len,
|
||||
.ppEnabledExtensionNames = extensions.data
|
||||
};
|
||||
VkPhysicalDeviceFeatures features = {0};
|
||||
if(vkCreateDevice(self->physicalDevice, &createInfo, NULL, &self->device) != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to create vulkan logical device");
|
||||
RETURN_ERROR(,"Failed to create vulkan logical device");
|
||||
}
|
||||
list_empty(&extensions);
|
||||
list_empty(&queueCreateInfo);
|
||||
|
@ -261,7 +284,7 @@ static void RenderContextCreateLogicalDevice(RenderContext *self) {
|
|||
|
||||
static void RenderContextCreateSurface(RenderContext *self) {
|
||||
if(SDL_Vulkan_CreateSurface(self->window, self->vulkanInstance, &self->windowSurface) == SDL_FALSE) {
|
||||
LOG_ERROR("Failed to create vulkan surface");
|
||||
RETURN_ERROR(,"Failed to create vulkan surface");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -301,15 +324,277 @@ static void RenderContextCreateSwapchain(RenderContext *self) {
|
|||
}
|
||||
SwapChainSupportInfoClean(&support);
|
||||
if(vkCreateSwapchainKHR(self->device, &createInfo, NULL, &self->swapchain) != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to create vulkan swapchain");
|
||||
RETURN_ERROR(,"Failed to create vulkan swapchain");
|
||||
}
|
||||
vkGetSwapchainImagesKHR(self->device, self->swapchain, &chainLength, NULL);
|
||||
self->swapchainImages = list_with_len(sizeof(VkImage), chainLength);
|
||||
vkGetSwapchainImagesKHR(self->device, self->swapchain, &chainLength, self->swapchainImages.data);
|
||||
}
|
||||
|
||||
static void RenderContextMakeImageViews(RenderContext *self) {
|
||||
self->swapchainViews = list_from_type(VkImageView);
|
||||
list_reserve(&self->swapchainViews, self->swapchainImages.len);
|
||||
VkImageViewCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = self->swapchainFormat,
|
||||
.components = {
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
},
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
VkImageView createdView;
|
||||
list_foreach(VkImage *,image, &self->swapchainImages) {
|
||||
createInfo.image = *image;
|
||||
if(vkCreateImageView(self->device, &createInfo, NULL, &createdView) != VK_SUCCESS)
|
||||
RETURN_ERROR(,"Failed to create view for swapchain image");
|
||||
else
|
||||
list_add(&self->swapchainViews, &createdView);
|
||||
}
|
||||
}
|
||||
|
||||
static VkShaderModule RenderContextCreateShaderModule(RenderContext *self, const uint32_t* data, uint32_t size) {
|
||||
VkShaderModuleCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = size,
|
||||
.pCode = data,
|
||||
};
|
||||
VkShaderModule module;
|
||||
if(vkCreateShaderModule(self->device, &createInfo, NULL, &module) != VK_SUCCESS) {
|
||||
RETURN_ERROR(VK_NULL_HANDLE,"Failed to create shader module");
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
static VkShaderModule RenderContextShaderModuleFromFile(RenderContext *self, const char* file) {
|
||||
List data = LoadFileBytes(file);
|
||||
VkShaderModule mod = RenderContextCreateShaderModule(self, data.data, data.len);
|
||||
list_empty(&data);
|
||||
return mod;
|
||||
}
|
||||
|
||||
static void RenderContextCreateRenderPass(RenderContext *self) {
|
||||
VkAttachmentDescription attach = {
|
||||
.format = self->swapchainFormat,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
};
|
||||
VkAttachmentReference attachReference = {
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
};
|
||||
VkSubpassDescription subpass = {
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &attachReference,
|
||||
};
|
||||
VkSubpassDependency deps = {
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
};
|
||||
VkRenderPassCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &attach,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &deps,
|
||||
};
|
||||
if(vkCreateRenderPass(self->device, &createInfo, NULL, &self->renderPass) != VK_SUCCESS) {
|
||||
RETURN_ERROR(,"Failed to create vulkan render pass");
|
||||
}
|
||||
}
|
||||
|
||||
static void RenderContextCreateGraphicsPipeline(RenderContext *self) {
|
||||
self->fragmentShader = RenderContextShaderModuleFromFile(self, "shaders/frag.spv");
|
||||
self->vertexShader = RenderContextShaderModuleFromFile(self, "shaders/vert.spv");
|
||||
|
||||
VkPipelineShaderStageCreateInfo stageCreateInfos[] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = self->vertexShader,
|
||||
.pName = "main"
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.module = self->fragmentShader,
|
||||
.pName = "main"
|
||||
}
|
||||
};
|
||||
VkPipelineVertexInputStateCreateInfo vertexInput = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = 0,
|
||||
.vertexAttributeDescriptionCount = 0
|
||||
};
|
||||
VkPipelineInputAssemblyStateCreateInfo assembly = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
.primitiveRestartEnable = VK_FALSE,
|
||||
};
|
||||
VkViewport viewport = {
|
||||
.x = 0.f, .y = 0.f,
|
||||
.width = (float)self->swapchainExtent.width,
|
||||
.height = (float)self->swapchainExtent.height,
|
||||
.minDepth = 0.f,
|
||||
.maxDepth = 1.f,
|
||||
};
|
||||
VkRect2D scissorRect = {
|
||||
.offset = {0,0},
|
||||
.extent = self->swapchainExtent,
|
||||
};
|
||||
VkDynamicState dynStates[] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR
|
||||
};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynamicState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.dynamicStateCount = 2,
|
||||
.pDynamicStates = dynStates,
|
||||
};
|
||||
VkPipelineViewportStateCreateInfo viewportState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.viewportCount = 1,
|
||||
.pViewports = &viewport,
|
||||
.scissorCount = 1,
|
||||
.pScissors = &scissorRect,
|
||||
};
|
||||
VkPipelineRasterizationStateCreateInfo rasterizer = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode = VK_CULL_MODE_BACK_BIT,
|
||||
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
||||
.rasterizerDiscardEnable = VK_FALSE,
|
||||
.lineWidth = 1.0
|
||||
};
|
||||
VkPipelineMultisampleStateCreateInfo multisample = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.sampleShadingEnable = VK_FALSE,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
};
|
||||
VkPipelineColorBlendAttachmentState blendAttachment = {
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||
// .blendEnable = VK_FALSE,
|
||||
.blendEnable = VK_TRUE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_DST_ALPHA,
|
||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||
};
|
||||
VkPipelineColorBlendStateCreateInfo blendStateCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &blendAttachment,
|
||||
.blendConstants = { 0.f, 0.f, 0.f, 0.f },
|
||||
};
|
||||
VkPipelineLayoutCreateInfo layoutCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
};
|
||||
if(vkCreatePipelineLayout(self->device, &layoutCreateInfo, NULL, &self->pipelineLayout) != VK_SUCCESS) {
|
||||
RETURN_ERROR(,"Failed to create vulkan pipeline layout");
|
||||
}
|
||||
VkGraphicsPipelineCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.stageCount = 2,
|
||||
.pStages = stageCreateInfos,
|
||||
.pVertexInputState = &vertexInput,
|
||||
.pInputAssemblyState = &assembly,
|
||||
.pViewportState = &viewportState,
|
||||
.pRasterizationState = &rasterizer,
|
||||
.pMultisampleState = &multisample,
|
||||
.pDepthStencilState = NULL,
|
||||
.pColorBlendState = &blendStateCreateInfo,
|
||||
.pDynamicState = &dynamicState,
|
||||
.layout = self->pipelineLayout,
|
||||
.renderPass = self->renderPass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = -1,
|
||||
};
|
||||
if(vkCreateGraphicsPipelines(self->device, VK_NULL_HANDLE, 1, &createInfo, NULL, &self->graphicsPipeline) != VK_SUCCESS) {
|
||||
RETURN_ERROR(,"Failed to create graphics pipeline");
|
||||
}
|
||||
}
|
||||
|
||||
static void RenderContextCreateFramebuffers(RenderContext *self) {
|
||||
self->swapchainBuffers = list_with_len(sizeof(VkFramebuffer), self->swapchainViews.len);
|
||||
VkImageView* views = self->swapchainViews.data;
|
||||
for(size_t i = 0; i < self->swapchainViews.len; ++i) {
|
||||
VkFramebufferCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.renderPass = self->renderPass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = views + i,
|
||||
.width = self->swapchainExtent.width,
|
||||
.height = self->swapchainExtent.height,
|
||||
.layers = 1
|
||||
};
|
||||
if(vkCreateFramebuffer(self->device, &createInfo, NULL, list_at(&self->swapchainBuffers, i)) != VK_SUCCESS) {
|
||||
RETURN_ERROR(,"Failed to create frambuffer %zu", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderContextSetupCommands(RenderContext *self) {
|
||||
QueueFamilyIndexes indexes = RenderContextFindQueueFamilies(self);
|
||||
VkCommandPoolCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.queueFamilyIndex = indexes.graphicsQueue,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
};
|
||||
if(vkCreateCommandPool(self->device, &createInfo, NULL, &self->commandPool) != VK_SUCCESS) {
|
||||
RETURN_ERROR(,"Failed to create command pool");
|
||||
}
|
||||
VkCommandBufferAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = self->commandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
if(vkAllocateCommandBuffers(self->device, &allocInfo, &self->commandBuffer) != VK_SUCCESS) {
|
||||
RETURN_ERROR(,"Failed to allocate command buffers");
|
||||
}
|
||||
}
|
||||
|
||||
static void RenderContextCreateSyncObjects(RenderContext *self) {
|
||||
VkSemaphoreCreateInfo semaphoreInfo = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
||||
VkFenceCreateInfo fenceInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
||||
};
|
||||
if(vkCreateSemaphore(self->device, &semaphoreInfo, NULL, &self->imageAvailable) != VK_SUCCESS
|
||||
|| vkCreateSemaphore(self->device, &semaphoreInfo, NULL, &self->renderFinished) != VK_SUCCESS
|
||||
|| vkCreateFence(self->device, &fenceInfo, NULL, &self->inFlight) != VK_SUCCESS) {
|
||||
RETURN_ERROR(, "Failed to create synchronisation objects");
|
||||
}
|
||||
}
|
||||
|
||||
RenderContext *RenderContextCreate(const VkApplicationInfo *appinfo) {
|
||||
LOG_INFO("Creating render context");
|
||||
LOG_INFO("Allocating render context");
|
||||
RenderContext* self = malloc(sizeof(RenderContext));
|
||||
memset(self, 0x0, sizeof(RenderContext));
|
||||
LOG_INFO("Initializing SDL window");
|
||||
|
@ -322,13 +607,39 @@ RenderContext *RenderContextCreate(const VkApplicationInfo *appinfo) {
|
|||
RenderContextSelectPhysicalDevice(self);
|
||||
LOG_INFO("Creating logical device");
|
||||
RenderContextCreateLogicalDevice(self);
|
||||
LOG_INFO("Set up swapchain");
|
||||
LOG_INFO("Creating swapchain");
|
||||
RenderContextCreateSwapchain(self);
|
||||
LOG_INFO("Creating image views");
|
||||
RenderContextMakeImageViews(self);
|
||||
LOG_INFO("Creating render pass");
|
||||
RenderContextCreateRenderPass(self);
|
||||
LOG_INFO("Creating graphics pipeline");
|
||||
RenderContextCreateGraphicsPipeline(self);
|
||||
LOG_INFO("Creating framebuffers");
|
||||
RenderContextCreateFramebuffers(self);
|
||||
LOG_INFO("Creating command pool");
|
||||
RenderContextSetupCommands(self);
|
||||
LOG_INFO("Creating synchronisation objects");
|
||||
RenderContextCreateSyncObjects(self);
|
||||
LOG_INFO("Finished setting up vulkan render context");
|
||||
return self;
|
||||
}
|
||||
|
||||
void RenderContextDestroy(RenderContext *self) {
|
||||
vkDestroyFence(self->device, self->inFlight, NULL);
|
||||
vkDestroySemaphore(self->device, self->imageAvailable, NULL);
|
||||
vkDestroySemaphore(self->device, self->renderFinished, NULL);
|
||||
vkFreeCommandBuffers(self->device, self->commandPool, 1, &self->commandBuffer);
|
||||
vkDestroyCommandPool(self->device, self->commandPool, NULL);
|
||||
list_foreach(VkFramebuffer *,buffer, &self->swapchainBuffers)
|
||||
vkDestroyFramebuffer(self->device, *buffer, NULL);
|
||||
vkDestroyPipeline(self->device, self->graphicsPipeline, NULL);
|
||||
vkDestroyPipelineLayout(self->device, self->pipelineLayout, NULL);
|
||||
vkDestroyRenderPass(self->device, self->renderPass, NULL);
|
||||
vkDestroyShaderModule(self->device, self->vertexShader, NULL);
|
||||
vkDestroyShaderModule(self->device, self->fragmentShader, NULL);
|
||||
list_foreach(VkImageView *,view, &self->swapchainViews)
|
||||
vkDestroyImageView(self->device, *view, NULL);
|
||||
vkDestroySwapchainKHR(self->device, self->swapchain, NULL);
|
||||
vkDestroySurfaceKHR(self->vulkanInstance, self->windowSurface, NULL);
|
||||
vkDestroyDevice(self->device, NULL);
|
||||
|
|
|
@ -17,11 +17,27 @@ typedef struct RenderContext {
|
|||
VkSurfaceKHR windowSurface;
|
||||
VkSwapchainKHR swapchain;
|
||||
List swapchainImages; // <VkImage>
|
||||
List swapchainViews; // <VkImageView>
|
||||
List swapchainBuffers; // <VkFramebuffer>
|
||||
VkFormat swapchainFormat;
|
||||
VkExtent2D swapchainExtent;
|
||||
|
||||
VkShaderModule fragmentShader;
|
||||
VkShaderModule vertexShader;
|
||||
|
||||
VkRenderPass renderPass;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkPipeline graphicsPipeline;
|
||||
|
||||
VkCommandPool commandPool;
|
||||
VkCommandBuffer commandBuffer;
|
||||
|
||||
VkQueue graphicsQueue;
|
||||
VkQueue presentQueue;
|
||||
|
||||
VkSemaphore imageAvailable;
|
||||
VkSemaphore renderFinished;
|
||||
VkFence inFlight;
|
||||
} RenderContext;
|
||||
|
||||
// number of queue family indexes, used for QueueFamilyIndexes.as_array
|
||||
|
|
Loading…
Reference in New Issue