diff --git a/Makefile b/Makefile index e48d25f..8e1e466 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -LDFLAGS = -lglfw -lvulkan -ldl -lpthread +LDFLAGS = -lglfw -lvulkan -ldl -lpthread -lm CFLAGS = -g -pedantic -Wall -Wextra -Wshadow -Wunused-macros diff --git a/main.c b/main.c index aad6d9a..579eb31 100644 --- a/main.c +++ b/main.c @@ -2,32 +2,33 @@ #include #define VK_USE_PLATFORM_WAYLAND_KHR #define GLFW_INCLUDE_VULKAN -#include #define GLFW_EXPOSE_NATIVE_WAYLAND +#include #include #include #include +#include +#include -enum bool { +typedef enum bool { false = 0, true = 1, -}; -typedef int bool; +} bool; -const uint32_t WIDTH = 800; -const uint32_t HEIGHT = 600; +static const uint32_t WIDTH = 800; +static const uint32_t HEIGHT = 600; #define MAX_FRAMES_IN_FLIGHT 2 -const char* VALIDATION_LAYERS[] = { "VK_LAYER_KHRONOS_validation" }; +static const char* VALIDATION_LAYERS[] = { "VK_LAYER_KHRONOS_validation" }; #define VALIDATION_LAYER_COUNT 1 -const char* DEVICE_EXTENSIONS[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; +static const char* DEVICE_EXTENSIONS[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; #define DEVICE_EXTENSION_COUNT 1 #ifdef NDEBUG - const bool enableValidationLayers = false; + static const bool enableValidationLayers = false; #else - const bool enableValidationLayers = true; + static const bool enableValidationLayers = true; #endif typedef struct Vec2 { @@ -41,13 +42,33 @@ typedef struct Vec3 { float z; } Vec3; +typedef struct Vec4 { + float x; + float y; + float z; + float w; +} Vec4; + +typedef struct Mat4 { + Vec4 x; + Vec4 y; + Vec4 z; + Vec4 w; +} Mat4; + +struct UniformBufferObject { + Mat4 model; + Mat4 view; + Mat4 proj; +} UniformBufferObject; + typedef struct Vertex { Vec2 pos; Vec3 color; } Vertex; #define VERTEX_COUNT 4 -const Vertex vertices[VERTEX_COUNT] = { +static const Vertex vertices[VERTEX_COUNT] = { {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, @@ -55,7 +76,7 @@ const Vertex vertices[VERTEX_COUNT] = { }; #define INDEX_COUNT 6 -const uint16_t indices[INDEX_COUNT] = { +static const uint16_t indices[INDEX_COUNT] = { 0, 1, 2, 2, 3, 0 }; @@ -74,6 +95,7 @@ struct VulkanData { VkExtent2D swapChainExtent; VkImageView swapChainImageViews[4]; int imageCount; + VkDescriptorSetLayout descriptorSetLayout; VkPipelineLayout pipelineLayout; VkRenderPass renderPass; VkPipeline graphicsPipeline; @@ -90,6 +112,11 @@ struct VulkanData { VkDeviceMemory vertexBufferMemory; VkBuffer indexBuffer; VkDeviceMemory indexBufferMemory; + VkBuffer uniformBuffers[MAX_FRAMES_IN_FLIGHT]; + VkDeviceMemory uniformBuffersMemory[MAX_FRAMES_IN_FLIGHT]; + void* uniformBuffersMapped[MAX_FRAMES_IN_FLIGHT]; + VkDescriptorPool descriptorPool; + VkDescriptorSet descriptorSets[MAX_FRAMES_IN_FLIGHT]; }; struct SwapChainSupportDetails { @@ -109,6 +136,73 @@ struct QueueFamilyIndices { struct Optional transferFamily; }; +static Vec3 normalize(Vec3 v) { + float size = sqrt(v.x * v.x + v.y * v.y + v.z * v.z); + Vec3 result = { + v.x / size, + v.y / size, + v.z / size, + }; + return result; +} + +static Vec3 crossProduct(Vec3 a, Vec3 b) { + Vec3 result = { + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x, + }; + return result; +} + +static float dotProduct(Vec3 a, Vec3 b) { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +static Mat4 rotate(float angle, Vec3 v) { + float c = cos(angle); + float s = sin(angle); + float x = v.x, y = v.y, z = v.z; + Mat4 result = { + {c + x * x * (1. - c), x * y * (1. - c) - z * s, x * z * (1. - c) + y * s, 0.}, + {x * y * (1. - c) + z * s, c + y * y * (1. - c), y * z * (1. - c) - x * s, 0.}, + {x * z * (1. - c) - y * s, y * z * (1. - c) + x * s, c + z * z * (1. - c), 0.}, + {0., 0., 0., 1.} + }; + return result; +} + +static Mat4 lookAt(Vec3 eye, Vec3 center, Vec3 up) { + Vec3 f = { + center.x - eye.x, + center.y - eye.y, + center.z - eye.z, + }; + f = normalize(f); + up = normalize(up); + Vec3 s = normalize(crossProduct(f, up)); + Vec3 u = crossProduct(s, f); + + Mat4 mat = { + {s.x, u.x, -f.x, 0.}, + {s.y, u.y, -f.y, 0.}, + {s.z, u.z, -f.z, 0.}, + {-dotProduct(s, eye), -dotProduct(u, eye), dotProduct(f, eye), 1.}, + }; + return mat; +} + +static Mat4 perspective(float angle, float aspectRatio, float near, float far) { + float f = cos(angle / 2.) / sin(angle / 2.); + Mat4 mat = { + {f / aspectRatio, 0., 0., 0.}, + {0., f, 0., 0.}, + {0., 0., (far + near)/(near - far), -1.}, + {0., 0., (2. * far * near)/(near - far), 0.}, + }; + return mat; +} + static VkVertexInputBindingDescription getBindingDescription() { VkVertexInputBindingDescription bindingDescription = { .binding = 0, @@ -141,10 +235,11 @@ GLFWwindow* initWindow(struct VulkanData* data) { glfwMakeContextCurrent(window); glfwSetWindowUserPointer(window, data); glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); + GLFWcursor* cursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + glfwSetCursor(window, cursor); return window; } -// INDIRECTION: main->run->initVulkan->createInstance static bool checkValidationLayerSupport() { uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, NULL); @@ -172,7 +267,6 @@ static bool checkValidationLayerSupport() { return true; } -// INDIRECTION: main->run->initVulkan->createInstance static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -186,7 +280,6 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( return VK_FALSE; } -// INDIRECTION: main->run->initVulkan static void createInstance(struct VulkanData* data) { if (enableValidationLayers && !checkValidationLayerSupport()) { printf("ERROR: Validation layers requested, but not available\n"); @@ -272,7 +365,6 @@ VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMes return func(instance, pCreateInfo, pAllocator, pDebugMessenger); } - static void setupDebugMessenger(struct VulkanData* data) { if (!enableValidationLayers) return; @@ -290,16 +382,7 @@ static void setupDebugMessenger(struct VulkanData* data) { } static void createSurface(struct VulkanData* data, GLFWwindow* window) { - VkWaylandSurfaceCreateInfoKHR createInfo = { - .sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, - .display = glfwGetWaylandDisplay(), - .surface = glfwGetWaylandWindow(window), - }; - // if (glfwCreateWindowSurface(data->instance, window, NULL, &surface) != VK_SUCCESS) { - // fprintf(stderr, "ERROR: Failed to create window surface\n"); - // exit(1); - // } - if (vkCreateWaylandSurfaceKHR(data->instance, &createInfo, NULL, &data->surface) != VK_SUCCESS) { + if (glfwCreateWindowSurface(data->instance, window, NULL, &data->surface) != VK_SUCCESS) { fprintf(stderr, "ERROR: Failed to create window surface\n"); exit(1); } @@ -318,7 +401,6 @@ static uint32_t clamp(uint32_t value, uint32_t min, uint32_t max) { return value; } - static int checkDeviceExtensionSupport(VkPhysicalDevice device) { uint32_t extensionCount; vkEnumerateDeviceExtensionProperties(device, NULL, &extensionCount, NULL); @@ -785,8 +867,9 @@ static void createGraphicsPipeline(struct VulkanData* data) { .rasterizerDiscardEnable = VK_FALSE, .polygonMode = VK_POLYGON_MODE_FILL, .lineWidth = 1.0f, - .cullMode = VK_CULL_MODE_BACK_BIT, - .frontFace = VK_FRONT_FACE_CLOCKWISE, + // .cullMode = VK_CULL_MODE_BACK_BIT, + .cullMode = 0, + .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .depthBiasEnable = VK_FALSE, .depthBiasConstantFactor = 0.0f, // Optional .depthBiasClamp = 0.0f, // Optional @@ -828,8 +911,8 @@ static void createGraphicsPipeline(struct VulkanData* data) { VkPipelineLayoutCreateInfo pipelineLayoutInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 0, // Optional - .pSetLayouts = NULL, // Optional + .setLayoutCount = 1, + .pSetLayouts = &data->descriptorSetLayout, .pushConstantRangeCount = 0, // Optional .pPushConstantRanges = NULL, // Optional }; @@ -1026,6 +1109,7 @@ static void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageInd .extent = data->swapChainExtent, }; vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, data->pipelineLayout, 0, 1, &data->descriptorSets[data->currentFrame], 0, NULL); vkCmdDrawIndexed(commandBuffer, INDEX_COUNT, 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffer); if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { @@ -1060,7 +1144,31 @@ static void recreateSwapChain(struct VulkanData* data, GLFWwindow* window) { createFramebuffers(data); } -static void drawFrame(struct VulkanData* data, GLFWwindow* window) { +static void updateUniformBuffer(uint32_t currentImage, struct VulkanData* data, struct timespec* start) { + struct timespec end; + + if (clock_gettime(CLOCK_MONOTONIC, &end) != 0) { + perror("clock_gettime"); + exit(1); + } + + double elapsed = (end.tv_sec - start->tv_sec) + (end.tv_nsec - start->tv_nsec) / 1e9; + + Vec3 v1 = { 2., 2., 2. }; + Vec3 v2 = { 0., 0., 0. }; + Vec3 v3 = { 0., 0., 1. }; + + struct UniformBufferObject ubo = { + .model = rotate(elapsed * M_PI / 2., v3), + .view = lookAt(v1, v2, v3), + .proj = perspective(M_PI / 4., data->swapChainExtent.width / (float) data->swapChainExtent.height, 0.1, 10.), + }; + + ubo.proj.y.y *= -1.; + memcpy(data->uniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); +} + +static void drawFrame(struct VulkanData* data, GLFWwindow* window, struct timespec* start) { vkWaitForFences(data->device, 1, &data->inFlightFences[data->currentFrame], VK_TRUE, UINT64_MAX); uint32_t imageIndex; @@ -1081,6 +1189,8 @@ static void drawFrame(struct VulkanData* data, GLFWwindow* window) { VkSemaphore waitSemaphores[] = {data->imageAvailableSemaphores[data->currentFrame]}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkSemaphore signalSemaphores[] = {data->renderFinishedSemaphores[data->currentFrame]}; + + updateUniformBuffer(data->currentFrame, data, start); VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, @@ -1255,33 +1365,126 @@ static void createVertexBuffer(struct VulkanData* data) { vkFreeMemory(data->device, stagingBufferMemory, NULL); } -static struct VulkanData initVulkan(GLFWwindow* window) { - struct VulkanData data; - data.currentFrame = 0; - data.framebufferResized = 0; - createInstance(&data); - setupDebugMessenger(&data); - createSurface(&data, window); - pickPhysicalDevice(&data); - createLogicalDevice(&data); - createSwapChain(&data, window); - createImageViews(&data); - createRenderPass(&data); - createGraphicsPipeline(&data); - createFramebuffers(&data); - createCommandPool(&data); - createIndexBuffer(&data); - createVertexBuffer(&data); - createCommandBuffers(&data); - createSyncObjects(&data); - return data; +static void createDescriptorSetLayout(struct VulkanData* data) { + VkDescriptorSetLayoutBinding uboLayoutBinding = { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .pImmutableSamplers = NULL, + }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = 1, + .pBindings = &uboLayoutBinding, + }; + + if (vkCreateDescriptorSetLayout(data->device, &layoutInfo, NULL, &data->descriptorSetLayout) != VK_SUCCESS) { + fprintf(stderr, "ERROR: Failed to create descriptor set layout\n"); + exit(1); + } } -static void mainLoop(GLFWwindow* window, struct VulkanData* data) { +static void createUniformBuffers(struct VulkanData* data) { + VkDeviceSize bufferSize = sizeof(UniformBufferObject); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &data->uniformBuffers[i], &data->uniformBuffersMemory[i], data); + vkMapMemory(data->device, data->uniformBuffersMemory[i], 0, bufferSize, 0, &data->uniformBuffersMapped[i]); + } +} + +static void createDescriptorPool(struct VulkanData* data) { + VkDescriptorPoolSize poolSize = { + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = MAX_FRAMES_IN_FLIGHT, + }; + + VkDescriptorPoolCreateInfo poolInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .poolSizeCount = 1, + .pPoolSizes = &poolSize, + .maxSets = MAX_FRAMES_IN_FLIGHT, + .flags = 0, + }; + + + if (vkCreateDescriptorPool(data->device, &poolInfo, NULL, &data->descriptorPool) != VK_SUCCESS) { + fprintf(stderr, "ERROR: Failed to create descriptor pool\n"); + exit(1); + } +} + +static void createDescriptorSets(struct VulkanData* data) { + VkDescriptorSetLayout layouts[MAX_FRAMES_IN_FLIGHT]; + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + layouts[i] = data->descriptorSetLayout; + } + VkDescriptorSetAllocateInfo allocInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = data->descriptorPool, + .descriptorSetCount = MAX_FRAMES_IN_FLIGHT, + .pSetLayouts = layouts, + }; + + if (vkAllocateDescriptorSets(data->device, &allocInfo, data->descriptorSets) != VK_SUCCESS) { + fprintf(stderr, "ERROR: Failed to allocate descriptor sets\n"); + exit(1); + } + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo = { + .buffer = data->uniformBuffers[i], + .offset = 0, + .range = sizeof(UniformBufferObject), + }; + + VkWriteDescriptorSet descriptorWrite = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = data->descriptorSets[i], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .pBufferInfo = &bufferInfo, + .pImageInfo = NULL, + .pTexelBufferView = NULL, + }; + + vkUpdateDescriptorSets(data->device, 1, &descriptorWrite, 0, NULL); + } +} + +static void initVulkan(GLFWwindow* window, struct VulkanData* data) { + data->currentFrame = 0; + data->framebufferResized = 0; + createInstance(data); + setupDebugMessenger(data); + createSurface(data, window); + pickPhysicalDevice(data); + createLogicalDevice(data); + createSwapChain(data, window); + createImageViews(data); + createRenderPass(data); + createDescriptorSetLayout(data); + createGraphicsPipeline(data); + createFramebuffers(data); + createCommandPool(data); + createVertexBuffer(data); + createIndexBuffer(data); + createUniformBuffers(data); + createDescriptorPool(data); + createDescriptorSets(data); + createCommandBuffers(data); + createSyncObjects(data); +} + +static void mainLoop(GLFWwindow* window, struct VulkanData* data, struct timespec* start) { while (!glfwWindowShouldClose(window)) { glfwSwapBuffers(window); glfwPollEvents(); - drawFrame(data, window); + drawFrame(data, window, start); int state = glfwGetKey(window, GLFW_KEY_Q); if (state == GLFW_PRESS) { break; @@ -1293,6 +1496,13 @@ static void mainLoop(GLFWwindow* window, struct VulkanData* data) { static void cleanup(GLFWwindow* window, struct VulkanData* data) { cleanupSwapChain(data); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroyBuffer(data->device, data->uniformBuffers[i], NULL); + vkFreeMemory(data->device, data->uniformBuffersMemory[i], NULL); + } + vkDestroyDescriptorPool(data->device, data->descriptorPool, NULL); + vkDestroyDescriptorSetLayout(data->device, data->descriptorSetLayout, NULL); vkDestroyBuffer(data->device, data->vertexBuffer, NULL); vkFreeMemory(data->device, data->vertexBufferMemory, NULL); vkDestroyBuffer(data->device, data->indexBuffer, NULL); @@ -1320,9 +1530,15 @@ static void cleanup(GLFWwindow* window, struct VulkanData* data) { static void run() { struct VulkanData data; + printf("INFO: Size of data struct: %ld bytes\n", sizeof(data)); GLFWwindow* window = initWindow(&data); - data = initVulkan(window); - mainLoop(window, &data); + initVulkan(window, &data); + struct timespec start; + if (clock_gettime(CLOCK_MONOTONIC, &start) != 0) { + perror("clock_gettime"); + exit(1); + } + mainLoop(window, &data, &start); cleanup(window, &data); } diff --git a/shaders/shader.vert b/shaders/shader.vert index 9f27f54..733e31f 100644 --- a/shaders/shader.vert +++ b/shaders/shader.vert @@ -5,7 +5,13 @@ layout(location = 1) in vec3 inColor; layout(location = 0) out vec3 fragColor; +layout(binding = 0) uniform UniformBufferObject { + mat4 model; + mat4 view; + mat4 proj; +} ubo; + void main() { - gl_Position = vec4(inPosition, 0.0, 1.0); + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); fragColor = inColor; }