diff --git a/.gitignore b/.gitignore index 3fe3d16..9633d97 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ topaz .cache **/*.BAK **/*.bak +**/*.spv **/*~ diff --git a/assets/shaders/quad.frag b/assets/shaders/quad.frag new file mode 100644 index 0000000..c3de146 --- /dev/null +++ b/assets/shaders/quad.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) in vec3 frag_color; + +layout(location = 0) out vec4 out_color; + +void main() +{ + out_color = vec4(frag_color, 1.0); +} diff --git a/assets/shaders/quad.vert b/assets/shaders/quad.vert new file mode 100644 index 0000000..45d2fcd --- /dev/null +++ b/assets/shaders/quad.vert @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) in vec2 in_position; +layout(location = 1) in vec3 in_color; + +layout(location = 0) out vec3 frag_color; + +void main() +{ + gl_Position = vec4(in_position, 0.0, 1.0); + frag_color = in_color; +} diff --git a/config.mk b/config.mk index b98bdfb..490d526 100644 --- a/config.mk +++ b/config.mk @@ -4,7 +4,7 @@ CC := cc CFLAGS := -Wall -Wextra -std=c99 -pedantic LIBS := -lm # Can be gl or vk -GRAPHICS_BACKEND := vk +GRAPHICS_BACKEND := gl DEBUG_BUILD=1 PLATFORM := $(shell uname) diff --git a/makefile b/makefile index 516bfde..bd71973 100644 --- a/makefile +++ b/makefile @@ -5,6 +5,10 @@ ifeq (${DEBUG_BUILD},1) CFLAGS += -ggdb -fsanitize=address,undefined -DDEBUG endif +SHADER_DIR := assets/shaders +SHADER_SRC := $(wildcard $(SHADER_DIR)/*.vert) $(wildcard $(SHADER_DIR)/*.frag) +SHADER_SPV := $(SHADER_SRC:%=%.spv) + SRC:=\ topaz.c\ core/linear.c\ @@ -22,7 +26,14 @@ ifeq (${GRAPHICS_BACKEND},vk) rendering/vk/instance.c\ rendering/vk/physical_device.c\ rendering/vk/device.c\ - rendering/vk/surface.c + rendering/vk/surface.c\ + rendering/vk/swapchain.c\ + rendering/vk/renderpass.c\ + rendering/vk/pipeline.c\ + rendering/vk/framebuffer.c\ + rendering/vk/command.c\ + rendering/vk/sync.c\ + rendering/vk/buffer.c ifeq (${PLATFORM},Darwin) SRC += rendering/vk/macos_platform.c @@ -32,15 +43,20 @@ endif OBJ:=${SRC:.c=.o} -all: ${OBJ} +all: shaders ${OBJ} ${CC} ${LIBS} ${CFLAGS} ${OBJ} -o topaz +shaders: ${SHADER_SPV} + +$(SHADER_DIR)/%.spv: $(SHADER_DIR)/% + glslc $< -o $@ + %.o: %.c ${CC} ${CFLAGS} -c $< -o $@ -.PHONY: clean +.PHONY: clean shaders clean: - @rm -rfv ${OBJ} topaz + @rm -rfv ${OBJ} ${SHADER_SPV} topaz INDENTABLE := $(shell find . -name "*.c" -o -name "*.h") INDENTABLE := $(filter-out ./gl/gl.h,$(INDENTABLE)) diff --git a/rendering/gl/context.h b/rendering/gl/context.h new file mode 100644 index 0000000..ba84e88 --- /dev/null +++ b/rendering/gl/context.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#ifndef GL_CONTEXT_H +#define GL_CONTEXT_H + +#include "gl.h" +#include "../../core/types.h" +#define RGFW_OPENGL +#ifndef RGFW_IMPLEMENTATION +#define RGFW_IMPORT +#endif +#include "../../rgfw.h" + +struct renderer_context { + RGFW_window *window; + GLuint shader_program; + GLuint vao; + GLuint vbo; + GLuint ebo; +}; + +#endif diff --git a/rendering/gl/platform.c b/rendering/gl/platform.c index ae018dd..54ee43f 100644 --- a/rendering/gl/platform.c +++ b/rendering/gl/platform.c @@ -1,51 +1,53 @@ /* SPDX-License-Identifier:BSD-3-Clause */ #include "gl.h" +#define RGFW_EXPORT #define RGFW_IMPLEMENTATION #define RGFW_OPENGL -#include "../rgfw.h" +#include "../../rgfw.h" + +#include "../renderer.h" +#include "../../core/log.h" /* * This function is the entrypoint for the whole game. Its role is to * initialize OpenGL, create the renderer and start the game loop. */ -int platform_run(i32 argc, u8 * *argv) +int platform_run(i32 argc, u8 **argv) { (void)argc; (void)argv; + log_info("Using OpenGL as rendering backend.\n"); + RGFW_glHints *hints = RGFW_getGlobalHints_OpenGL(); hints->major = 3; hints->minor = 3; RGFW_setGlobalHints_OpenGL(hints); - RGFW_window *win = RGFW_createWindow("Topaz", 0, 0, 800, 600, RGFW_windowCenter | RGFW_windowNoResize | RGFW_windowHide); + RGFW_window *win = RGFW_createWindow("topaz", 0, 0, 800, 600, RGFW_windowCenter | RGFW_windowNoResize | RGFW_windowHide); RGFW_window_createContext_OpenGL(win, hints); int glad_version = gladLoadGL(RGFW_getProcAddress_OpenGL); if (glad_version == 0) { - printf("Failed to initialize OpenGL context.\n"); + log_error("Failed to initialize OpenGL context.\n"); return -1; } RGFW_window_show(win); - RGFW_window_setExitKey(win, RGFW_escape); - const u8 *version = glGetString(GL_VERSION); - printf("OpenGL Version: %s\n", version); - printf("GLAD Version: %d.%d\n", GLAD_VERSION_MAJOR(glad_version), GLAD_VERSION_MINOR(glad_version)); + log_info("OpenGL initialized.\n"); + struct renderer_context *context = renderer_context_init(win); while (RGFW_window_shouldClose(win) == RGFW_FALSE) { RGFW_event event; while (RGFW_window_checkEvent(win, &event)); - - glClearColor(0.1f, 0.1f, 0.1f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - RGFW_window_swapBuffers_OpenGL(win); + renderer_present(context); } + renderer_context_deinit(context); RGFW_window_close(win); return 0; } diff --git a/rendering/gl/renderer.c b/rendering/gl/renderer.c new file mode 100644 index 0000000..af30d4e --- /dev/null +++ b/rendering/gl/renderer.c @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#include "context.h" +#include "../renderer.h" +#include "../../core/log.h" +#include +#include + +static const char *vertex_shader_src = + "#version 330 core\n" + "layout (location = 0) in vec2 in_position;\n" + "layout (location = 1) in vec3 in_color;\n" + "out vec3 frag_color;\n" + "void main() {\n" + " gl_Position = vec4(in_position, 0.0, 1.0);\n" + " frag_color = in_color;\n" + "}\n"; + +static const char *fragment_shader_src = + "#version 330 core\n" + "in vec3 frag_color;\n" + "out vec4 out_color;\n" + "void main() {\n" + " out_color = vec4(frag_color, 1.0);\n" + "}\n"; + +static GLuint compile_shader(GLenum type, const char *source) +{ + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + char info[512]; + glGetShaderInfoLog(shader, 512, NULL, info); + log_error("Shader compilation failed: "); + printf("%s\n", info); + } + + return shader; +} + +static GLuint create_shader_program(void) +{ + GLuint vert = compile_shader(GL_VERTEX_SHADER, vertex_shader_src); + GLuint frag = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_src); + + GLuint program = glCreateProgram(); + glAttachShader(program, vert); + glAttachShader(program, frag); + glLinkProgram(program); + + GLint success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (!success) { + char info[512]; + glGetProgramInfoLog(program, 512, NULL, info); + log_error("Shader linking failed: "); + printf("%s\n", info); + } + + glDeleteShader(vert); + glDeleteShader(frag); + + return program; +} + +static void create_quad_buffers(struct renderer_context *context) +{ + f32 vertices[] = { + -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, + -0.5f, 0.5f, 1.0f, 1.0f, 1.0f + }; + + u16 indices[] = { 0, 1, 2, 2, 3, 0 }; + + glGenVertexArrays(1, &context->vao); + glGenBuffers(1, &context->vbo); + glGenBuffers(1, &context->ebo); + + glBindVertexArray(context->vao); + + glBindBuffer(GL_ARRAY_BUFFER, context->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, context->ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)0); + glEnableVertexAttribArray(0); + + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)(2 * sizeof(f32))); + glEnableVertexAttribArray(1); + + glBindVertexArray(0); + + log_info("Quad buffers created.\n"); +} + +struct renderer_context *renderer_context_init(RGFW_window *window) +{ + struct renderer_context *context = malloc(sizeof(struct renderer_context)); + context->window = window; + + context->shader_program = create_shader_program(); + log_info("Shader program created.\n"); + + create_quad_buffers(context); + + glEnable(GL_FRAMEBUFFER_SRGB); + + return context; +} + +void renderer_context_deinit(struct renderer_context *context) +{ + glDeleteVertexArrays(1, &context->vao); + glDeleteBuffers(1, &context->vbo); + glDeleteBuffers(1, &context->ebo); + glDeleteProgram(context->shader_program); + free(context); +} + +struct mesh *renderer_build_chunk_mesh(void) +{ + return NULL; +} + +void renderer_draw_mesh(struct renderer_context *context, struct mesh mesh) +{ + (void)context; + (void)mesh; +} + +void renderer_draw_chunk(struct renderer_context *context, struct mesh mesh) +{ + (void)context; + (void)mesh; +} + +void renderer_present(struct renderer_context *context) +{ + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(context->shader_program); + glBindVertexArray(context->vao); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); + + RGFW_window_swapBuffers_OpenGL(context->window); +} diff --git a/rendering/renderer.h b/rendering/renderer.h index 8ce23a2..854c9dc 100644 --- a/rendering/renderer.h +++ b/rendering/renderer.h @@ -2,11 +2,11 @@ #ifndef RENDERER_H #define RENDERER_H -#include "../types.h" -#ifdef BACKEND_VK -#define RGFW_VULKAN +#include "../core/types.h" + +#ifndef RGFW_H +typedef struct RGFW_window RGFW_window; #endif -#include "../rgfw.h" /* * A mesh is a drawable object represented as an index (offset) in the global diff --git a/rendering/vk/buffer.c b/rendering/vk/buffer.c new file mode 100644 index 0000000..17f0b70 --- /dev/null +++ b/rendering/vk/buffer.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#include "buffer.h" +#include "../../core/log.h" +#include + +static u32 find_memory_type(struct renderer_context *context, u32 type_filter, VkMemoryPropertyFlags properties) +{ + VkPhysicalDeviceMemoryProperties mem_props; + vkGetPhysicalDeviceMemoryProperties(context->physical_device, &mem_props); + + for (u32 i = 0; i < mem_props.memoryTypeCount; i++) { + if ((type_filter & (1 << i)) && + (mem_props.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + + fatal("Can't find suitable memory type.\n"); + return 0; +} + +void vk_buffer_create(struct renderer_context *context, VkDeviceSize size, + VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, + VkBuffer *buffer, VkDeviceMemory *memory) +{ + VkBufferCreateInfo buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = size, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + + if (vkCreateBuffer(context->device, &buffer_info, NULL, buffer) != VK_SUCCESS) { + fatal("Can't create buffer.\n"); + } + + VkMemoryRequirements mem_reqs; + vkGetBufferMemoryRequirements(context->device, *buffer, &mem_reqs); + + VkMemoryAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = mem_reqs.size, + .memoryTypeIndex = find_memory_type(context, mem_reqs.memoryTypeBits, properties) + }; + + if (vkAllocateMemory(context->device, &alloc_info, NULL, memory) != VK_SUCCESS) { + fatal("Can't allocate buffer memory.\n"); + } + + vkBindBufferMemory(context->device, *buffer, *memory, 0); +} + +void vk_buffer_destroy(struct renderer_context *context, VkBuffer buffer, VkDeviceMemory memory) +{ + vkDestroyBuffer(context->device, buffer, NULL); + vkFreeMemory(context->device, memory, NULL); +} + +void vk_quad_buffers_init(struct renderer_context *context) +{ + /* Quad vertices: position (x, y) + color (r, g, b) */ + f32 vertices[] = { + -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, + -0.5f, 0.5f, 1.0f, 1.0f, 1.0f + }; + + /* Indices for two triangles forming a quad */ + u16 indices[] = { 0, 1, 2, 2, 3, 0 }; + + VkDeviceSize vertex_size = sizeof(vertices); + VkDeviceSize index_size = sizeof(indices); + + vk_buffer_create(context, vertex_size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &context->vertex_buffer, &context->vertex_buffer_memory); + + void *data; + vkMapMemory(context->device, context->vertex_buffer_memory, 0, vertex_size, 0, &data); + memcpy(data, vertices, vertex_size); + vkUnmapMemory(context->device, context->vertex_buffer_memory); + + vk_buffer_create(context, index_size, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &context->index_buffer, &context->index_buffer_memory); + + vkMapMemory(context->device, context->index_buffer_memory, 0, index_size, 0, &data); + memcpy(data, indices, index_size); + vkUnmapMemory(context->device, context->index_buffer_memory); + + log_info("Quad buffers created.\n"); +} + +void vk_quad_buffers_deinit(struct renderer_context *context) +{ + vk_buffer_destroy(context, context->index_buffer, context->index_buffer_memory); + vk_buffer_destroy(context, context->vertex_buffer, context->vertex_buffer_memory); +} diff --git a/rendering/vk/buffer.h b/rendering/vk/buffer.h new file mode 100644 index 0000000..9768009 --- /dev/null +++ b/rendering/vk/buffer.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#ifndef BUFFER_H +#define BUFFER_H + +#include "vk.h" + +void vk_buffer_create(struct renderer_context *context, VkDeviceSize size, + VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, + VkBuffer *buffer, VkDeviceMemory *memory); +void vk_buffer_destroy(struct renderer_context *context, VkBuffer buffer, VkDeviceMemory memory); + +void vk_quad_buffers_init(struct renderer_context *context); +void vk_quad_buffers_deinit(struct renderer_context *context); + +#endif diff --git a/rendering/vk/command.c b/rendering/vk/command.c new file mode 100644 index 0000000..f8fd559 --- /dev/null +++ b/rendering/vk/command.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#include "command.h" +#include "../../core/log.h" +#include + +void vk_command_pool_init(struct renderer_context *context) +{ + VkCommandPoolCreateInfo pool_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = context->queue_indices.graphics + }; + + if (vkCreateCommandPool(context->device, &pool_info, NULL, &context->command_pool) != VK_SUCCESS) { + fatal("Can't create command pool.\n"); + } + + log_info("Command pool created.\n"); +} + +void vk_command_pool_deinit(struct renderer_context *context) +{ + vkDestroyCommandPool(context->device, context->command_pool, NULL); +} + +void vk_command_buffers_init(struct renderer_context *context) +{ + context->command_buffers = malloc(sizeof(VkCommandBuffer) * context->swapchain.image_count); + + VkCommandBufferAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = context->command_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = context->swapchain.image_count + }; + + if (vkAllocateCommandBuffers(context->device, &alloc_info, context->command_buffers) != VK_SUCCESS) { + fatal("Can't allocate command buffers.\n"); + } + + log_info("Command buffers allocated.\n"); +} diff --git a/rendering/vk/command.h b/rendering/vk/command.h new file mode 100644 index 0000000..5cf1b3c --- /dev/null +++ b/rendering/vk/command.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#ifndef COMMAND_H +#define COMMAND_H + +#include "vk.h" + +void vk_command_pool_init(struct renderer_context *context); +void vk_command_pool_deinit(struct renderer_context *context); +void vk_command_buffers_init(struct renderer_context *context); + +#endif diff --git a/rendering/vk/device.c b/rendering/vk/device.c index 2f7ccda..a56971d 100644 --- a/rendering/vk/device.c +++ b/rendering/vk/device.c @@ -9,6 +9,8 @@ void vk_device_init(struct renderer_context *context) { struct vector *device_extensions = vector_init(0, sizeof(char *)); + vector_push(device_extensions, char *, VK_KHR_SWAPCHAIN_EXTENSION_NAME); + struct vector *physical_device_extensions = vk_physical_device_get_extensions(context); for (usize i = 0; i < physical_device_extensions->length; i++) { if (strcmp(((char **)physical_device_extensions->data)[i], "VK_KHR_portability_subset") == 0) { @@ -24,27 +26,32 @@ void vk_device_init(struct renderer_context *context) } f32 priority = 0.0; - VkDeviceQueueCreateInfo graphics_queue_create_info[2]; - graphics_queue_create_info[0] = (VkDeviceQueueCreateInfo){ + VkDeviceQueueCreateInfo queue_create_info[2]; + u32 queue_create_info_count = 0; + + queue_create_info[queue_create_info_count++] = (VkDeviceQueueCreateInfo){ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = context->queue_indices.graphics, .queueCount = 1, .pQueuePriorities = &priority }; - graphics_queue_create_info[1] = (VkDeviceQueueCreateInfo){ - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .queueFamilyIndex = context->queue_indices.present, - .queueCount = 1, - .pQueuePriorities = &priority - }; + /* Only add a second queue create info if present uses a different family */ + if (context->queue_indices.present != context->queue_indices.graphics) { + queue_create_info[queue_create_info_count++] = (VkDeviceQueueCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = context->queue_indices.present, + .queueCount = 1, + .pQueuePriorities = &priority + }; + } VkDeviceCreateInfo device_create_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = NULL, .flags = 0, - .queueCreateInfoCount = 2, - .pQueueCreateInfos = graphics_queue_create_info, + .queueCreateInfoCount = queue_create_info_count, + .pQueueCreateInfos = queue_create_info, .enabledLayerCount = 0, .ppEnabledLayerNames = NULL, .enabledExtensionCount = device_extensions->length, @@ -58,8 +65,8 @@ void vk_device_init(struct renderer_context *context) fatal("Can't create Vulkan device.\n"); } - vkGetDeviceQueue(context->device, graphics_queue_create_info[0].queueFamilyIndex, 0, &context->graphics_queue); - vkGetDeviceQueue(context->device, graphics_queue_create_info[1].queueFamilyIndex, 0, &context->present_queue); + vkGetDeviceQueue(context->device, context->queue_indices.graphics, 0, &context->graphics_queue); + vkGetDeviceQueue(context->device, context->queue_indices.present, 0, &context->present_queue); vector_deinit(physical_device_extensions); vector_deinit(device_extensions); diff --git a/rendering/vk/framebuffer.c b/rendering/vk/framebuffer.c new file mode 100644 index 0000000..e09518e --- /dev/null +++ b/rendering/vk/framebuffer.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#include "framebuffer.h" +#include "../../core/log.h" +#include + +void vk_framebuffers_init(struct renderer_context *context) +{ + context->swapchain.framebuffers = malloc(sizeof(VkFramebuffer) * context->swapchain.image_count); + + for (u32 i = 0; i < context->swapchain.image_count; i++) { + VkImageView attachments[] = { context->swapchain.image_views[i] }; + + VkFramebufferCreateInfo framebuffer_info = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = context->render_pass, + .attachmentCount = 1, + .pAttachments = attachments, + .width = context->swapchain.extent.width, + .height = context->swapchain.extent.height, + .layers = 1 + }; + + if (vkCreateFramebuffer(context->device, &framebuffer_info, NULL, &context->swapchain.framebuffers[i]) != VK_SUCCESS) { + fatal("Can't create framebuffer.\n"); + } + } + + log_info("Framebuffers created.\n"); +} + +void vk_framebuffers_deinit(struct renderer_context *context) +{ + for (u32 i = 0; i < context->swapchain.image_count; i++) { + vkDestroyFramebuffer(context->device, context->swapchain.framebuffers[i], NULL); + } + free(context->swapchain.framebuffers); +} diff --git a/rendering/vk/framebuffer.h b/rendering/vk/framebuffer.h new file mode 100644 index 0000000..206d1ea --- /dev/null +++ b/rendering/vk/framebuffer.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#ifndef FRAMEBUFFER_H +#define FRAMEBUFFER_H + +#include "vk.h" + +void vk_framebuffers_init(struct renderer_context *context); +void vk_framebuffers_deinit(struct renderer_context *context); + +#endif diff --git a/rendering/vk/macos_platform.c b/rendering/vk/macos_platform.c index 723f735..ca36ae4 100644 --- a/rendering/vk/macos_platform.c +++ b/rendering/vk/macos_platform.c @@ -4,6 +4,7 @@ #include #include +#define RGFW_IMPORT #include "../../rgfw.h" #define cls objc_getClass diff --git a/rendering/vk/physical_device.c b/rendering/vk/physical_device.c index 99611f6..e035e14 100644 --- a/rendering/vk/physical_device.c +++ b/rendering/vk/physical_device.c @@ -74,8 +74,8 @@ void vk_physical_device_select_family_indices(struct renderer_context *context) /* * This loop iterates over all the family properties * provided by the physical device and searches for - * two unique queue family indices, one for presentation - * and one for graphics. + * queue family indices for presentation and graphics. + * They may be the same queue family on many GPUs. */ for (u32 i = 0; i < property_count; i++) { if (context->queue_indices.graphics != 0xaa && context->queue_indices.present != 0xaa) break; @@ -83,7 +83,6 @@ void vk_physical_device_select_family_indices(struct renderer_context *context) if ((prop.queueFlags & VK_QUEUE_GRAPHICS_BIT) && context->queue_indices.graphics == 0xaa) { context->queue_indices.graphics = i; - continue; } VkBool32 present_support = VK_FALSE; @@ -92,7 +91,9 @@ void vk_physical_device_select_family_indices(struct renderer_context *context) fatal("Can't check for surface support.\n"); } - if (present_support == VK_TRUE) context->queue_indices.present = i; + if (present_support == VK_TRUE && context->queue_indices.present == 0xaa) { + context->queue_indices.present = i; + } } free(properties); diff --git a/rendering/vk/pipeline.c b/rendering/vk/pipeline.c new file mode 100644 index 0000000..7ce7de8 --- /dev/null +++ b/rendering/vk/pipeline.c @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#include "pipeline.h" +#include "../../core/log.h" +#include +#include + +static u8 *read_file(const char *path, usize *size) +{ + FILE *file = fopen(path, "rb"); + if (!file) { + return NULL; + } + + fseek(file, 0, SEEK_END); + *size = ftell(file); + fseek(file, 0, SEEK_SET); + + u8 *data = malloc(*size); + fread(data, 1, *size, file); + fclose(file); + + return data; +} + +static VkShaderModule create_shader_module(VkDevice device, u8 *code, usize size) +{ + VkShaderModuleCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = size, + .pCode = (u32 *)code + }; + + VkShaderModule module; + if (vkCreateShaderModule(device, &create_info, NULL, &module) != VK_SUCCESS) { + fatal("Can't create shader module.\n"); + } + + return module; +} + +void vk_pipeline_init(struct renderer_context *context) +{ + usize vert_size, frag_size; + u8 *vert_code = read_file("assets/shaders/quad.vert.spv", &vert_size); + u8 *frag_code = read_file("assets/shaders/quad.frag.spv", &frag_size); + + if (!vert_code || !frag_code) { + fatal("Can't read shader files.\n"); + } + + VkShaderModule vert_module = create_shader_module(context->device, vert_code, vert_size); + VkShaderModule frag_module = create_shader_module(context->device, frag_code, frag_size); + + free(vert_code); + free(frag_code); + + VkPipelineShaderStageCreateInfo shader_stages[] = { + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = vert_module, + .pName = "main" + }, + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = frag_module, + .pName = "main" + } + }; + + VkVertexInputBindingDescription binding = { + .binding = 0, + .stride = sizeof(f32) * 5, + .inputRate = VK_VERTEX_INPUT_RATE_VERTEX + }; + + VkVertexInputAttributeDescription attributes[] = { + { + .binding = 0, + .location = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = 0 + }, + { + .binding = 0, + .location = 1, + .format = VK_FORMAT_R32G32B32_SFLOAT, + .offset = sizeof(f32) * 2 + } + }; + + VkPipelineVertexInputStateCreateInfo vertex_input = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &binding, + .vertexAttributeDescriptionCount = 2, + .pVertexAttributeDescriptions = attributes + }; + + VkPipelineInputAssemblyStateCreateInfo input_assembly = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE + }; + + VkViewport viewport = { + .x = 0.0f, + .y = 0.0f, + .width = (f32)context->swapchain.extent.width, + .height = (f32)context->swapchain.extent.height, + .minDepth = 0.0f, + .maxDepth = 1.0f + }; + + VkRect2D scissor = { + .offset = { 0, 0 }, + .extent = context->swapchain.extent + }; + + VkPipelineViewportStateCreateInfo viewport_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .pViewports = &viewport, + .scissorCount = 1, + .pScissors = &scissor + }; + + VkPipelineRasterizationStateCreateInfo rasterizer = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .lineWidth = 1.0f, + .cullMode = VK_CULL_MODE_BACK_BIT, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .depthBiasEnable = VK_FALSE + }; + + VkPipelineMultisampleStateCreateInfo multisampling = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .sampleShadingEnable = VK_FALSE, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT + }; + + VkPipelineColorBlendAttachmentState blend_attachment = { + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + .blendEnable = VK_FALSE + }; + + VkPipelineColorBlendStateCreateInfo color_blend = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = VK_FALSE, + .attachmentCount = 1, + .pAttachments = &blend_attachment + }; + + VkPipelineLayoutCreateInfo layout_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 0, + .pushConstantRangeCount = 0 + }; + + if (vkCreatePipelineLayout(context->device, &layout_info, NULL, &context->pipeline_layout) != VK_SUCCESS) { + fatal("Can't create pipeline layout.\n"); + } + + VkGraphicsPipelineCreateInfo pipeline_info = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = 2, + .pStages = shader_stages, + .pVertexInputState = &vertex_input, + .pInputAssemblyState = &input_assembly, + .pViewportState = &viewport_state, + .pRasterizationState = &rasterizer, + .pMultisampleState = &multisampling, + .pColorBlendState = &color_blend, + .layout = context->pipeline_layout, + .renderPass = context->render_pass, + .subpass = 0 + }; + + if (vkCreateGraphicsPipelines(context->device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &context->pipeline) != VK_SUCCESS) { + fatal("Can't create graphics pipeline.\n"); + } + + vkDestroyShaderModule(context->device, vert_module, NULL); + vkDestroyShaderModule(context->device, frag_module, NULL); + + log_info("Graphics pipeline created.\n"); +} + +void vk_pipeline_deinit(struct renderer_context *context) +{ + vkDestroyPipeline(context->device, context->pipeline, NULL); + vkDestroyPipelineLayout(context->device, context->pipeline_layout, NULL); +} diff --git a/rendering/vk/pipeline.h b/rendering/vk/pipeline.h new file mode 100644 index 0000000..155ab34 --- /dev/null +++ b/rendering/vk/pipeline.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#ifndef PIPELINE_H +#define PIPELINE_H + +#include "vk.h" + +void vk_pipeline_init(struct renderer_context *context); +void vk_pipeline_deinit(struct renderer_context *context); + +#endif diff --git a/rendering/vk/platform.c b/rendering/vk/platform.c index 8f8f6e5..685b609 100644 --- a/rendering/vk/platform.c +++ b/rendering/vk/platform.c @@ -1,7 +1,9 @@ /* SPDX-License-Identifier:BSD-3-Clause */ #define RGFW_VULKAN +#define RGFW_EXPORT #define RGFW_IMPLEMENTATION +#include "../../rgfw.h" #include #include "../../core/log.h" @@ -36,6 +38,7 @@ int platform_run(i32 argc, u8 * *argv) while (RGFW_window_shouldClose(win) == RGFW_FALSE) { RGFW_event event; while (RGFW_window_checkEvent(win, &event)); + renderer_present(context); } renderer_context_deinit(context); diff --git a/rendering/vk/renderer.c b/rendering/vk/renderer.c index 2ac4135..0c70be3 100644 --- a/rendering/vk/renderer.c +++ b/rendering/vk/renderer.c @@ -4,10 +4,20 @@ #include "physical_device.h" #include "device.h" #include "surface.h" +#include "swapchain.h" +#include "renderpass.h" +#include "pipeline.h" +#include "framebuffer.h" +#include "command.h" +#include "sync.h" +#include "buffer.h" #include "vk.h" +#include "../../core/log.h" #include #include +#include "sync.h" + struct renderer_context *renderer_context_init(RGFW_window *window) { struct renderer_context *context = (struct renderer_context *)malloc(sizeof(struct renderer_context)); @@ -18,17 +28,77 @@ struct renderer_context *renderer_context_init(RGFW_window *window) vk_physical_device_pick(context); vk_physical_device_select_family_indices(context); vk_device_init(context); + vk_swapchain_init(context); + vk_renderpass_init(context); + vk_pipeline_init(context); + vk_framebuffers_init(context); + vk_command_pool_init(context); + vk_command_buffers_init(context); + vk_sync_init(context); + vk_quad_buffers_init(context); + return context; } void renderer_context_deinit(struct renderer_context *context) { + vkDeviceWaitIdle(context->device); + + vk_quad_buffers_deinit(context); + vk_sync_deinit(context); + free(context->command_buffers); + vk_command_pool_deinit(context); + vk_framebuffers_deinit(context); + vk_pipeline_deinit(context); + vk_renderpass_deinit(context); + vk_swapchain_deinit(context); vk_surface_deinit(context); vk_device_deinit(context); vk_instance_deinit(context); free(context); } +static void record_command_buffer(struct renderer_context *context, u32 image_index) +{ + VkCommandBuffer cmd = context->command_buffers[context->current_frame]; + + VkCommandBufferBeginInfo begin_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO + }; + + if (vkBeginCommandBuffer(cmd, &begin_info) != VK_SUCCESS) { + fatal("Can't begin command buffer.\n"); + } + + VkClearValue clear_color = { .color = { { 0.1f, 0.1f, 0.1f, 1.0f } } }; + + VkRenderPassBeginInfo render_pass_info = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = context->render_pass, + .framebuffer = context->swapchain.framebuffers[image_index], + .renderArea.offset = { 0, 0 }, + .renderArea.extent = context->swapchain.extent, + .clearValueCount = 1, + .pClearValues = &clear_color + }; + + vkCmdBeginRenderPass(cmd, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, context->pipeline); + + VkBuffer vertex_buffers[] = { context->vertex_buffer }; + VkDeviceSize offsets[] = { 0 }; + vkCmdBindVertexBuffers(cmd, 0, 1, vertex_buffers, offsets); + vkCmdBindIndexBuffer(cmd, context->index_buffer, 0, VK_INDEX_TYPE_UINT16); + + vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0); + + vkCmdEndRenderPass(cmd); + + if (vkEndCommandBuffer(cmd) != VK_SUCCESS) { + fatal("Can't end command buffer.\n"); + } +} + struct mesh *renderer_build_chunk_mesh(void) { return NULL; @@ -36,7 +106,7 @@ struct mesh *renderer_build_chunk_mesh(void) void renderer_draw_mesh(struct renderer_context *context, struct mesh mesh) { - (void) context; + (void)context; (void)mesh; } @@ -45,3 +115,55 @@ void renderer_draw_chunk(struct renderer_context *context, struct mesh mesh) (void)mesh; (void)context; } + +void renderer_present(struct renderer_context *context) +{ + vkWaitForFences(context->device, 1, &context->in_flight[context->current_frame], VK_TRUE, UINT64_MAX); + + u32 image_index; + VkResult result = vkAcquireNextImageKHR(context->device, context->swapchain.handle, UINT64_MAX, + context->image_available[context->current_frame], VK_NULL_HANDLE, &image_index); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + return; + } + + vkResetFences(context->device, 1, &context->in_flight[context->current_frame]); + vkResetCommandBuffer(context->command_buffers[context->current_frame], 0); + + record_command_buffer(context, image_index); + + VkSemaphore wait_semaphores[] = { context->image_available[context->current_frame] }; + VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + VkSemaphore signal_semaphores[] = { context->render_finished[image_index] }; + + VkSubmitInfo submit_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 1, + .pWaitSemaphores = wait_semaphores, + .pWaitDstStageMask = wait_stages, + .commandBufferCount = 1, + .pCommandBuffers = &context->command_buffers[context->current_frame], + .signalSemaphoreCount = 1, + .pSignalSemaphores = signal_semaphores + }; + + if (vkQueueSubmit(context->graphics_queue, 1, &submit_info, context->in_flight[context->current_frame]) != VK_SUCCESS) { + fatal("Can't submit draw command buffer.\n"); + } + + VkSwapchainKHR swapchains[] = { context->swapchain.handle }; + + VkPresentInfoKHR present_info = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1, + .pWaitSemaphores = signal_semaphores, + .swapchainCount = 1, + .pSwapchains = swapchains, + .pImageIndices = &image_index + }; + + vkQueuePresentKHR(context->present_queue, &present_info); + + context->current_frame = (context->current_frame + 1) % MAX_FRAMES_IN_FLIGHT; +} diff --git a/rendering/vk/renderpass.c b/rendering/vk/renderpass.c new file mode 100644 index 0000000..873d8e3 --- /dev/null +++ b/rendering/vk/renderpass.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#include "renderpass.h" +#include "../../core/log.h" + +void vk_renderpass_init(struct renderer_context *context) +{ + VkAttachmentDescription color_attachment = { + .format = context->swapchain.format, + .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 color_ref = { + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + + VkSubpassDescription subpass = { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .colorAttachmentCount = 1, + .pColorAttachments = &color_ref + }; + + VkSubpassDependency dependency = { + .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_WRITE_BIT + }; + + VkRenderPassCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = 1, + .pAttachments = &color_attachment, + .subpassCount = 1, + .pSubpasses = &subpass, + .dependencyCount = 1, + .pDependencies = &dependency + }; + + if (vkCreateRenderPass(context->device, &create_info, NULL, &context->render_pass) != VK_SUCCESS) { + fatal("Can't create render pass.\n"); + } + + log_info("Render pass created.\n"); +} + +void vk_renderpass_deinit(struct renderer_context *context) +{ + vkDestroyRenderPass(context->device, context->render_pass, NULL); +} diff --git a/rendering/vk/renderpass.h b/rendering/vk/renderpass.h new file mode 100644 index 0000000..2f98eee --- /dev/null +++ b/rendering/vk/renderpass.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#ifndef RENDERPASS_H +#define RENDERPASS_H + +#include "vk.h" + +void vk_renderpass_init(struct renderer_context *context); +void vk_renderpass_deinit(struct renderer_context *context); + +#endif diff --git a/rendering/vk/swapchain.c b/rendering/vk/swapchain.c new file mode 100644 index 0000000..7e7bf23 --- /dev/null +++ b/rendering/vk/swapchain.c @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#include "swapchain.h" +#include "../../core/log.h" +#include +#include + +#define RGFW_IMPORT +#include "../../rgfw.h" + +static VkSurfaceFormatKHR select_surface_format(VkPhysicalDevice device, VkSurfaceKHR surface) +{ + u32 format_count = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, NULL); + + VkSurfaceFormatKHR *formats = malloc(sizeof(VkSurfaceFormatKHR) * format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, formats); + + VkSurfaceFormatKHR selected = formats[0]; + for (u32 i = 0; i < format_count; i++) { + if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && + formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + selected = formats[i]; + break; + } + } + + free(formats); + return selected; +} + +static VkPresentModeKHR select_present_mode(VkPhysicalDevice device, VkSurfaceKHR surface) +{ + u32 mode_count = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &mode_count, NULL); + + VkPresentModeKHR *modes = malloc(sizeof(VkPresentModeKHR) * mode_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &mode_count, modes); + + VkPresentModeKHR selected = VK_PRESENT_MODE_FIFO_KHR; + for (u32 i = 0; i < mode_count; i++) { + if (modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { + selected = VK_PRESENT_MODE_MAILBOX_KHR; + break; + } + } + + free(modes); + return selected; +} + +static VkExtent2D select_extent(VkSurfaceCapabilitiesKHR *caps, RGFW_window *window) +{ + if (caps->currentExtent.width != 0xFFFFFFFF) { + return caps->currentExtent; + } + + i32 w, h; + RGFW_window_getSize(window, &w, &h); + + VkExtent2D extent; + extent.width = (u32)w; + extent.height = (u32)h; + + if (extent.width < caps->minImageExtent.width) + extent.width = caps->minImageExtent.width; + if (extent.width > caps->maxImageExtent.width) + extent.width = caps->maxImageExtent.width; + if (extent.height < caps->minImageExtent.height) + extent.height = caps->minImageExtent.height; + if (extent.height > caps->maxImageExtent.height) + extent.height = caps->maxImageExtent.height; + + return extent; +} + +void vk_swapchain_init(struct renderer_context *context) +{ + VkSurfaceCapabilitiesKHR caps; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->physical_device, context->surface, &caps); + + VkSurfaceFormatKHR format = select_surface_format(context->physical_device, context->surface); + VkPresentModeKHR present_mode = select_present_mode(context->physical_device, context->surface); + VkExtent2D extent = select_extent(&caps, context->window); + + u32 image_count = caps.minImageCount + 1; + if (caps.maxImageCount > 0 && image_count > caps.maxImageCount) { + image_count = caps.maxImageCount; + } + + VkSwapchainCreateInfoKHR create_info = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = context->surface, + .minImageCount = image_count, + .imageFormat = format.format, + .imageColorSpace = format.colorSpace, + .imageExtent = extent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .preTransform = caps.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = present_mode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE + }; + + u32 queue_indices[] = { context->queue_indices.graphics, context->queue_indices.present }; + if (context->queue_indices.graphics != context->queue_indices.present) { + create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + create_info.queueFamilyIndexCount = 2; + create_info.pQueueFamilyIndices = queue_indices; + } else { + create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + if (vkCreateSwapchainKHR(context->device, &create_info, NULL, &context->swapchain.handle) != VK_SUCCESS) { + fatal("Can't create swapchain.\n"); + } + + context->swapchain.format = format.format; + context->swapchain.extent = extent; + + vkGetSwapchainImagesKHR(context->device, context->swapchain.handle, &context->swapchain.image_count, NULL); + context->swapchain.images = malloc(sizeof(VkImage) * context->swapchain.image_count); + vkGetSwapchainImagesKHR(context->device, context->swapchain.handle, &context->swapchain.image_count, context->swapchain.images); + + context->swapchain.image_views = malloc(sizeof(VkImageView) * context->swapchain.image_count); + for (u32 i = 0; i < context->swapchain.image_count; i++) { + VkImageViewCreateInfo view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = context->swapchain.images[i], + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = context->swapchain.format, + .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.baseMipLevel = 0, + .subresourceRange.levelCount = 1, + .subresourceRange.baseArrayLayer = 0, + .subresourceRange.layerCount = 1 + }; + + if (vkCreateImageView(context->device, &view_info, NULL, &context->swapchain.image_views[i]) != VK_SUCCESS) { + fatal("Can't create image view.\n"); + } + } + + log_info("Swapchain created.\n"); +} + +void vk_swapchain_deinit(struct renderer_context *context) +{ + for (u32 i = 0; i < context->swapchain.image_count; i++) { + vkDestroyImageView(context->device, context->swapchain.image_views[i], NULL); + } + free(context->swapchain.image_views); + free(context->swapchain.images); + vkDestroySwapchainKHR(context->device, context->swapchain.handle, NULL); +} diff --git a/rendering/vk/swapchain.h b/rendering/vk/swapchain.h new file mode 100644 index 0000000..fa77117 --- /dev/null +++ b/rendering/vk/swapchain.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#ifndef SWAPCHAIN_H +#define SWAPCHAIN_H + +#include "vk.h" + +void vk_swapchain_init(struct renderer_context *context); +void vk_swapchain_deinit(struct renderer_context *context); + +#endif diff --git a/rendering/vk/sync.c b/rendering/vk/sync.c new file mode 100644 index 0000000..da2113e --- /dev/null +++ b/rendering/vk/sync.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#include "sync.h" +#include "../../core/log.h" +#include + +void vk_sync_init(struct renderer_context *context) +{ + u32 count = context->swapchain.image_count; + + context->image_available = malloc(sizeof(VkSemaphore) * count); + context->render_finished = malloc(sizeof(VkSemaphore) * count); + context->in_flight = malloc(sizeof(VkFence) * count); + + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO + }; + + VkFenceCreateInfo fence_info = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = VK_FENCE_CREATE_SIGNALED_BIT + }; + + for (u32 i = 0; i < count; i++) { + if (vkCreateSemaphore(context->device, &semaphore_info, NULL, &context->image_available[i]) != VK_SUCCESS || + vkCreateSemaphore(context->device, &semaphore_info, NULL, &context->render_finished[i]) != VK_SUCCESS || + vkCreateFence(context->device, &fence_info, NULL, &context->in_flight[i]) != VK_SUCCESS) { + fatal("Can't create sync objects.\n"); + } + } + + context->current_frame = 0; + log_info("Sync objects created.\n"); +} + +void vk_sync_deinit(struct renderer_context *context) +{ + u32 count = context->swapchain.image_count; + + for (u32 i = 0; i < count; i++) { + vkDestroySemaphore(context->device, context->image_available[i], NULL); + vkDestroySemaphore(context->device, context->render_finished[i], NULL); + vkDestroyFence(context->device, context->in_flight[i], NULL); + } + + free(context->image_available); + free(context->render_finished); + free(context->in_flight); +} diff --git a/rendering/vk/sync.h b/rendering/vk/sync.h new file mode 100644 index 0000000..4da9ee7 --- /dev/null +++ b/rendering/vk/sync.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier:BSD-3-Clause */ +#ifndef SYNC_H +#define SYNC_H + +#include "vk.h" + +#define MAX_FRAMES_IN_FLIGHT 2 + +void vk_sync_init(struct renderer_context *context); +void vk_sync_deinit(struct renderer_context *context); + +#endif diff --git a/rendering/vk/vk.h b/rendering/vk/vk.h index 9a4c2cb..69d90ae 100644 --- a/rendering/vk/vk.h +++ b/rendering/vk/vk.h @@ -4,12 +4,24 @@ #include #include "../../core/types.h" +#define RGFW_VULKAN +#define RGFW_IMPORT #include "../../rgfw.h" struct queue_family_indices { u32 graphics, present; }; +struct swapchain_info { + VkSwapchainKHR handle; + VkFormat format; + VkExtent2D extent; + u32 image_count; + VkImage *images; + VkImageView *image_views; + VkFramebuffer *framebuffers; +}; + struct renderer_context { VkInstance instance; VkPhysicalDevice physical_device; @@ -18,6 +30,24 @@ struct renderer_context { VkSurfaceKHR surface; struct queue_family_indices queue_indices; RGFW_window *window; + + struct swapchain_info swapchain; + VkRenderPass render_pass; + VkPipelineLayout pipeline_layout; + VkPipeline pipeline; + + VkCommandPool command_pool; + VkCommandBuffer *command_buffers; + + VkSemaphore *image_available; + VkSemaphore *render_finished; + VkFence *in_flight; + u32 current_frame; + + VkBuffer vertex_buffer; + VkDeviceMemory vertex_buffer_memory; + VkBuffer index_buffer; + VkDeviceMemory index_buffer_memory; }; #endif