Compare commits

..

4 Commits

9 changed files with 458 additions and 20 deletions

2
cutes

@ -1 +1 @@
Subproject commit 3f5189f2219ef42bcd98e0c271d05830563d4332 Subproject commit 4428b2b135b06c9e8ce0c2f147dafc8b5662babc

View File

@ -23,6 +23,13 @@ workspace "Vulkan-Practice"
includedirs {"src/", "cutes"} includedirs {"src/", "cutes"}
links { "SDL2", "m", "vulkan", "SDL2_image" } links { "SDL2", "m", "vulkan", "SDL2_image" }
postbuildcommands {
"glslc shaders/shader.frag -o shaders/frag.spv",
"glslc shaders/shader.vert -o shaders/vert.spv",
"{RMDIR} %{cfg.targetdir}/shaders",
"{COPYDIR} shaders/ %{cfg.targetdir}/shaders/"
}
filter "configurations:Debug" filter "configurations:Debug"
defines { "DEBUG" } defines { "DEBUG" }
runtime "Debug" runtime "Debug"

BIN
shaders/frag.spv Normal file

Binary file not shown.

9
shaders/shader.frag Normal file
View File

@ -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);
}

20
shaders/shader.vert Normal file
View File

@ -0,0 +1,20 @@
#version 450
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)
);
layout(location = 0) out vec3 fragColor;
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
}

BIN
shaders/vert.spv Normal file

Binary file not shown.

View File

@ -2,13 +2,14 @@
#include <SDL2/SDL_vulkan.h> #include <SDL2/SDL_vulkan.h>
#include <stdlib.h> #include <stdlib.h>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <vulkan/vulkan_core.h>
#include "debug.h" #include "debug.h"
#include "render_context.h" #include "render_context.h"
#define APPNAME "VulkanApp" #define APPNAME "VulkanApp"
const char* appName = APPNAME; static const char *appName = APPNAME;
const VkApplicationInfo appInfo = { static const VkApplicationInfo appInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = APPNAME, .pApplicationName = APPNAME,
.applicationVersion = VK_MAKE_VERSION(0, 0, 1), .applicationVersion = VK_MAKE_VERSION(0, 0, 1),
@ -17,6 +18,79 @@ const VkApplicationInfo appInfo = {
.apiVersion = VK_API_VERSION_1_0, .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) { void Quit(RenderContext *context) {
RenderContextDestroy(context); RenderContextDestroy(context);
@ -26,12 +100,13 @@ void Quit(RenderContext *context) {
void Loop(RenderContext *context) { void Loop(RenderContext *context) {
SDL_Event event; SDL_Event event;
while(1) { do {
SDL_PollEvent(&event); SDL_PollEvent(&event);
if(event.type == SDL_QUIT) Draw(context);
} while(event.type != SDL_QUIT);
vkDeviceWaitIdle(context->device);
Quit(context); Quit(context);
} }
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
if(SDL_Init(SDL_INIT_VIDEO) != 0) { if(SDL_Init(SDL_INIT_VIDEO) != 0) {

View File

@ -1,6 +1,7 @@
#include "render_context.h" #include "render_context.h"
#include <SDL2/SDL_vulkan.h> #include <SDL2/SDL_vulkan.h>
#include <SDL2/SDL_video.h> #include <SDL2/SDL_video.h>
#include <stdio.h>
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
#include "debug.h" #include "debug.h"
#include "list.h" #include "list.h"
@ -15,6 +16,20 @@ static const List requiredVulkanExtensions = {
.element_size = sizeof(char*) .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 // get the list of supported vulkan protocol extensions
static List RenderContextGetExtensions(const RenderContext *self) { static List RenderContextGetExtensions(const RenderContext *self) {
// get the number of available extensions // get the number of available extensions
@ -62,7 +77,7 @@ static VkExtent2D SelectSwapExtent(RenderContext *self, const VkSurfaceCapabilit
return (VkExtent2D) { return (VkExtent2D) {
.width = CLAMP((uint32_t)width, capabilities->minImageExtent.width, capabilities->maxImageExtent.width), .width = CLAMP((uint32_t)width, capabilities->minImageExtent.width, capabilities->maxImageExtent.width),
.height = CLAMP((uint32_t)height, capabilities->minImageExtent.height, capabilities->maxImageExtent.height) .height = CLAMP((uint32_t)height, capabilities->minImageExtent.height, capabilities->maxImageExtent.height)
};; };
} }
static int CheckExtensionSupport(VkPhysicalDevice device) { static int CheckExtensionSupport(VkPhysicalDevice device) {
@ -132,7 +147,7 @@ static void RenderContextInitSdlWindow(RenderContext* self, const VkApplicationI
1920, 1080, 1920, 1080,
SDL_WINDOW_VULKAN | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_SHOWN); SDL_WINDOW_VULKAN | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_SHOWN);
if(!self->window) { 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 // initializes self->vulkanInstance
static void RenderContextInitVulkanInstance(RenderContext *self, const VkApplicationInfo *appinfo) { static void RenderContextInitVulkanInstance(RenderContext *self, const VkApplicationInfo *appinfo) {
List extensions = RenderContextGetExtensions(self); List extensions = RenderContextGetExtensions(self);
const char* validationLayers[] = {
"VK_LAYER_KHRONOS_validation"
};
VkInstanceCreateInfo createInfo = { VkInstanceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = appinfo, .pApplicationInfo = appinfo,
.enabledExtensionCount = extensions.len, .enabledExtensionCount = extensions.len,
.ppEnabledExtensionNames = extensions.data, .ppEnabledExtensionNames = extensions.data,
.enabledLayerCount = 1,
.ppEnabledLayerNames = validationLayers
}; };
if(vkCreateInstance(&createInfo, NULL, &self->vulkanInstance) != VK_SUCCESS) { 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); list_empty(&extensions);
} }
@ -208,14 +228,17 @@ static QueueFamilyIndexes RenderContextFindQueueFamilies(RenderContext *self) {
static List QueueCreateInfoListFrom(QueueFamilyIndexes indexes) { static List QueueCreateInfoListFrom(QueueFamilyIndexes indexes) {
// return empty list if indexes is not initialized // return empty list if indexes is not initialized
if(!indexes.init) if(!indexes.init)
return list_from_type(VkDeviceQueueCreateInfo); RETURN_ERROR(list_from_type(VkDeviceQueueCreateInfo), "Failed to init queue family indeces");
// filter duplicates from indexes // filter duplicates from indexes
List uniqueFamilies = list_from_type(uint32_t); List uniqueFamilies = list_from_type(uint32_t);
for(size_t i = 0; i < QueueFamilyIndexes_Count; ++i) { 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); list_add(&uniqueFamilies, indexes.as_array + i);
} }
}
// compose list of create infos without priories assigned // compose list of create infos without priories assigned
List createInfo_ = list_from_type_with_len(VkDeviceQueueCreateInfo, uniqueFamilies.len); List createInfo_ = list_from_type_with_len(VkDeviceQueueCreateInfo, uniqueFamilies.len);
// for simplicities sake get the direct pointers to underlying arrays // 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) { for(size_t i = 0; i < uniqueFamilies.len; ++i) {
createInfo[i] = (VkDeviceQueueCreateInfo) { createInfo[i] = (VkDeviceQueueCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = families[0], .queueFamilyIndex = families[i],
.queueCount = 1, .queueCount = 1,
}; };
} }
@ -244,14 +267,14 @@ static void RenderContextCreateLogicalDevice(RenderContext *self) {
List extensions = list_copy(&requiredVulkanExtensions); List extensions = list_copy(&requiredVulkanExtensions);
VkDeviceCreateInfo createInfo = { VkDeviceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.queueCreateInfoCount = (uint32_t)queueCreateInfo.len,
.pQueueCreateInfos = queueCreateInfo.data, .pQueueCreateInfos = queueCreateInfo.data,
.queueCreateInfoCount = queueCreateInfo.len,
.enabledExtensionCount = (uint32_t)extensions.len, .enabledExtensionCount = (uint32_t)extensions.len,
.ppEnabledExtensionNames = extensions.data .ppEnabledExtensionNames = extensions.data
}; };
VkPhysicalDeviceFeatures features = {0}; VkPhysicalDeviceFeatures features = {0};
if(vkCreateDevice(self->physicalDevice, &createInfo, NULL, &self->device) != VK_SUCCESS) { 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(&extensions);
list_empty(&queueCreateInfo); list_empty(&queueCreateInfo);
@ -261,7 +284,7 @@ static void RenderContextCreateLogicalDevice(RenderContext *self) {
static void RenderContextCreateSurface(RenderContext *self) { static void RenderContextCreateSurface(RenderContext *self) {
if(SDL_Vulkan_CreateSurface(self->window, self->vulkanInstance, &self->windowSurface) == SDL_FALSE) { 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); SwapChainSupportInfoClean(&support);
if(vkCreateSwapchainKHR(self->device, &createInfo, NULL, &self->swapchain) != VK_SUCCESS) { 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); vkGetSwapchainImagesKHR(self->device, self->swapchain, &chainLength, NULL);
self->swapchainImages = list_with_len(sizeof(VkImage), chainLength); self->swapchainImages = list_with_len(sizeof(VkImage), chainLength);
vkGetSwapchainImagesKHR(self->device, self->swapchain, &chainLength, self->swapchainImages.data); 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) { RenderContext *RenderContextCreate(const VkApplicationInfo *appinfo) {
LOG_INFO("Creating render context"); LOG_INFO("Allocating render context");
RenderContext* self = malloc(sizeof(RenderContext)); RenderContext* self = malloc(sizeof(RenderContext));
memset(self, 0x0, sizeof(RenderContext)); memset(self, 0x0, sizeof(RenderContext));
LOG_INFO("Initializing SDL window"); LOG_INFO("Initializing SDL window");
@ -322,13 +607,39 @@ RenderContext *RenderContextCreate(const VkApplicationInfo *appinfo) {
RenderContextSelectPhysicalDevice(self); RenderContextSelectPhysicalDevice(self);
LOG_INFO("Creating logical device"); LOG_INFO("Creating logical device");
RenderContextCreateLogicalDevice(self); RenderContextCreateLogicalDevice(self);
LOG_INFO("Set up swapchain"); LOG_INFO("Creating swapchain");
RenderContextCreateSwapchain(self); 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"); LOG_INFO("Finished setting up vulkan render context");
return self; return self;
} }
void RenderContextDestroy(RenderContext *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); vkDestroySwapchainKHR(self->device, self->swapchain, NULL);
vkDestroySurfaceKHR(self->vulkanInstance, self->windowSurface, NULL); vkDestroySurfaceKHR(self->vulkanInstance, self->windowSurface, NULL);
vkDestroyDevice(self->device, NULL); vkDestroyDevice(self->device, NULL);

View File

@ -17,11 +17,27 @@ typedef struct RenderContext {
VkSurfaceKHR windowSurface; VkSurfaceKHR windowSurface;
VkSwapchainKHR swapchain; VkSwapchainKHR swapchain;
List swapchainImages; // <VkImage> List swapchainImages; // <VkImage>
List swapchainViews; // <VkImageView>
List swapchainBuffers; // <VkFramebuffer>
VkFormat swapchainFormat; VkFormat swapchainFormat;
VkExtent2D swapchainExtent; VkExtent2D swapchainExtent;
VkShaderModule fragmentShader;
VkShaderModule vertexShader;
VkRenderPass renderPass;
VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline;
VkCommandPool commandPool;
VkCommandBuffer commandBuffer;
VkQueue graphicsQueue; VkQueue graphicsQueue;
VkQueue presentQueue; VkQueue presentQueue;
VkSemaphore imageAvailable;
VkSemaphore renderFinished;
VkFence inFlight;
} RenderContext; } RenderContext;
// number of queue family indexes, used for QueueFamilyIndexes.as_array // number of queue family indexes, used for QueueFamilyIndexes.as_array