Compare commits
4 Commits
91ed8c6e9b
...
672962c2d7
Author | SHA1 | Date |
---|---|---|
Sara | 672962c2d7 | |
Sara | 3b4541477a | |
Sara | db3fb15c7e | |
Sara | 34775c1edf |
2
cutes
2
cutes
|
@ -1 +1 @@
|
||||||
Subproject commit 3f5189f2219ef42bcd98e0c271d05830563d4332
|
Subproject commit 4428b2b135b06c9e8ce0c2f147dafc8b5662babc
|
|
@ -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"
|
||||||
|
|
Binary file not shown.
|
@ -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);
|
||||||
|
}
|
|
@ -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];
|
||||||
|
}
|
Binary file not shown.
85
src/main.c
85
src/main.c
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue