#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <shaderc/shaderc.h>
#include <vulkan/vulkan_core.h>
#define SDL_MAIN_HANDLED
#define VK_USE_PLATFORM_XCB_KHR
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <vulkan/vulkan.h>
/* #define VMA_STATIC_VULKAN_FUNCTIONS 0 */
/* #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 */
/* #include "vk_mem_alloc.h" */
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#define FAST_OBJ_IMPLEMENTATION
#include <fast_obj.h>
#define CGLTF_IMPLEMENTATION
#include <cgltf.h>
#include "cplusplus.h"
//#include "vkutil.h"
#include "state.h"
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#include "vk_mem_alloc.h"
#define MODEL_PATH "assets/human.obj"
#define TEXTURE_PATH "assets/viking_room.png"
#define PARTICLE_COUNT 2048
void load_model_obj();
void load_model_gltf();
// embedded clgm library
uint32_t currentFrame = 0;
state_t s;
/* Vertex vertices[] = { */
/* (Vertex) { (V2) {-0.2f, -0.5f}, (V3) {0.0f, 1.0f, 0.0f}}, */
/* (Vertex) { (V2) {0.5f, 0.3f}, (V3) {0.0f, 0.0f, 1.0f}}, */
/* (Vertex) { (V2) {-0.5f, 0.7f}, (V3) {1.0f, 0.0f, 0.0f}}, */
/* (Vertex) { (V2) {0.2f, -0.5f}, (V3) {0.0f, 0.0f, 1.0f}}, */
/* (Vertex) { (V2) {0.5f, 0.7f}, (V3) {1.0f, 0.0f, 0.0f}}, */
/* (Vertex) { (V2) {-0.5f, 0.3f}, (V3) {0.0f, 1.0f, 0.0f}}, */
/* (Vertex) { (V2) {0.0f, -0.5f}, (V3) {1.0f, 0.0f, 0.0f}}, */
/* (Vertex) { (V2) {0.5f, 0.5f}, (V3) {0.0f, 1.0f, 0.0f}}, */
/* (Vertex) { (V2) {-0.5f, 0.5f}, (V3) {0.0f, 0.0f, 1.0f}}, */
/* }; */
/* const int VERTICES_SIZE = VK_ARRAY_LEN(vertices); */
/* const uint16_t indices[] = { */
/* 0, 1, 2, 3, 4, 5, 6, 7, 8, */
/* }; */
/* const int INDICES_SIZE = VK_ARRAY_LEN(indices); */
Vertex vertices[] = {
(Vertex) { (V3) {-0.5f, -0.5f, 0.0f}, (V3) {1.0f, 0.0f, 0.0f}, (V2) {0.0f, 0.0f}},
(Vertex) { (V3) {0.5f, -0.5f, 0.0f}, (V3) {0.0f, 1.0f, 0.0f}, (V2) {1.0f, 0.0f}},
(Vertex) { (V3) {0.5f, 0.5f, 0.0f}, (V3) {0.0f, 0.0f, 1.0f}, (V2) {1.0f, 1.0f}},
(Vertex) { (V3) {-0.5f, 0.5f, 0.0f}, (V3) {1.0f, 1.0f, 1.0f}, (V2) {0.0f, 1.0f}},
(Vertex) { (V3) {-0.5f, -0.5f, -0.5f}, (V3) {1.0f, 0.0f, 0.0f}, (V2) {0.0f, 0.0f}},
(Vertex) { (V3) {0.5f, -0.5f, -0.5f}, (V3) {0.0f, 1.0f, 0.0f}, (V2) {1.0f, 0.0f}},
(Vertex) { (V3) {0.5f, 0.5f, -0.5f}, (V3) {0.0f, 0.0f, 1.0f}, (V2) {1.0f, 1.0f}},
(Vertex) { (V3) {-0.5f, 0.5f, -0.5f}, (V3) {1.0f, 1.0f, 1.0f}, (V2) {0.0f, 1.0f}},
(Vertex) { (V3) {0.0f, 0.0f, 0.5f}, (V3) {1.0f, 1.0f, 1.0f}, (V2) {0.0f, 0.0f}},
};
const int VERTICES_SIZE = VK_ARRAY_LEN(vertices);
const uint32_t indices[] = {
0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 5, 4
};
const int INDICES_SIZE = VK_ARRAY_LEN(indices);
/* Vertex vertices[] = { */
/* (Vertex) { (V3) {0}, (V3) {0}, (V2) {0}}, */
/* (Vertex) { (V3) {1}, (V3) {0}, (V2) {0}}, */
/* (Vertex) { (V3) {2}, (V3) {0}, (V2) {0}}, */
/* (Vertex) { (V3) {3}, (V3) {0}, (V2) {0}}, */
/* (Vertex) { (V3) {4}, (V3) {0}, (V2) {0}}, */
/* (Vertex) { (V3) {5}, (V3) {0}, (V2) {0}}, */
/* }; */
/* const int VERTICES_SIZE = VK_ARRAY_LEN(vertices); */
/* const uint16_t indices[] = { */
/* 0,0,0,0,0,0 */
/* }; */
/* const int INDICES_SIZE = VK_ARRAY_LEN(indices); */
static int resizing_event_watcher(void *data, SDL_Event *event) {
(void) data;
if (event->type == SDL_WINDOWEVENT &&
event->window.event == SDL_WINDOWEVENT_RESIZED) {
s.sdl_window_resized = 1;
}
return 0;
}
void
move_towards(vec3 position, vec3 front, float step)
{
// Calculate the direction vector from position to front
float direction[3] = {
front[0] - position[0],
front[1] - position[1],
front[2] - position[2]
};
// Normalize the direction vector
glm_normalize(direction);
// Move position along the direction vector by the step amount
position[0] += direction[0] * step;
position[1] += direction[1] * step;
position[2] += direction[2] * step;
}
bool
init()
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
vk_log(VK_INFO, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
return false;
}
s.vk.window = SDL_CreateWindow("Vulkanizater",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
s.window_w,
s.window_h,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_VULKAN);
if (s.vk.window == NULL) {
vk_log(VK_INFO, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
return false;
}
SDL_AddEventWatch(resizing_event_watcher,NULL);
return true;
}
void
closeSDL()
{
SDL_DestroyWindow(s.vk.window);
s.vk.window = NULL;
SDL_Quit();
}
static VKAPI_ATTR VkBool32 VKAPI_CALL
debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
(void) messageSeverity;
(void) messageType;
(void) pUserData;
vk_log(VK_ERROR, "validation layer: %s\n", pCallbackData->pMessage);
return VK_FALSE;
}
void
vulkan_setup_debug_messenger()
{
if (!enable_validation_layers) return;
VkDebugUtilsMessengerCreateInfoEXT createInfo = {0};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = debugCallback;
createInfo.pUserData = NULL; // Optional
// TODO: func pointers returned are NULL
s.pfnCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(s.vk.instance, "vkCreateDebugUtilsMessengerEXT");
s.pfnDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(s.vk.instance, "vkDestroyDebugUtilsMessengerEXT");
VkResult result = s.pfnCreateDebugUtilsMessengerEXT(s.vk.instance, &createInfo, NULL, &s.vk_debug_messenger);
if (result != VK_SUCCESS) {
vk_log(VK_WARN, "failed to set up debug messenger!\n");
} else {
vk_log(VK_INFO, "Debug messanger created!\n");
}
}
typedef struct QueueFamilyIndices {
uint32_t graphicsAndComputeFamily;
bool graphicsFlag;
uint32_t presentFamily;
bool presentFlag;
} QueueFamilyIndices;
bool
vulkan_queue_family_check_flags(QueueFamilyIndices x)
{
return x.graphicsFlag && x.presentFlag;
}
QueueFamilyIndices vulkan_find_queue_families(VkPhysicalDevice device) {
QueueFamilyIndices indices;
indices.graphicsFlag = false;
indices.presentFlag = false;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL);
VkQueueFamilyProperties queueFamilies[queueFamilyCount];
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies);
for (uint32_t i = 0; i < queueFamilyCount; i++) {
if ((queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && (queueFamilies[i].queueFlags & VK_QUEUE_COMPUTE_BIT)) {
indices.graphicsAndComputeFamily = i;
indices.graphicsFlag = true;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, s.vk.surface, &presentSupport);
if (presentSupport) {
indices.presentFamily = i;
indices.presentFlag = true;
}
if (vulkan_queue_family_check_flags(indices)) break;
}
return indices;
}
void move_relative(vec3 position, vec3 front, float step, int x) {
// Calculate the direction vector from position to front
float direction_vec[3] = {
front[0] - position[0],
front[1] - position[1],
front[2] - position[2]
};
// Normalize the direction vector
glm_normalize(direction_vec);
// Calculate the right vector
vec3 right;
glm_vec3_cross(GLM_ZUP, direction_vec, right);
glm_normalize(right);
// Move front according to the specified direction
if (x) {
front[0] += right[0] * step;
front[1] += right[1] * step;
front[2] += right[2] * step;
} else {
// Calculate the up vector
vec3 up;
glm_vec3_cross(right, direction_vec, up);
glm_normalize(up);
front[0] += up[0] * step;
front[1] += up[1] * step;
front[2] += up[2] * step;
}
}
void
update_camera(float xoffset, float yoffset)
{
s.camera.yaw += xoffset;
s.camera.pitch += yoffset;
// Make sure that when pitch is out of bounds, the screen doesn't get flipped
/* if (s.camera.pitch > 89.0f) */
/* s.camera.pitch = 89.0f; */
/* if (s.camera.pitch < -89.0f) */
/* s.camera.pitch = -89.0f; */
move_relative(s.camera.pos, s.camera.front, xoffset / 100.0, 1 /* x axis */);
move_relative(s.camera.pos, s.camera.front, yoffset / 100.0, 0 /* y axis */);
/* vec3 front; */
/* front[0] = -sin(glm_rad(s.camera.yaw)) * cos(glm_rad(s.camera.pitch)); */
/* front[1] = sin(glm_rad(s.camera.pitch)); */
/* front[2] = -cos(glm_rad(s.camera.yaw)) * cos(glm_rad(s.camera.pitch)); */
/* glm_normalize_to(front, s.camera.front); */
}
void
mouseCallback(SDL_Event *event)
{
if (event->type != SDL_MOUSEMOTION) return;
float xoffset = event->motion.xrel;
float yoffset = -event->motion.yrel; // Reversed since y-coordinates range from bottom to top
float sensitivity = 0.1f; // Change this value to your liking
xoffset *= sensitivity;
yoffset *= sensitivity;
if (s.mouse_pressed) {
update_camera(xoffset, yoffset);
} else if (s.middle_click) {
vec4 _up = {0, 1, 0, 0};
vec4 _right = {1, 0, 0, 0};
vec4 up, right;
glm_mat4_mulv(s.ubo.model, _up, up);
glm_mat4_mulv(s.ubo.model, _right, right);
glm_rotate(s.ubo.model, glm_rad(xoffset * glm_rad(90.0f)), GLM_ZUP);
glm_rotate(s.ubo.model, glm_rad(yoffset * glm_rad(90.0f)), GLM_XUP);
}
return;
}
VkShaderModule
createShaderModule(const char * code, long size)
{
VkShaderModuleCreateInfo createInfo = {0};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = size;
createInfo.pCode = (const uint32_t *)code;
VkShaderModule shaderModule;
if (vkCreateShaderModule(s.vk.device, &createInfo, NULL, &shaderModule) != VK_SUCCESS) {
vk_log(VK_ERROR, "failed to create shader module!\n");
}
return shaderModule;
}
shaderc_compilation_result_t
load_compile_shader_data(const char * path, shaderc_shader_kind shader_kind)
{
FILE* file = fopen(path, "r");
if (!file) {
vk_log(VK_ERROR, "SHADER COMPILATION: Failed to open file: %s\n", path);
return NULL;
}
fseek(file, 0, SEEK_END);
long glsl_length = ftell(file);
fseek(file, 0, SEEK_SET);
char* glsl_src = (char*)malloc(glsl_length + 1);
if (!glsl_src) {
vk_log(VK_ERROR, "SHADER COMPILATION: Failed to allocate memory\n");
fclose(file);
return NULL;
}
fread(glsl_src, 1, glsl_length, file);
glsl_src[glsl_length] = '\0';
fclose(file);
shaderc_compiler_t compiler = shaderc_compiler_initialize();
if (!compiler) {
vk_log(VK_ERROR, "SHADER COMPILATION: Failed to initialize shader compiler\n");
free(glsl_src);
return NULL;
}
shaderc_compile_options_t options = shaderc_compile_options_initialize();
shaderc_compilation_result_t result = shaderc_compile_into_spv(compiler,
glsl_src,
glsl_length,
shader_kind,
path, "main", options);
if (shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success) {
vk_log(VK_ERROR, "SHADER COMPILATION: error: %s\n", shaderc_result_get_error_message(result));
shaderc_result_release(result);
shaderc_compiler_release(compiler);
free(glsl_src);
return NULL;
}
shaderc_compiler_release(compiler);
free(glsl_src);
return result;
}
void
vulkan_create_graphics_pipeline(VkPolygonMode polygon_mode)
{
shaderc_compilation_result_t vert_result = load_compile_shader_data("src/shader.vert", shaderc_vertex_shader);
if (!vert_result) {
vk_log(VK_ERROR, "Can't load vertex shader\n");
if (s.prev_vert_result) {
vert_result = s.prev_vert_result;
}
}
if (s.prev_vert_result && vert_result != s.prev_vert_result) shaderc_result_release(s.prev_vert_result);
s.prev_vert_result = vert_result;
shaderc_compilation_result_t frag_result = load_compile_shader_data("src/shader.frag", shaderc_fragment_shader);
if (!frag_result) {
vk_log(VK_ERROR, "Can't load fragment shader\n");
if (s.prev_frag_result) {
frag_result = s.prev_frag_result;
}
}
if (s.prev_frag_result && frag_result != s.prev_frag_result) shaderc_result_release(s.prev_frag_result);
s.prev_frag_result = frag_result;
const char * vert_data = shaderc_result_get_bytes(vert_result);
long vert_size = shaderc_result_get_length(vert_result);
const char * frag_data = shaderc_result_get_bytes(frag_result);
long frag_size = shaderc_result_get_length(frag_result);
vk_log(VK_INFO, "Shaders loaded\n");
VkShaderModule vertShaderModule = createShaderModule(vert_data, vert_size);
VkShaderModule fragShaderModule = createShaderModule(frag_data, frag_size);
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {0};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {0};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE,
VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE,
// TODO Make polygon mode dynamic
//VK_DYNAMIC_STATE_POLYGON_MODE_EXT
};
VkPipelineDynamicStateCreateInfo dynamicState = {0};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = VK_ARRAY_LEN(dynamicStates);
dynamicState.pDynamicStates = dynamicStates;
VkVertexInputBindingDescription bindingDescription = {0};
bindingDescription.binding = 0;
bindingDescription.stride = sizeof(Vertex);
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attributeDescriptions[3] = {0};
attributeDescriptions[0].binding = 0;
attributeDescriptions[0].location = 0;
attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[0].offset = offsetof(Vertex, pos);
attributeDescriptions[1].binding = 0;
attributeDescriptions[1].location = 1;
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[1].offset = offsetof(Vertex, normal);
attributeDescriptions[2].binding = 0;
attributeDescriptions[2].location = 2;
attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
attributeDescriptions[2].offset = offsetof(Vertex, texCoord);
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {0};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputInfo.vertexAttributeDescriptionCount = VK_ARRAY_LEN(attributeDescriptions);
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions;
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {0};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float) s.vk.swapchain.extent.width;
viewport.height = (float) s.vk.swapchain.extent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {0};
scissor.offset = (VkOffset2D){0, 0};
scissor.extent = s.vk.swapchain.extent;
VkPipelineViewportStateCreateInfo viewportState = {0};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer = {0};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = polygon_mode;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_NONE;
//rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasConstantFactor = 0.0f; // Optional
rasterizer.depthBiasClamp = 0.0f; // Optional
rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
VkPipelineMultisampleStateCreateInfo multisampling = {0};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = s.vk.msaa_samples;
multisampling.minSampleShading = 1.0f; // Optional
multisampling.pSampleMask = NULL; // Optional
multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
multisampling.alphaToOneEnable = VK_FALSE; // Optional
VkPipelineColorBlendAttachmentState colorBlendAttachment = {0};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlending = {0};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_TRUE;
colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f; // Optional
colorBlending.blendConstants[1] = 0.0f; // Optional
colorBlending.blendConstants[2] = 0.0f; // Optional
colorBlending.blendConstants[3] = 0.0f; // Optional
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {0};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &s.vk_descriptor_set_layout;
pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
pipelineLayoutInfo.pPushConstantRanges = NULL; // Optional
if (vkCreatePipelineLayout(s.vk.device, &pipelineLayoutInfo, NULL, &s.graphics_pipeline.layout) != VK_SUCCESS) {
vk_log(VK_ERROR, "failed to create pipeline layout!\n");
}
VkPipelineDepthStencilStateCreateInfo depthStencil = {0};
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencil.depthTestEnable = VK_TRUE;
depthStencil.depthWriteEnable = VK_TRUE;
depthStencil.depthBoundsTestEnable = VK_FALSE;
depthStencil.stencilTestEnable = VK_FALSE;
depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
depthStencil.minDepthBounds = 0.0f; // Optional
depthStencil.maxDepthBounds = 1.0f; // Optional
depthStencil.front = (VkStencilOpState){0}; // Optional
depthStencil.back = (VkStencilOpState){0}; // Optional
VkPipelineRenderingCreateInfo pipeline_rendering_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &s.vk.swapchain.image_format,
.depthAttachmentFormat = findDepthFormat(s.vk),
};
VkGraphicsPipelineCreateInfo pipelineInfo = {0};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
pipelineInfo.pNext = &pipeline_rendering_create_info;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = &depthStencil;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = s.graphics_pipeline.layout;
pipelineInfo.renderPass = VK_NULL_HANDLE;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
pipelineInfo.basePipelineIndex = -1; // Optional
if (vkCreateGraphicsPipelines(s.vk.device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &s.graphics_pipeline.handle) != VK_SUCCESS) {
vk_log(VK_ERROR, "failed to create graphics pipeline!\n");
}
if (s.prev_vert_result != vert_result) {
shaderc_result_release(vert_result);
}
if (s.prev_frag_result != frag_result) {
shaderc_result_release(frag_result);
}
vkDestroyShaderModule(s.vk.device, fragShaderModule, NULL);
vkDestroyShaderModule(s.vk.device, vertShaderModule, NULL);
}
void
vulkan_create_command_buffer()
{
// TODO Find a way to group allocation
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VkCommandBufferAllocateInfo allocInfo = {0};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = s.vk.command_pool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
VK_CHECK(vkAllocateCommandBuffers(s.vk.device, &allocInfo, &s.frames[i].vk_command_buffer));
}
}
void
recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
{
VkCommandBufferBeginInfo beginInfo = {0};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0; // Optional
beginInfo.pInheritanceInfo = NULL; // Optional
VK_CHECK(vkBeginCommandBuffer(commandBuffer, &beginInfo));
vkCmdSetDepthTestEnable(commandBuffer, 1);
vkCmdSetDepthWriteEnable(commandBuffer, 1);
// TODO Make polygon mode dynamic
//vkCmdSetPolygonModeEXT(commandBuffer, s.polygon_mode);
vks_transition_image_layout_info transition_info = { 0 };
transition_info.image = s.vk.swapchain.images[imageIndex];
transition_info.format = VK_FORMAT_R8G8B8A8_SRGB;
transition_info.srcAccessMask = 0;
transition_info.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
transition_info.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
transition_info.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
transition_info.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
transition_info.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
transition_info.mipLevels = 1;
vks_transition_image_layout(s.vk, &transition_info);
VkRenderingAttachmentInfo colorAttachment = {0};
colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
colorAttachment.imageView = s.vk.color_image.view;
colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachment.resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT;
colorAttachment.resolveImageView = s.vk.swapchain.image_views[imageIndex];
colorAttachment.resolveImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.clearValue.color =
(VkClearColorValue){{0.0f, 0.0f, 0.0f, 1.0f}};
VkRenderingAttachmentInfo depthAttachment = {0};
depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
depthAttachment.imageView = s.vk.depth_image.view;
depthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.clearValue.depthStencil = (VkClearDepthStencilValue){1.0f, 0u};
VkRenderingInfo renderingInfo = {};
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
renderingInfo.renderArea =
(VkRect2D){{0, 0}, s.vk.swapchain.extent};
renderingInfo.layerCount = 1;
renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &colorAttachment;
renderingInfo.pDepthAttachment = &depthAttachment;
vkCmdBeginRendering(commandBuffer, &renderingInfo);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
s.graphics_pipeline.handle);
VkViewport viewport = {0};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)(s.vk.swapchain.extent.width);
viewport.height = (float)(s.vk.swapchain.extent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor = {0};
scissor.offset = (VkOffset2D){0, 0};
scissor.extent = s.vk.swapchain.extent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
VkBuffer vertexBuffers[] = {s.vertex_buffer.handle};
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffer, s.index_buffer.handle, 0,
VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
s.graphics_pipeline.layout, 0, 1,
&s.frames[currentFrame].vk_descriptor_set, 0, NULL);
vkCmdDrawIndexed(commandBuffer, s.indices_count, 1, 0, 0, 0);
imgui_draw(); // does this need to be here?
imgui_draw_cmd(commandBuffer);
vkCmdEndRendering(commandBuffer);
transition_info.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
transition_info.dstAccessMask = 0;
transition_info.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
transition_info.dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
transition_info.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
transition_info.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
vks_transition_image_layout(s.vk, &transition_info);
VK_CHECK(vkEndCommandBuffer(commandBuffer));
}
void
vulkan_create_sync_objects()
{
VkSemaphoreCreateInfo semaphoreInfo = {0};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo = {0};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VK_CHECK(vkCreateSemaphore(s.vk.device, &semaphoreInfo, NULL, &s.frames[i].image_available_semaphore));
VK_CHECK(vkCreateSemaphore(s.vk.device, &semaphoreInfo, NULL, &s.frames[i].render_finished_semaphore));
VK_CHECK(vkCreateFence(s.vk.device, &fenceInfo, NULL, &s.frames[i].in_flight_fence));
}
}
void
vulkan_create_vertex_buffer()
{
VkDeviceSize bufferSize = sizeof(s.vertices[0]) * s.vertices_count;
/* VkDeviceSize bufferSize = sizeof(vertices[0]) * VERTICES_SIZE; */
VkBufferCreateInfo bufferInfo = { 0 };
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = bufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocCreateInfo = {0};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
vmaCreateBuffer(s.vk.allocator,
&bufferInfo,
&allocCreateInfo,
&s.vertex_buffer.handle,
&s.vertex_buffer.allocation,
NULL);
vmaCopyMemoryToAllocation(s.vk.allocator, s.vertices, s.vertex_buffer.allocation, 0, (size_t)bufferSize);
}
void
vulkan_create_index_buffer()
{
VkDeviceSize bufferSize = sizeof(s.indices[0]) * s.indices_count;
/* VkDeviceSize bufferSize = sizeof(indices[0]) * INDICES_SIZE; */
VkBufferCreateInfo bufferInfo = { 0 };
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = bufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocCreateInfo = {0};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
vmaCreateBuffer(s.vk.allocator,
&bufferInfo,
&allocCreateInfo,
&s.index_buffer.handle,
&s.index_buffer.allocation,
NULL);
vmaCopyMemoryToAllocation(s.vk.allocator, s.indices, s.index_buffer.allocation, 0, (size_t)bufferSize);
}
void
vulkan_create_descriptor_set_layout()
{
VkDescriptorSetLayoutBinding uboLayoutBinding = {0};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL;
//uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
uboLayoutBinding.pImmutableSamplers = NULL; // optional
VkDescriptorSetLayoutBinding samplerLayoutBinding = {0};
samplerLayoutBinding.binding = 1;
samplerLayoutBinding.descriptorCount = 1;
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerLayoutBinding.pImmutableSamplers = NULL;
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding bindings[2] = {uboLayoutBinding, samplerLayoutBinding};
VkDescriptorSetLayoutCreateInfo layoutInfo = {0};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = VK_ARRAY_LEN(bindings);
layoutInfo.pBindings = bindings;
VK_CHECK(vkCreateDescriptorSetLayout(s.vk.device, &layoutInfo, NULL, &s.vk_descriptor_set_layout));
}
void
vulkan_create_uniform_buffers()
{
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VkBufferCreateInfo bufferInfo = { 0 };
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = bufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocCreateInfo = {0};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
//if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
VmaAllocationInfo allocInfo;
vmaCreateBuffer(s.vk.allocator,
&bufferInfo,
&allocCreateInfo,
&s.frames[i].uniform_buffer.handle,
&s.frames[i].uniform_buffer.allocation,
&allocInfo);
s.frames[i].uniform_buffer_mapped = allocInfo.pMappedData;
}
}
void
vulkan_create_descriptor_pool()
{
VkDescriptorPoolSize poolSizes[3] = {0};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[0].descriptorCount = MAX_FRAMES_IN_FLIGHT;
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = MAX_FRAMES_IN_FLIGHT;
poolSizes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
poolSizes[2].descriptorCount = (uint32_t)(MAX_FRAMES_IN_FLIGHT) * 2;
VkDescriptorPoolCreateInfo poolInfo = {0};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = VK_ARRAY_LEN(poolSizes);
poolInfo.pPoolSizes = poolSizes;
poolInfo.maxSets = MAX_FRAMES_IN_FLIGHT;
VK_CHECK(vkCreateDescriptorPool(s.vk.device, &poolInfo, NULL, &s.vk_descriptor_pool));
}
void
vulkan_create_descriptor_sets()
{
VkDescriptorSetLayout layouts[MAX_FRAMES_IN_FLIGHT] = {0};
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
layouts[i] = s.vk_descriptor_set_layout;
}
// TODO Find a way to group allocation
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VkDescriptorSetAllocateInfo allocInfo = {0};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = s.vk_descriptor_pool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = layouts;
VK_CHECK(vkAllocateDescriptorSets(s.vk.device, &allocInfo, &s.frames[i].vk_descriptor_set));
}
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VkDescriptorBufferInfo bufferInfo = {0};
bufferInfo.buffer = s.frames[i].uniform_buffer.handle;
bufferInfo.offset = 0;
bufferInfo.range = sizeof(UniformBufferObject);
VkWriteDescriptorSet descriptorWrites[2] = {0};
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = s.frames[i].vk_descriptor_set;
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrites[0].descriptorCount = 1;
descriptorWrites[0].pBufferInfo = &bufferInfo;
VkDescriptorImageInfo imageInfo = {0};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = s.texture_image.view;
imageInfo.sampler = s.vk_texture_sampler;
descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = s.frames[i].vk_descriptor_set;
descriptorWrites[1].dstBinding = 1;
descriptorWrites[1].dstArrayElement = 0;
descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrites[1].descriptorCount = 1;
descriptorWrites[1].pImageInfo = &imageInfo;
/* VkDescriptorBufferInfo storageBufferInfoLastFrame = {0}; */
/* storageBufferInfoLastFrame.buffer = s.frames[(i - 1) % MAX_FRAMES_IN_FLIGHT].vk_shader_storage_buffer; */
/* storageBufferInfoLastFrame.offset = 0; */
/* storageBufferInfoLastFrame.range = sizeof(Particle) * PARTICLE_COUNT; */
/* descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; */
/* descriptorWrites[2].dstSet = s.frames[i].vk_compute_descriptor_set; */
/* descriptorWrites[2].dstBinding = 1; */
/* descriptorWrites[2].dstArrayElement = 0; */
/* descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; */
/* descriptorWrites[2].descriptorCount = 1; */
/* descriptorWrites[2].pBufferInfo = &storageBufferInfoLastFrame; */
/* VkDescriptorBufferInfo storageBufferInfoCurrentFrame = {0}; */
/* storageBufferInfoCurrentFrame.buffer = s.frames[i].vk_shader_storage_buffer; */
/* storageBufferInfoCurrentFrame.offset = 0; */
/* storageBufferInfoCurrentFrame.range = sizeof(Particle) * PARTICLE_COUNT; */
/* descriptorWrites[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; */
/* descriptorWrites[3].dstSet = s.frames[i].vk_compute_descriptor_set; */
/* descriptorWrites[3].dstBinding = 2; */
/* descriptorWrites[3].dstArrayElement = 0; */
/* descriptorWrites[3].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; */
/* descriptorWrites[3].descriptorCount = 1; */
/* descriptorWrites[3].pBufferInfo = &storageBufferInfoCurrentFrame; */
vkUpdateDescriptorSets(s.vk.device, VK_ARRAY_LEN(descriptorWrites), descriptorWrites, 0, NULL);
}
}
void
recreate_graphics_pipeline()
{
vks_recreate_swapchain(&s.vk);
vkDestroyPipeline(s.vk.device, s.graphics_pipeline.handle, NULL);
vkDestroyPipelineLayout(s.vk.device, s.graphics_pipeline.layout, NULL);
vulkan_create_graphics_pipeline(s.polygon_mode);
}
int polygon_mode_n = 3;
VkPolygonMode polygon_modes[3] = {VK_POLYGON_MODE_FILL, VK_POLYGON_MODE_LINE,
VK_POLYGON_MODE_POINT};
int toggle = 0;
void
handle_input(bool * quit)
{
SDL_Event e;
while (SDL_PollEvent(&e) != 0) {
imgui_proc_event(&e);
// User requests quit
if (e.type == SDL_QUIT) {
*quit = true;
}
// User presses a key
else if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_w:
// s.camera.pos[0] += 0.1f;
move_towards(s.camera.front, s.camera.pos, -0.1f);
move_towards(s.camera.pos, s.camera.front, 0.1f);
break;
case SDLK_s:
//s.camera.pos[0] -= 0.1f;
move_towards(s.camera.front, s.camera.pos, 0.1f);
move_towards(s.camera.pos, s.camera.front, -0.1f);
break;
case SDLK_a:
move_relative(s.camera.front, s.camera.pos, -0.1f, 1);
move_relative(s.camera.pos, s.camera.front, 0.1f, 1);
break;
case SDLK_d:
move_relative(s.camera.front, s.camera.pos, 0.1f, 1);
move_relative(s.camera.pos, s.camera.front, -0.1f, 1);
break;
case SDLK_q:
move_relative(s.camera.front, s.camera.pos, 0.1f, 0);
move_relative(s.camera.pos, s.camera.front, 0.1f, 0);
break;
case SDLK_e:
move_relative(s.camera.front, s.camera.pos, -0.1f, 0);
move_relative(s.camera.pos, s.camera.front, -0.1f, 0);
break;
case SDLK_g: // reload shaders
recreate_graphics_pipeline();
break;
case SDLK_z: // toggle polygon mode
polygon_mode_n = (polygon_mode_n + 1) % 3;
s.polygon_mode = polygon_modes[polygon_mode_n];
recreate_graphics_pipeline();
break;
case SDLK_l:
s.rotate = s.rotate ? 0 : 1;
break;
case SDLK_m:
vkDeviceWaitIdle(s.vk.device);
free(s.vertices);
free(s.indices);
/* if (!strcmp(s.model_path, "assets/human.obj")) */
/* strcpy(s.model_path, "assets/viking_room.obj"); */
/* else if (!strcmp(s.model_path, "assets/viking_room.obj")) */
/* strcpy(s.model_path, "assets/monkey.obj"); */
/* else */
/* strcpy(s.model_path, "assets/human.obj"); */
if (toggle) {
strcpy(s.model_path, "assets/viking_room.obj");
load_model_obj();
toggle = 0;
} else {
toggle = 1;
load_model_gltf();
}
vkDestroyBuffer(s.vk.device, s.vertex_buffer.handle, NULL);
vkFreeMemory(s.vk.device, s.vertex_buffer.memory, NULL);
vkDestroyBuffer(s.vk.device, s.index_buffer.handle, NULL);
vkFreeMemory(s.vk.device, s.index_buffer.memory, NULL);
vulkan_create_vertex_buffer();
vulkan_create_index_buffer();
break;
case SDLK_r:
s.camera.pos[0] = 1.0f;
s.camera.pos[1] = 1.0f;
s.camera.pos[2] = 1.0f;
s.camera.front[0] = 0.0f;
s.camera.front[1] = 0.0f;
s.camera.front[2] = 0.0f;
s.camera.up[0] = 0.0f;
s.camera.up[1] = 0.0f;
s.camera.up[2] = 1.0f;
s.camera.yaw = 0.0f;
s.camera.pitch = 90.0f;
s.camera.lastX = 400.0f;
s.camera.lastY = 300.0f;
s.camera.fov = 45.0f;
glm_mat4_identity(s.ubo.model);
s.zoom = 10;
update_camera(0,0);
break;
}
}
else if (e.type == SDL_MOUSEWHEEL) {
if(e.wheel.y > 0) // scroll up
{
s.zoom -= 1;
}
else if(e.wheel.y < 0) // scroll down
{
s.zoom += 1;
if (s.zoom == -100) s.zoom = 1;
}
}
else if (e.type == SDL_MOUSEBUTTONDOWN) {
if (e.button.button != 2) {
s.mouse_pressed = 1;
} else {
s.middle_click = 1;
}
}
else if (e.type == SDL_MOUSEBUTTONUP) {
if (e.button.button != 2) {
s.mouse_pressed = 0;
} else {
s.middle_click = 0;
}
}
mouseCallback(&e);
}
}
void
vulkan_create_texture_image()
{
int texWidth, texHeight, texChannels;
stbi_uc* pixels = stbi_load(TEXTURE_PATH, &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
VkDeviceSize imageSize = texWidth * texHeight * 4;
s.vk_mip_levels = (uint32_t)floor(log2(fmax(texWidth, texHeight))) + 1;
if (!pixels) {
vk_log(VK_ERROR, "failed to load texture image!\n");
}
VkBufferCreateInfo bufferInfo = { 0 };
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = imageSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocCreateInfo = {0};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
vks_buffer stagingBuffer = {0};
vmaCreateBuffer(s.vk.allocator,
&bufferInfo,
&allocCreateInfo,
&stagingBuffer.handle,
&stagingBuffer.allocation,
NULL);
vmaCopyMemoryToAllocation(s.vk.allocator, pixels, stagingBuffer.allocation, 0, (size_t)imageSize);
stbi_image_free(pixels);
vks_create_image(s.vk, texWidth, texHeight, s.vk_mip_levels, VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_SAMPLE_COUNT_1_BIT, &s.texture_image);
vks_transition_image_layout_info transition_info = { 0 };
transition_info.image = s.texture_image.handle;
transition_info.format = VK_FORMAT_R8G8B8A8_SRGB;
transition_info.srcAccessMask = 0;
transition_info.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition_info.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
transition_info.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition_info.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
transition_info.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
transition_info.mipLevels = s.vk_mip_levels;
vks_transition_image_layout(s.vk, &transition_info);
vks_copy_buffer_to_image(s.vk,
stagingBuffer.handle,
s.texture_image.handle,
(uint32_t)texWidth,
(uint32_t)texHeight);
vks_generate_mipmaps(s.vk, s.texture_image.handle, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, s.vk_mip_levels);
transition_info.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition_info.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
transition_info.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition_info.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
transition_info.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
transition_info.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
vks_transition_image_layout(s.vk, &transition_info);
vmaDestroyBuffer(s.vk.allocator, stagingBuffer.handle, stagingBuffer.allocation);
s.texture_image.view = vks_create_image_view(s.vk, s.texture_image.handle, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, s.vk_mip_levels);
}
void vulkan_create_texture_sampler() {
VkSamplerCreateInfo samplerInfo = {0};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.anisotropyEnable = VK_TRUE;
VkPhysicalDeviceProperties properties = {0};
vkGetPhysicalDeviceProperties(s.vk.physical_device, &properties);
samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = VK_LOD_CLAMP_NONE;
VK_CHECK(vkCreateSampler(s.vk.device, &samplerInfo, NULL, &s.vk_texture_sampler));
}
void
load_model_obj()
{
fastObjMesh *m = fast_obj_read(s.model_path);
if (!m) {
vk_log(VK_ERROR, "Can't load object\n");
}
s.indices_count = m->index_count;
s.indices = (uint32_t *)malloc(s.indices_count * sizeof(uint32_t));
s.vertices_count = 0;
if (m->group_count > 1) {
vk_log(VK_WARN, "fast_obj Mesh with groups > 1 not supported.");
}
/*
Since blender exports objs indexed we use those same indices for our
indexed draw call.
*/
s.vertices = (Vertex *)malloc(s.indices_count * sizeof(Vertex));
/* Count indexs and vertices */
size_t c = 0;
for (size_t ii = 0; ii < m->group_count; ii++) {
const fastObjGroup *grp = &m->groups[ii];
for (unsigned int jj = 0; jj < grp->face_count; jj++) {
unsigned int fv = m->face_vertices[grp->face_offset + jj];
for (unsigned int kk = 0; kk < fv; kk++) {
/* position index */
fastObjIndex mi = m->indices[grp->index_offset + c];
uint32_t mip = m->indices[grp->index_offset + c].p - 1; /* make index start from zero */
/* flag if we've seen the index*/
int index_seen = 0;
for (size_t i = 0; i < c; i++) {
if (mip == s.indices[i]) {
index_seen = 1;
break;
}
}
s.indices[c] = mip;
if (!index_seen) {
int index = mi.p - 1; /* zero indexed */
if (mi.p) {
s.vertices[index].pos.x = m->positions[3 * mi.p + 0];
s.vertices[index].pos.y = m->positions[3 * mi.p + 1];
s.vertices[index].pos.z = m->positions[3 * mi.p + 2];
}
if (mi.t) {
s.vertices[index].texCoord.x = m->texcoords[2 * mi.t + 0];
s.vertices[index].texCoord.y = 1.0f - m->texcoords[2 * mi.t + 1];
}
if (mi.n) {
s.vertices[index].normal.x = m->normals[3 * mi.n + 0];
s.vertices[index].normal.y = m->normals[3 * mi.n + 1];
s.vertices[index].normal.z = m->normals[3 * mi.n + 2];
}
/* If not seen, incremet vertices */
s.vertices_count++;
}
c++;
}
}
}
vk_log(VK_INFO, "[obj] %s: Loaded %ld vertices %ld indices\n", s.model_path, s.vertices_count, s.indices_count);
fast_obj_destroy(m);
}
void load_model_gltf() {
// TODO maybe copy the raylib implemenetation
char * path = "assets/monkey.glb";
cgltf_options options;
memset(&options, 0, sizeof(cgltf_options));
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, path, &data);
if (result == cgltf_result_success)
result = cgltf_load_buffers(&options, data, path);
else {
vk_log(VK_ERROR, "Can't load %s\n", path);
}
if (result == cgltf_result_success)
result = cgltf_validate(data);
if (data->meshes_count < 1) {
cgltf_free(data);
return;
}
for (cgltf_size i = 0; i < data->meshes_count; ++i) {
cgltf_mesh *mesh = &data->meshes[i];
for (cgltf_size j = 0; j < mesh->primitives_count; ++j) {
cgltf_primitive* primitive = &mesh->primitives[j];
cgltf_accessor* index_accessor = primitive->indices;
if (index_accessor) {
s.indices_count = index_accessor->count;
s.indices = (uint32_t *)malloc(s.indices_count * sizeof(uint32_t));
// Read indices
for (cgltf_size k = 0; k < index_accessor->count; ++k) {
s.indices[k] = cgltf_accessor_read_index(index_accessor, k);
}
}
// Find the position attribute accessor
for (cgltf_size k = 0; k < primitive->attributes_count; ++k) {
if (primitive->attributes[k].type == cgltf_attribute_type_position) {
cgltf_accessor* position_accessor = primitive->attributes[k].data;
s.vertices_count = position_accessor->count;
s.vertices = (Vertex *)malloc(s.vertices_count * sizeof(Vertex));
// Read vertex positions
for (cgltf_size k = 0; k < position_accessor->count; ++k) {
cgltf_accessor_read_float(position_accessor, k, (cgltf_float *)(&s.vertices[k].pos), 3);
}
}
if (primitive->attributes[k].type == cgltf_attribute_type_texcoord) {
cgltf_accessor* texcoord_accessor = primitive->attributes[k].data;
// Read texture coordinates
for (cgltf_size k = 0; k < texcoord_accessor->count; ++k) {
cgltf_accessor_read_float(texcoord_accessor, k, (cgltf_float *)(&s.vertices[k].texCoord), 2);
}
}
if (primitive->attributes[k].type == cgltf_attribute_type_normal) {
cgltf_accessor* color_accessor = primitive->attributes[k].data;
// Read texture coordinates
for (cgltf_size k = 0; k < color_accessor->count; ++k) {
cgltf_accessor_read_float(color_accessor, k, (cgltf_float *)(&s.vertices[k].normal), 3);
}
}
}
}
}
vk_log(VK_INFO, "[glTF] %s: Loaded %ld vertices %ld indices\n", path, s.vertices_count, s.indices_count);
cgltf_free(data);
}
float rand_float() {
return rand() / (float)RAND_MAX;
}
void vulkan_create_compute_stuff()
{
shaderc_compilation_result_t comp_result = load_compile_shader_data("src/shader.comp", shaderc_compute_shader);
if (!comp_result) {
vk_log(VK_ERROR, "Can't load compupte shader\n");
if (s.prev_comp_result) {
comp_result = s.prev_comp_result;
}
}
if (s.prev_comp_result && comp_result != s.prev_comp_result) shaderc_result_release(s.prev_comp_result);
s.prev_comp_result = comp_result;
const char * comp_data = shaderc_result_get_bytes(comp_result);
long comp_size = shaderc_result_get_length(comp_result);
VkShaderModule compShaderModule = createShaderModule(comp_data, comp_size);
VkPipelineShaderStageCreateInfo computeShaderStageInfo = {0};
computeShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
computeShaderStageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
computeShaderStageInfo.module = compShaderModule;
computeShaderStageInfo.pName = "main";
srand((unsigned int)time(NULL));
Particle particles[PARTICLE_COUNT];
int width, height;
SDL_GetWindowSize(s.vk.window, &width, &height);
for (int i = 0; i < PARTICLE_COUNT; i++) {
float r = 0.25f * sqrtf(rand_float());
float theta = rand_float() * 2 * 3.14159265358979323846;
float x = r * cosf(theta) * height / width;
float y = r * sinf(theta);
particles[i].position[0] = x;
particles[i].position[1] = y;
vec2 norm;
glm_vec2_copy((vec2){x, y}, norm);
glm_vec2_normalize(norm);
particles[i].velocity[0] = norm[0] * 0.00025f;
particles[i].velocity[1] = norm[1] * 0.00025f;
particles[i].color[0] = rand_float();
particles[i].color[1] = rand_float();
particles[i].color[2] = rand_float();
particles[i].color[3] = 1.0f;
}
VkDeviceSize bufferSize = sizeof(Particle) * PARTICLE_COUNT;
vks_buffer stagingBuffer = {0};
vks_create_buffer(s.vk, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffer);
void* data;
vkMapMemory(s.vk.device, stagingBuffer.memory, 0, bufferSize, 0, &data);
memcpy(data, particles, (size_t)bufferSize);
vkUnmapMemory(s.vk.device, stagingBuffer.memory);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vks_create_buffer(s.vk, bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &s.frames[i].shader_storage_buffer);
// Copy data from the staging buffer (host) to the shader storage buffer (GPU)
vks_copy_buffer(s.vk, stagingBuffer.handle, s.frames[i].shader_storage_buffer.handle, bufferSize);
}
VkDescriptorSetLayoutBinding layoutBindings[3];
layoutBindings[0].binding = 0;
layoutBindings[0].descriptorCount = 1;
layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layoutBindings[0].pImmutableSamplers = NULL;
layoutBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layoutBindings[1].binding = 1;
layoutBindings[1].descriptorCount = 1;
layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
layoutBindings[1].pImmutableSamplers = NULL;
layoutBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layoutBindings[2].binding = 2;
layoutBindings[2].descriptorCount = 1;
layoutBindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
layoutBindings[2].pImmutableSamplers = NULL;
layoutBindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
VkDescriptorSetLayoutCreateInfo layoutInfo = {0};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = 3;
layoutInfo.pBindings = layoutBindings;
VK_CHECK(vkCreateDescriptorSetLayout(s.vk.device, &layoutInfo, NULL, &s.vk_compute_descriptor_set_layout));
VkComputePipelineCreateInfo pipelineInfo = {0};
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
pipelineInfo.layout = s.vk_compute_pipeline_layout;
pipelineInfo.stage = computeShaderStageInfo;
VK_CHECK(vkCreateComputePipelines(s.vk.device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &s.vk_compute_pipeline));
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {0};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &s.vk_compute_descriptor_set_layout;
VK_CHECK(vkCreatePipelineLayout(s.vk.device, &pipelineLayoutInfo, NULL, &s.vk_compute_pipeline_layout));
if (s.prev_comp_result != comp_result) {
shaderc_result_release(comp_result);
}
vkDestroyShaderModule(s.vk.device, compShaderModule, NULL);
}
void
init_vulkan()
{
vk_log(VK_WARN, "====================================\n");
vk_log(VK_WARN, " DEBUG ON \n");
vk_log(VK_WARN, "====================================\n");
vks_create_vulkan_context(&s.vk);
vulkan_create_descriptor_set_layout();
vulkan_create_graphics_pipeline(s.polygon_mode);
//vulkan_create_compute_stuff();
vulkan_create_texture_image();
vulkan_create_texture_sampler();
init_imgui(&s.vk);
load_model_obj();
//load_model_gltf();
vulkan_create_vertex_buffer();
vulkan_create_index_buffer();
vulkan_create_uniform_buffers();
vulkan_create_descriptor_pool();
vulkan_create_descriptor_sets();
vulkan_create_command_buffer();
vulkan_create_sync_objects();
}
void
close_vulkan()
{
vkDeviceWaitIdle(s.vk.device);
// Cleanup
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(s.vk.device, s.frames[i].image_available_semaphore, NULL);
vkDestroySemaphore(s.vk.device, s.frames[i].render_finished_semaphore, NULL);
vkDestroyFence(s.vk.device, s.frames[i].in_flight_fence, NULL);
}
vkDestroyCommandPool(s.vk.device, s.vk.command_pool, NULL);
vks_cleanup_swapchain(s.vk);
imgui_destroy(s.vk);
vkDestroySampler(s.vk.device, s.vk_texture_sampler, NULL);
vkDestroyImageView(s.vk.device, s.texture_image.view, NULL);
vmaDestroyImage(s.vk.allocator, s.texture_image.handle, s.texture_image.allocation);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vmaDestroyBuffer(s.vk.allocator, s.frames[i].uniform_buffer.handle, s.frames[i].uniform_buffer.allocation);
}
vkDestroyDescriptorPool(s.vk.device, s.vk_descriptor_pool, NULL);
vkDestroyDescriptorSetLayout(s.vk.device, s.vk_descriptor_set_layout, NULL);
vmaDestroyBuffer(s.vk.allocator, s.vertex_buffer.handle, s.vertex_buffer.allocation);
vmaDestroyBuffer(s.vk.allocator, s.index_buffer.handle, s.index_buffer.allocation);
vkDestroyPipeline(s.vk.device, s.graphics_pipeline.handle, NULL);
vkDestroyPipelineLayout(s.vk.device, s.graphics_pipeline.layout, NULL);
vmaDestroyAllocator(s.vk.allocator);
vkDestroyDevice(s.vk.device, NULL);
vkDestroySurfaceKHR(s.vk.instance, s.vk.surface, NULL);
/* if (enable_validation_layers) { */
/* DestroyDebugUtilsMessengerEXT(s.vk.instance, s.vk_debug_messenger, NULL); */
/* } */
vkDestroyInstance(s.vk.instance, NULL);
if (s.prev_vert_result) {
shaderc_result_release(s.prev_vert_result);
}
if (s.prev_frag_result) {
shaderc_result_release(s.prev_frag_result);
}
free(s.vertices);
free(s.indices);
}
float
current_time()
{
static struct timespec startTime;
static int isStartTimeInitialized = 0;
if (!isStartTimeInitialized) {
clock_gettime(CLOCK_MONOTONIC, &startTime);
isStartTimeInitialized = 1;
}
struct timespec currentTime;
clock_gettime(CLOCK_MONOTONIC, ¤tTime);
return (currentTime.tv_sec - startTime.tv_sec) +
(currentTime.tv_nsec - startTime.tv_nsec) / 1e9f;
}
void
updateUniformBuffer(uint32_t currentImage, float dt)
{
UniformBufferObject ubo = {0};
ubo.time = current_time();
ubo.dt = dt;
ubo.resolution[0] = s.vk.swapchain.extent.width;
ubo.resolution[1] = s.vk.swapchain.extent.height;
//glm_mat4_identity(ubo.model);
glm_mat4_dup(s.ubo.model, ubo.model);
if (s.rotate)
glm_rotate(ubo.model, glm_rad(30 * dt * glm_rad(90.0f)), GLM_ZUP);
glm_mat4_dup(ubo.model, s.ubo.model);
/* vec3 eye = GLM_VEC3_ONE_INIT; */
/* vec3 center = GLM_VEC3_ZERO_INIT; */
// vk_log(VK_INFO, "{%.2f, %.2f, %.2f}\n", s.camera.front[0], s.camera.front[1], s.camera.front[2]);
/* glm_vec3_add(s.camera.pos, s.camera.front, center); */
glm_lookat(s.camera.pos, s.camera.front, s.camera.up, ubo.view);
/* glm_lookat(eye, center, GLM_ZUP, ubo.view); */
float aspect = s.vk.swapchain.extent.width / (float)s.vk.swapchain.extent.height;
glm_perspective(glm_rad(45.0f + s.zoom ), aspect, 0.1f, 100.0f, ubo.proj);
// Inverting the Y axis for Vulkan
ubo.proj[1][1] *= -1;
memcpy(s.frames[currentImage].uniform_buffer_mapped, &ubo, sizeof(ubo));
}
float prev_time = 0;
void
draw_frame()
{
float time = current_time();
float dt = time - prev_time;
vkWaitForFences(s.vk.device, 1, &s.frames[currentFrame].in_flight_fence, VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
VkResult result = vkAcquireNextImageKHR(s.vk.device, s.vk.swapchain.handle, UINT64_MAX, s.frames[currentFrame].image_available_semaphore, VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
vks_recreate_swapchain(&s.vk);
return;
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
vk_log(VK_ERROR, "failed to acquire swap chain image!\n");
}
updateUniformBuffer(currentFrame, dt);
vkResetFences(s.vk.device, 1, &s.frames[currentFrame].in_flight_fence);
// both could work
//vkResetCommandPool(s.vk.device, s.vk_command_pool, VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT);
vkResetCommandBuffer(s.frames[currentFrame].vk_command_buffer, 0);
recordCommandBuffer(s.frames[currentFrame].vk_command_buffer, imageIndex);
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
VkSubmitInfo submitInfo = {0};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &s.frames[currentFrame].image_available_semaphore;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &s.frames[currentFrame].vk_command_buffer;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &s.frames[currentFrame].render_finished_semaphore;
VK_CHECK(vkQueueSubmit(s.vk.graphics_and_compute_queue, 1, &submitInfo, s.frames[currentFrame].in_flight_fence));
VkSwapchainKHR swapChains[] = {s.vk.swapchain.handle};
VkPresentInfoKHR presentInfo = {0};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &s.frames[currentFrame].render_finished_semaphore;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
presentInfo.pResults = NULL;
result = vkQueuePresentKHR(s.vk.present_queue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || s.sdl_window_resized) {
s.sdl_window_resized = 0;
vks_recreate_swapchain(&s.vk);
} else if (result != VK_SUCCESS) {
vk_log(VK_ERROR, "failed to present swap chain image!\n");
}
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
prev_time = time;
}
int
main(int argc, char* argv[])
{
(void) argc;
(void)argv;
init_state(&s);
if (!init()) {
vk_log(VK_INFO, "Failed to initialize!\n");
abort();
}
init_vulkan();
bool quit = false;
// At the end, don't forget to:
update_camera(0, 0);
// Game loop
while (!quit) {
handle_input(&quit);
imgui_new_frame(s.vk);
draw_frame();
//SDL_Delay(16);
}
close_vulkan();
closeSDL();
return 0;
}