basic rendering for both opengl and vulkan
This commit is contained in:
parent
4b18afa040
commit
dadd2edaf1
29 changed files with 1140 additions and 38 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,4 +5,5 @@ topaz
|
|||
.cache
|
||||
**/*.BAK
|
||||
**/*.bak
|
||||
**/*.spv
|
||||
**/*~
|
||||
|
|
|
|||
10
assets/shaders/quad.frag
Normal file
10
assets/shaders/quad.frag
Normal 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
12
assets/shaders/quad.vert
Normal 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;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
24
makefile
24
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))
|
||||
|
|
|
|||
21
rendering/gl/context.h
Normal file
21
rendering/gl/context.h
Normal 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
|
||||
|
|
@ -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
154
rendering/gl/renderer.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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
101
rendering/vk/buffer.c
Normal 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
15
rendering/vk/buffer.h
Normal 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
42
rendering/vk/command.c
Normal 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
11
rendering/vk/command.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
37
rendering/vk/framebuffer.c
Normal file
37
rendering/vk/framebuffer.c
Normal 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);
|
||||
}
|
||||
10
rendering/vk/framebuffer.h
Normal file
10
rendering/vk/framebuffer.h
Normal 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
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <objc/runtime.h>
|
||||
#include <objc/message.h>
|
||||
#define RGFW_IMPORT
|
||||
#include "../../rgfw.h"
|
||||
|
||||
#define cls objc_getClass
|
||||
|
|
|
|||
|
|
@ -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
198
rendering/vk/pipeline.c
Normal 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
10
rendering/vk/pipeline.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
58
rendering/vk/renderpass.c
Normal 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
10
rendering/vk/renderpass.h
Normal 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
160
rendering/vk/swapchain.c
Normal 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
10
rendering/vk/swapchain.h
Normal 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
48
rendering/vk/sync.c
Normal 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
12
rendering/vk/sync.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue