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
|
.cache
|
||||||
**/*.BAK
|
**/*.BAK
|
||||||
**/*.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
|
CFLAGS := -Wall -Wextra -std=c99 -pedantic
|
||||||
LIBS := -lm
|
LIBS := -lm
|
||||||
# Can be gl or vk
|
# Can be gl or vk
|
||||||
GRAPHICS_BACKEND := vk
|
GRAPHICS_BACKEND := gl
|
||||||
DEBUG_BUILD=1
|
DEBUG_BUILD=1
|
||||||
PLATFORM := $(shell uname)
|
PLATFORM := $(shell uname)
|
||||||
|
|
||||||
|
|
|
||||||
24
makefile
24
makefile
|
|
@ -5,6 +5,10 @@ ifeq (${DEBUG_BUILD},1)
|
||||||
CFLAGS += -ggdb -fsanitize=address,undefined -DDEBUG
|
CFLAGS += -ggdb -fsanitize=address,undefined -DDEBUG
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
SHADER_DIR := assets/shaders
|
||||||
|
SHADER_SRC := $(wildcard $(SHADER_DIR)/*.vert) $(wildcard $(SHADER_DIR)/*.frag)
|
||||||
|
SHADER_SPV := $(SHADER_SRC:%=%.spv)
|
||||||
|
|
||||||
SRC:=\
|
SRC:=\
|
||||||
topaz.c\
|
topaz.c\
|
||||||
core/linear.c\
|
core/linear.c\
|
||||||
|
|
@ -22,7 +26,14 @@ ifeq (${GRAPHICS_BACKEND},vk)
|
||||||
rendering/vk/instance.c\
|
rendering/vk/instance.c\
|
||||||
rendering/vk/physical_device.c\
|
rendering/vk/physical_device.c\
|
||||||
rendering/vk/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)
|
ifeq (${PLATFORM},Darwin)
|
||||||
SRC += rendering/vk/macos_platform.c
|
SRC += rendering/vk/macos_platform.c
|
||||||
|
|
@ -32,15 +43,20 @@ endif
|
||||||
|
|
||||||
OBJ:=${SRC:.c=.o}
|
OBJ:=${SRC:.c=.o}
|
||||||
|
|
||||||
all: ${OBJ}
|
all: shaders ${OBJ}
|
||||||
${CC} ${LIBS} ${CFLAGS} ${OBJ} -o topaz
|
${CC} ${LIBS} ${CFLAGS} ${OBJ} -o topaz
|
||||||
|
|
||||||
|
shaders: ${SHADER_SPV}
|
||||||
|
|
||||||
|
$(SHADER_DIR)/%.spv: $(SHADER_DIR)/%
|
||||||
|
glslc $< -o $@
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
${CC} ${CFLAGS} -c $< -o $@
|
${CC} ${CFLAGS} -c $< -o $@
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean shaders
|
||||||
clean:
|
clean:
|
||||||
@rm -rfv ${OBJ} topaz
|
@rm -rfv ${OBJ} ${SHADER_SPV} topaz
|
||||||
|
|
||||||
INDENTABLE := $(shell find . -name "*.c" -o -name "*.h")
|
INDENTABLE := $(shell find . -name "*.c" -o -name "*.h")
|
||||||
INDENTABLE := $(filter-out ./gl/gl.h,$(INDENTABLE))
|
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 */
|
/* SPDX-License-Identifier:BSD-3-Clause */
|
||||||
|
|
||||||
#include "gl.h"
|
#include "gl.h"
|
||||||
|
#define RGFW_EXPORT
|
||||||
#define RGFW_IMPLEMENTATION
|
#define RGFW_IMPLEMENTATION
|
||||||
#define RGFW_OPENGL
|
#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
|
* This function is the entrypoint for the whole game. Its role is to
|
||||||
* initialize OpenGL, create the renderer and start the game loop.
|
* 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)argc;
|
||||||
(void)argv;
|
(void)argv;
|
||||||
|
|
||||||
|
log_info("Using OpenGL as rendering backend.\n");
|
||||||
|
|
||||||
RGFW_glHints *hints = RGFW_getGlobalHints_OpenGL();
|
RGFW_glHints *hints = RGFW_getGlobalHints_OpenGL();
|
||||||
hints->major = 3;
|
hints->major = 3;
|
||||||
hints->minor = 3;
|
hints->minor = 3;
|
||||||
RGFW_setGlobalHints_OpenGL(hints);
|
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);
|
RGFW_window_createContext_OpenGL(win, hints);
|
||||||
|
|
||||||
int glad_version = gladLoadGL(RGFW_getProcAddress_OpenGL);
|
int glad_version = gladLoadGL(RGFW_getProcAddress_OpenGL);
|
||||||
if (glad_version == 0) {
|
if (glad_version == 0) {
|
||||||
printf("Failed to initialize OpenGL context.\n");
|
log_error("Failed to initialize OpenGL context.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
RGFW_window_show(win);
|
RGFW_window_show(win);
|
||||||
|
|
||||||
RGFW_window_setExitKey(win, RGFW_escape);
|
RGFW_window_setExitKey(win, RGFW_escape);
|
||||||
|
|
||||||
const u8 *version = glGetString(GL_VERSION);
|
log_info("OpenGL initialized.\n");
|
||||||
printf("OpenGL Version: %s\n", version);
|
|
||||||
printf("GLAD Version: %d.%d\n", GLAD_VERSION_MAJOR(glad_version), GLAD_VERSION_MINOR(glad_version));
|
|
||||||
|
|
||||||
|
struct renderer_context *context = renderer_context_init(win);
|
||||||
|
|
||||||
while (RGFW_window_shouldClose(win) == RGFW_FALSE) {
|
while (RGFW_window_shouldClose(win) == RGFW_FALSE) {
|
||||||
RGFW_event event;
|
RGFW_event event;
|
||||||
while (RGFW_window_checkEvent(win, &event));
|
while (RGFW_window_checkEvent(win, &event));
|
||||||
|
renderer_present(context);
|
||||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
RGFW_window_swapBuffers_OpenGL(win);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderer_context_deinit(context);
|
||||||
RGFW_window_close(win);
|
RGFW_window_close(win);
|
||||||
return 0;
|
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
|
#ifndef RENDERER_H
|
||||||
#define RENDERER_H
|
#define RENDERER_H
|
||||||
#include "../types.h"
|
#include "../core/types.h"
|
||||||
#ifdef BACKEND_VK
|
|
||||||
#define RGFW_VULKAN
|
#ifndef RGFW_H
|
||||||
|
typedef struct RGFW_window RGFW_window;
|
||||||
#endif
|
#endif
|
||||||
#include "../rgfw.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A mesh is a drawable object represented as an index (offset) in the global
|
* 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)
|
void vk_device_init(struct renderer_context *context)
|
||||||
{
|
{
|
||||||
struct vector *device_extensions = vector_init(0, sizeof(char *));
|
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);
|
struct vector *physical_device_extensions = vk_physical_device_get_extensions(context);
|
||||||
for (usize i = 0; i < physical_device_extensions->length; i++) {
|
for (usize i = 0; i < physical_device_extensions->length; i++) {
|
||||||
if (strcmp(((char **)physical_device_extensions->data)[i], "VK_KHR_portability_subset") == 0) {
|
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;
|
f32 priority = 0.0;
|
||||||
VkDeviceQueueCreateInfo graphics_queue_create_info[2];
|
VkDeviceQueueCreateInfo queue_create_info[2];
|
||||||
graphics_queue_create_info[0] = (VkDeviceQueueCreateInfo){
|
u32 queue_create_info_count = 0;
|
||||||
|
|
||||||
|
queue_create_info[queue_create_info_count++] = (VkDeviceQueueCreateInfo){
|
||||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||||
.queueFamilyIndex = context->queue_indices.graphics,
|
.queueFamilyIndex = context->queue_indices.graphics,
|
||||||
.queueCount = 1,
|
.queueCount = 1,
|
||||||
.pQueuePriorities = &priority
|
.pQueuePriorities = &priority
|
||||||
};
|
};
|
||||||
|
|
||||||
graphics_queue_create_info[1] = (VkDeviceQueueCreateInfo){
|
/* Only add a second queue create info if present uses a different family */
|
||||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
if (context->queue_indices.present != context->queue_indices.graphics) {
|
||||||
.queueFamilyIndex = context->queue_indices.present,
|
queue_create_info[queue_create_info_count++] = (VkDeviceQueueCreateInfo){
|
||||||
.queueCount = 1,
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||||
.pQueuePriorities = &priority
|
.queueFamilyIndex = context->queue_indices.present,
|
||||||
};
|
.queueCount = 1,
|
||||||
|
.pQueuePriorities = &priority
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
VkDeviceCreateInfo device_create_info = {
|
VkDeviceCreateInfo device_create_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||||
.pNext = NULL,
|
.pNext = NULL,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.queueCreateInfoCount = 2,
|
.queueCreateInfoCount = queue_create_info_count,
|
||||||
.pQueueCreateInfos = graphics_queue_create_info,
|
.pQueueCreateInfos = queue_create_info,
|
||||||
.enabledLayerCount = 0,
|
.enabledLayerCount = 0,
|
||||||
.ppEnabledLayerNames = NULL,
|
.ppEnabledLayerNames = NULL,
|
||||||
.enabledExtensionCount = device_extensions->length,
|
.enabledExtensionCount = device_extensions->length,
|
||||||
|
|
@ -58,8 +65,8 @@ void vk_device_init(struct renderer_context *context)
|
||||||
fatal("Can't create Vulkan device.\n");
|
fatal("Can't create Vulkan device.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
vkGetDeviceQueue(context->device, graphics_queue_create_info[0].queueFamilyIndex, 0, &context->graphics_queue);
|
vkGetDeviceQueue(context->device, context->queue_indices.graphics, 0, &context->graphics_queue);
|
||||||
vkGetDeviceQueue(context->device, graphics_queue_create_info[1].queueFamilyIndex, 0, &context->present_queue);
|
vkGetDeviceQueue(context->device, context->queue_indices.present, 0, &context->present_queue);
|
||||||
|
|
||||||
vector_deinit(physical_device_extensions);
|
vector_deinit(physical_device_extensions);
|
||||||
vector_deinit(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/runtime.h>
|
||||||
#include <objc/message.h>
|
#include <objc/message.h>
|
||||||
|
#define RGFW_IMPORT
|
||||||
#include "../../rgfw.h"
|
#include "../../rgfw.h"
|
||||||
|
|
||||||
#define cls objc_getClass
|
#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
|
* This loop iterates over all the family properties
|
||||||
* provided by the physical device and searches for
|
* provided by the physical device and searches for
|
||||||
* two unique queue family indices, one for presentation
|
* queue family indices for presentation and graphics.
|
||||||
* and one for graphics.
|
* They may be the same queue family on many GPUs.
|
||||||
*/
|
*/
|
||||||
for (u32 i = 0; i < property_count; i++) {
|
for (u32 i = 0; i < property_count; i++) {
|
||||||
if (context->queue_indices.graphics != 0xaa && context->queue_indices.present != 0xaa) break;
|
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) {
|
if ((prop.queueFlags & VK_QUEUE_GRAPHICS_BIT) && context->queue_indices.graphics == 0xaa) {
|
||||||
context->queue_indices.graphics = i;
|
context->queue_indices.graphics = i;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VkBool32 present_support = VK_FALSE;
|
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");
|
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);
|
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 */
|
/* SPDX-License-Identifier:BSD-3-Clause */
|
||||||
|
|
||||||
#define RGFW_VULKAN
|
#define RGFW_VULKAN
|
||||||
|
#define RGFW_EXPORT
|
||||||
#define RGFW_IMPLEMENTATION
|
#define RGFW_IMPLEMENTATION
|
||||||
|
#include "../../rgfw.h"
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "../../core/log.h"
|
#include "../../core/log.h"
|
||||||
|
|
||||||
|
|
@ -36,6 +38,7 @@ int platform_run(i32 argc, u8 * *argv)
|
||||||
while (RGFW_window_shouldClose(win) == RGFW_FALSE) {
|
while (RGFW_window_shouldClose(win) == RGFW_FALSE) {
|
||||||
RGFW_event event;
|
RGFW_event event;
|
||||||
while (RGFW_window_checkEvent(win, &event));
|
while (RGFW_window_checkEvent(win, &event));
|
||||||
|
renderer_present(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer_context_deinit(context);
|
renderer_context_deinit(context);
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,20 @@
|
||||||
#include "physical_device.h"
|
#include "physical_device.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "surface.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 "vk.h"
|
||||||
|
#include "../../core/log.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "sync.h"
|
||||||
|
|
||||||
struct renderer_context *renderer_context_init(RGFW_window *window)
|
struct renderer_context *renderer_context_init(RGFW_window *window)
|
||||||
{
|
{
|
||||||
struct renderer_context *context = (struct renderer_context *)malloc(sizeof(struct renderer_context));
|
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_pick(context);
|
||||||
vk_physical_device_select_family_indices(context);
|
vk_physical_device_select_family_indices(context);
|
||||||
vk_device_init(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;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_context_deinit(struct renderer_context *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_surface_deinit(context);
|
||||||
vk_device_deinit(context);
|
vk_device_deinit(context);
|
||||||
vk_instance_deinit(context);
|
vk_instance_deinit(context);
|
||||||
free(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)
|
struct mesh *renderer_build_chunk_mesh(void)
|
||||||
{
|
{
|
||||||
return NULL;
|
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 renderer_draw_mesh(struct renderer_context *context, struct mesh mesh)
|
||||||
{
|
{
|
||||||
(void) context;
|
(void)context;
|
||||||
(void)mesh;
|
(void)mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,3 +115,55 @@ void renderer_draw_chunk(struct renderer_context *context, struct mesh mesh)
|
||||||
(void)mesh;
|
(void)mesh;
|
||||||
(void)context;
|
(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 <vulkan/vulkan.h>
|
||||||
#include "../../core/types.h"
|
#include "../../core/types.h"
|
||||||
|
#define RGFW_VULKAN
|
||||||
|
#define RGFW_IMPORT
|
||||||
#include "../../rgfw.h"
|
#include "../../rgfw.h"
|
||||||
|
|
||||||
struct queue_family_indices {
|
struct queue_family_indices {
|
||||||
u32 graphics, present;
|
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 {
|
struct renderer_context {
|
||||||
VkInstance instance;
|
VkInstance instance;
|
||||||
VkPhysicalDevice physical_device;
|
VkPhysicalDevice physical_device;
|
||||||
|
|
@ -18,6 +30,24 @@ struct renderer_context {
|
||||||
VkSurfaceKHR surface;
|
VkSurfaceKHR surface;
|
||||||
struct queue_family_indices queue_indices;
|
struct queue_family_indices queue_indices;
|
||||||
RGFW_window *window;
|
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
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue