basic rendering for both opengl and vulkan

This commit is contained in:
Lorenzo Torres 2026-01-07 02:43:00 +01:00
parent 4b18afa040
commit dadd2edaf1
29 changed files with 1140 additions and 38 deletions

1
.gitignore vendored
View file

@ -5,4 +5,5 @@ topaz
.cache
**/*.BAK
**/*.bak
**/*.spv
**/*~

10
assets/shaders/quad.frag Normal file
View file

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

12
assets/shaders/quad.vert Normal file
View file

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

View file

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

View file

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

21
rendering/gl/context.h Normal file
View file

@ -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

View file

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

154
rendering/gl/renderer.c Normal file
View file

@ -0,0 +1,154 @@
/* SPDX-License-Identifier:BSD-3-Clause */
#include "context.h"
#include "../renderer.h"
#include "../../core/log.h"
#include <stdlib.h>
#include <stdio.h>
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);
}

View file

@ -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

101
rendering/vk/buffer.c Normal file
View file

@ -0,0 +1,101 @@
/* SPDX-License-Identifier:BSD-3-Clause */
#include "buffer.h"
#include "../../core/log.h"
#include <string.h>
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);
}

15
rendering/vk/buffer.h Normal file
View file

@ -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

42
rendering/vk/command.c Normal file
View file

@ -0,0 +1,42 @@
/* SPDX-License-Identifier:BSD-3-Clause */
#include "command.h"
#include "../../core/log.h"
#include <stdlib.h>
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");
}

11
rendering/vk/command.h Normal file
View file

@ -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

View file

@ -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){
/* 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);

View file

@ -0,0 +1,37 @@
/* SPDX-License-Identifier:BSD-3-Clause */
#include "framebuffer.h"
#include "../../core/log.h"
#include <stdlib.h>
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);
}

View file

@ -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

View file

@ -4,6 +4,7 @@
#include <objc/runtime.h>
#include <objc/message.h>
#define RGFW_IMPORT
#include "../../rgfw.h"
#define cls objc_getClass

View file

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

198
rendering/vk/pipeline.c Normal file
View file

@ -0,0 +1,198 @@
/* SPDX-License-Identifier:BSD-3-Clause */
#include "pipeline.h"
#include "../../core/log.h"
#include <stdlib.h>
#include <stdio.h>
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);
}

10
rendering/vk/pipeline.h Normal file
View file

@ -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

View file

@ -1,7 +1,9 @@
/* SPDX-License-Identifier:BSD-3-Clause */
#define RGFW_VULKAN
#define RGFW_EXPORT
#define RGFW_IMPLEMENTATION
#include "../../rgfw.h"
#include <vulkan/vulkan.h>
#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);

View file

@ -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 <stdlib.h>
#include <stdio.h>
#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;
}

58
rendering/vk/renderpass.c Normal file
View file

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

10
rendering/vk/renderpass.h Normal file
View file

@ -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

160
rendering/vk/swapchain.c Normal file
View file

@ -0,0 +1,160 @@
/* SPDX-License-Identifier:BSD-3-Clause */
#include "swapchain.h"
#include "../../core/log.h"
#include <stdlib.h>
#include <string.h>
#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);
}

10
rendering/vk/swapchain.h Normal file
View file

@ -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

48
rendering/vk/sync.c Normal file
View file

@ -0,0 +1,48 @@
/* SPDX-License-Identifier:BSD-3-Clause */
#include "sync.h"
#include "../../core/log.h"
#include <stdlib.h>
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);
}

12
rendering/vk/sync.h Normal file
View file

@ -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

View file

@ -4,12 +4,24 @@
#include <vulkan/vulkan.h>
#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