Compare commits

..
52 Commits
Author SHA1 Message Date
Piotr Krygier daed76f02f [WIP] Continue working on pipeline 2025-06-20 13:44:33 +02:00
Piotr Krygier f8eb98ddf4 [WIP] Add descriptors and pipeline builder 2025-06-20 13:44:33 +02:00
Piotr Krygier 173a82b9d1 [WIP] Refactor project
There are many problems with the way the current version is provided
and maintained. This commit or series of commits will fix this.
2025-06-20 13:44:33 +02:00
Piotr Krygier f5ab5f3f27 Improve testing framework
Replaced Criterrion with CMock. Added few tests, currently not working
properly. Random test upgrades.
2025-06-20 13:44:33 +02:00
Piotr Krygier 56c0b7922f [WIP] Create pipeline for transparent on screen objects 2025-06-20 13:44:33 +02:00
Piotr Krygier 3c72c9b387 Fixed window resizing 2025-06-20 13:44:33 +02:00
Piotr Krygier f197280e88 [WIP] Working on depth 2025-06-20 13:44:33 +02:00
Piotr Krygier ad90e5897a [WIP] Alpha blending enabled 2025-06-20 13:44:33 +02:00
Piotr Krygier c9384eb795 [WIP] Try to enable alpha blending 2025-06-20 13:44:33 +02:00
Piotr Krygier fb3151c5d1 [WIP] Render full text on screen 2025-06-20 13:44:33 +02:00
Piotr Krygier d59d8c640e [WIP] Add render text functionality 2025-06-20 13:44:33 +02:00
Piotr Krygier c1a8b14a90 [WIP] Fonts update 2025-06-20 13:44:33 +02:00
Piotr Krygier 99d7546e3d Fix instance buffer size 2025-06-20 13:44:33 +02:00
Piotr Krygier b2508ce4ff [WIP] Add text rendering 2025-06-20 13:44:33 +02:00
Piotr Krygier 3bd14b20c2 Minor cleanup and texture update 2025-06-20 13:44:33 +02:00
Piotr Krygier 2d230d7280 Implement mulitsampling 2025-06-20 13:44:33 +02:00
Piotr Krygier 3c73e6bac3 Remove boilerplate code from rse_vulkan_image.c 2025-06-20 13:44:33 +02:00
Piotr Krygier 1f585b4d8e Refactoring
Cleaned up a project, removed uneccessary includes.
Fixed few errors, nothing major.
2025-06-20 13:44:33 +02:00
Piotr Krygier 42dc4d6fcd Refactor project
Crealned up some TODOs, FIXMEs etc. Added better error handling. Looks
better, feels better.
2023-10-10 10:46:12 +02:00
Piotr Krygier bd4dd74881 Add bindless textures
Created bindless descriptors for holding texture data.
2023-10-04 13:29:23 +02:00
Piotr Krygier d328e38761 Add depth buffering 2023-10-03 13:22:24 +02:00
Piotr Krygier 548ed57969 Add textures
Textures supported
2023-10-03 09:00:02 +02:00
Piotr Krygier 4fcda01ba2 Add texture handler
Added functionality to load images as textures and bind them to meshes.
2023-10-02 13:21:17 +02:00
Piotr Krygier 0e040dd0d5 Move common log to new locale 2023-09-27 08:03:39 +02:00
Piotr Krygier 252a0820ed Move to bindless design
Replaced DrawIndexed with DrawIndexedIndirect function. This way
less parameters are passed from CPU and are store in GPU
mapped memory. Multiple meshes with multiple instances also work.
2023-09-26 14:15:21 +02:00
Piotr Krygier 3a58bd8756 Improve logging output 2023-09-26 14:15:21 +02:00
Piotr Krygier 7ca73f3c60 Fix queue family validation error 2023-09-26 14:15:21 +02:00
Piotr Krygier 940bc7e512 Remove RAM stored mesh data
Remove RAM stored vertices and indices data. It is now stored only in
GPU memory.
2023-09-26 14:15:21 +02:00
Piotr Krygier 0ef475e57e Add time function
Updates to objects should not be done in frame by frame basis, but by
time difference. This commit adds time diff function which can be used
to tell how much time has passed since last frame.
2023-09-26 14:15:21 +02:00
Piotr Krygier 863f502dde Improve threading 2023-09-11 11:15:25 +02:00
Piotr Krygier 507db80fb1 Create simple thread library 2023-08-14 14:49:57 +02:00
Piotr Krygier 3d538eaa2c Move back to C
I'm insane and as such I have moved to C AGAIN! Without any reason.
2023-08-14 13:48:29 +02:00
Piotr Krygier bf31b96a2e Add my own math library
GLM works only for C++ and I plan on moving to back to C. There is
a C port, but it would be good for me to figure out vertex math myself.
Hence, my own library has been created.
2023-08-10 09:43:26 +02:00
Piotr Krygier c22569cc48 Added Criterion unit test framework 2023-08-08 10:41:07 +02:00
Piotr Krygier a82550d414 Addded depenendency load tool
Added simple python script that will load needed libraries into
the project folder structure. Most libraries are now not needed to be
present in development system.
2023-08-07 13:48:30 +02:00
Piotr Krygier 320c9293b1 Update return types 2023-05-13 17:47:42 +02:00
Piotr Krygier 6781c1cff1 Remove VulkanState dependency
Passing VulkanState everywhere (including windowLoop) made no sense.
I know that "globals are evil", but this is the best solution for now.
2023-04-04 15:27:42 +02:00
Piotr Krygier e06288320d Enable multiple buffers
Enables creation of multiple mesh objects in a single buffer. This is
still WIP.
2023-04-04 14:56:31 +02:00
Piotr Krygier c4734e99c2 Add basic functionality to draw instances
Allows creation of instances of meshes and drawing all of them with
single draw call. Instances can have their own transformation data
(location, rotation and scale).
2023-03-31 16:31:47 +02:00
Piotr Krygier 6a09350902 Split vulkan_base into smaller units
Closes #30. This is a basic refactoring of the graphics module, so
everything won't be in single file.
2023-03-29 09:41:04 +02:00
Piotr Krygier 649e7f76fe Change build system to CMake
According to my own documentation, the project tree should look entirely
different. Also, it should use CMake instead of Make. This commit
introduces changes that will make my project tree to adhere to my
own documentation.
2023-03-16 12:04:35 +01:00
Piotr Krygier 0c45da5cc4 Cleanup code 2023-03-16 12:04:35 +01:00
Piotr Krygier d0ac0f0e97 Remove hardocded window system 2023-03-16 12:04:35 +01:00
Piotr Krygier a175045518 Change Buffer struct to hold AllocationInfo
Added allocationInfo field to struct Buffer. Also, modified createBuffer
to use this field. This might be useful, when accessing allocation
information later. Already used for uniform buffers.
2023-03-16 12:04:35 +01:00
Piotr Krygier cce4875329 Add uniform buffers 2023-03-08 14:36:30 +01:00
Piotr Krygier a94e7a95f5 Add index buffer 2022-12-17 21:57:11 +01:00
Piotr Krygier f8b32717f3 Add staging buffer 2022-12-17 21:25:00 +01:00
Piotr Krygier f4a6260a5e Fix some TODOs and FIXMEs 2022-12-14 15:44:57 +01:00
Piotr Krygier 34665e1c2e Change vertex buffer handling
Previously vertex data was only in shaders. I have removed it from there
and put hardcoded vertex data into cpp source code.
2022-12-14 14:16:30 +01:00
Piotr Krygier e7b9d4a32f Changed project back to C++ 2022-10-05 15:51:19 +02:00
Piotr Krygier ce0c399810 Move back to C++ 2022-07-05 16:03:31 -04:00
Piotr Krygier 4755371ffa Cleanup repo 2022-06-28 09:54:41 +02:00
106 changed files with 143066 additions and 10545 deletions

No files matched your search

-8
View File
@@ -30,11 +30,3 @@ ColumnLimit: 120
# Pointer star next to type, not variable name # Pointer star next to type, not variable name
DerivePointerAlignment: false DerivePointerAlignment: false
PointerAlignment: Left PointerAlignment: Left
# Function parameters Alignment
AlignAfterOpenBracket: Align
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
BinPackArguments: false
BinPackParameters: OnePerLine
+1 -11
View File
@@ -1,12 +1,2 @@
CompileFlags: CompileFlags:
Add: [ Add: -ferror-limit=1000
"-ferror-limit=0",
"-Wall",
"-Wpedantic",
"-Werror",
"-Wno-gnu-statement-expression",
"-I./",
"-I./graphics",
"-I./utilites",
]
+3 -15
View File
@@ -38,21 +38,9 @@ bin/*
# VSCode configurations # VSCode configurations
.vscode/ .vscode/
# Third party libraires
third_party/
# Cache folder # Cache folder
.cache/ .cache/
# Cmake cache files
CMakeCache.txt
# Since we are using conan, we can remove CMakeFiles folder
CMakeFiles/
# Clang specific files
.clangd
.clangd/
compile_commands.json
# Ignore subproject path except config files
subprojects/*
!subprojects/packagefiles/
!subprojects/*.wrap
BIN
View File
Binary file not shown.
+16
View File
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.12)
project("RedScarfEngine"
VERSION 0.0.1
DESCRIPTION "Vulkan based 3D engine"
LANGUAGES C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
add_subdirectory(red_scarf_engine)
add_subdirectory(utilities)
add_subdirectory(graphics)
# Enable later
# add_subdirectory(tests)
Binary file not shown.
+10
View File
@@ -2,3 +2,13 @@
Graphics/Game engine written in Vulkan. More of a science project than the actual engine that will be used anywhere. Graphics/Game engine written in Vulkan. More of a science project than the actual engine that will be used anywhere.
# Running
There are couple of usefull scripts to build and run this project.
## prepare_env.py
[WIP] This python script will download all project dependencies that are present in dependencies.dep file. It can be
either git repo link or an archive to be downloaded.
## run.sh
Sice we wan to use Vulkan version that is bind to this project, couple of environment variables need to be added to be
able to run this project. run.sh script will set those variables and run the executable.
+29
View File
@@ -0,0 +1,29 @@
[GLFW]
git_url = https://github.com/glfw/glfw.git
tag = 3.3.8
custom_commands = cd GLFW && mkdir build && cd build && cmake .. -D BUILD_SHARED_LIBS=ON && make
[Vulkan-SDK]
dl_url = https://sdk.lunarg.com/sdk/download/1.3.250.1/linux/vulkansdk-linux-x86_64-1.3.250.1.tar.gz
custom_commands = tar -xf Vulkan-SDK && mv 1.3.250.1 vulkan && rm Vulkan-SDK && cd vulkan && ./vulkansdk -j 4 glslang vulkan-headers loader layers tools shaderc vulkan-extensionlayer
[vma]
git_url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
tag = v3.0.1
[Criterion]
git_url = https://github.com/Snaipe/Criterion.git
tag = v2.4.2
custom_commands = cd Criterion && meson build && cd build && ninja
[stb]
git_url = https://github.com/nothings/stb.git
tag = master
[FreeType]
dl_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.10.0.tar.gz
custom_commands = tar -xf FreeType && mv freetype-2.10.0 freetype && rm FreeType && cd freetype && cmake -E make_directory build && cmake -E chdir build cmake -D BUILD_SHARED_LIBS:BOOL=true .. && cmake --build build
[CMock]
git_url = https://github.com/throwtheswitch/cmock.git
custom_commands = cd cmock && bundle install
-18
View File
@@ -1,18 +0,0 @@
rse_events_sources = [
'src/events_manager.c'
]
sdl3_dep = dependency('sdl3',
version : '>=3.4.0')
rse_events_lib = shared_library(
'rse_events',
rse_events_sources,
include_directories : [
'../'
],
dependencies : [sdl3_dep,
],
link_with: rse_utilities_lib,
install : true
)
-26
View File
@@ -1,26 +0,0 @@
/**
* @file events_manager.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Events manager for Red Scarf Engine
* @version 0.1
* @date 2026-03-02
*
* @copyright Copyright (c) 2026
*
*/
#ifndef RSE_EVENTS_MANAGER_H_
#define RSE_EVENTS_MANAGER_H_
#include "src/events_context.h"
#include "utilities/commons.h"
rse_err_t rse_events_init(struct events_context_t* events_context);
rse_err_t rse_events_set_event_handler(struct events_context_t* events_context,
enum EVENT_HANDLER event_type,
rse_event_handler_t event_handler);
rse_err_t rse_events_main_loop(struct events_context_t* context);
#endif /* RSE_EVENTS_MANAGER_H_ */
-44
View File
@@ -1,44 +0,0 @@
/**
* @file events_context.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Events context for Red Scarf Engine
* @version 0.1
* @date 2026-03-02
*
* @copyright Copyright (c) 2026
*
*/
#ifndef EVENT_CONTEXT_H_
#define EVENT_CONTEXT_H_
#include "SDL3/SDL_mouse.h"
#include "utilities/commons.h"
enum EVENT_HANDLER {
EVENT_HANDLER_QUIT,
EVENT_HANDLER_MOUSE,
EVENT_HANDLER_LAST
};
enum EVENT_SHOULD_STOP_ENGINE {
EVENT_SHOULD_STOP_ENGINE_FALSE,
EVENT_SHOULD_STOP_ENGINE_TRUE
};
struct mouse_event_data_t
{
float x_pos;
float y_pos;
SDL_MouseButtonFlags mouse_buttons;
};
typedef rse_err_t (*rse_event_handler_t)(void*);
struct events_context_t
{
enum EVENT_SHOULD_STOP_ENGINE should_stop;
rse_event_handler_t event_handlers[EVENT_HANDLER_LAST];
};
#endif /* EVENT_CONTEXT_H_ */
-99
View File
@@ -1,99 +0,0 @@
/**
* @file events_manager.c
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Events manager for Red Scarf Engine
* @version 0.1
* @date 2026-03-02
*
* @copyright Copyright (c) 2026
*
*/
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_log.h"
#include "SDL3/SDL_mouse.h"
#include "events_context.h"
#include "rse_events_manager.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
static rse_err_t default_event(void* args)
{
(void)args;
return RSE_ERROR_NO_ERROR;
}
static rse_err_t run_custom_event_handler(struct events_context_t* context,
enum EVENT_HANDLER event_handler,
void* args)
{
return context->event_handlers[event_handler](args);
}
static rse_err_t quit_event(struct events_context_t* context)
{
context->should_stop = EVENT_SHOULD_STOP_ENGINE_TRUE;
return run_custom_event_handler(context, EVENT_HANDLER_QUIT, NULL);
}
static rse_err_t mouse_event(struct events_context_t* context)
{
struct mouse_event_data_t mouse_data = {0};
mouse_data.mouse_buttons = SDL_GetMouseState(&mouse_data.x_pos, &mouse_data.y_pos);
return run_custom_event_handler(context, EVENT_HANDLER_MOUSE, (void*)&mouse_data);
}
rse_err_t rse_events_init(struct events_context_t* events_context)
{
size_t i = 0U;
for (i = 0U; i < EVENT_HANDLER_LAST; ++i) {
events_context->event_handlers[i] = default_event;
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t rse_events_set_event_handler(struct events_context_t* events_context,
enum EVENT_HANDLER event_type,
rse_event_handler_t event_handler)
{
if (event_type >= EVENT_HANDLER_LAST) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Invalid event type selected");
return RSE_ERROR_INVALID_PARAM;
}
if (event_handler == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Provided NULL event handler");
return RSE_ERROR_INVALID_PARAM;
}
events_context->event_handlers[event_type] = event_handler;
return RSE_ERROR_NO_ERROR;
}
rse_err_t rse_events_main_loop(struct events_context_t* context)
{
SDL_Event event;
rse_err_t status = RSE_ERROR_NO_ERROR;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_EVENT_QUIT:
STATUS_CHECK(quit_event(context));
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
STATUS_CHECK(mouse_event(context));
break;
}
}
return status;
}
+77
View File
@@ -0,0 +1,77 @@
find_package(Vulkan REQUIRED)
find_package(VulkanMemoryAllocator CONFIG REQUIRED)
find_package(glfw3 REQUIRED)
find_package(FreeType REQUIRED)
find_package(Vulkan COMPONENTS glslc)
find_program(glslc_executable NAMES glslc HINTS ${Vulkan_GLSLC_EXECUTABLE}/..)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror")
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/shaders)
cmake_policy(SET CMP0116 NEW)
function(compile_shader)
cmake_parse_arguments(PARSE_ARGV 1 arg "" "ENV;FORMAT" "SOURCES")
foreach(source ${arg_SOURCES})
message(${source}.${arg_FORMAT})
add_custom_command(
OUTPUT ${source}.${arg_FORMAT}
DEPENDS ${source}
COMMAND
${glslc_executable}
-o ${source}.${arg_FORMAT}
${CMAKE_CURRENT_SOURCE_DIR}/${source}
)
endforeach()
endfunction()
compile_shader(graphics
ENV opengl
FORMAT num
SOURCES
shaders/shader.vert
shaders/solid_objects.frag
shaders/on_screen_text.frag
)
add_library(graphics SHARED
shaders/shader.vert.num
shaders/solid_objects.frag.num
shaders/on_screen_text.frag.num
)
target_include_directories(graphics PRIVATE
${PROJECT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}
${Vulkan_INCLUDE_DIR}
${vma_INCLUDE_DIR}
${glfw3_INCLUDE_DIR}
${stb_INCLUDE_DIR}
${FreeType_INCLUDE_DIR}
)
target_sources(graphics PRIVATE
src/vma_port.cpp
src/window.c
src/vulkan_base.c
src/vulkan_commands.c
src/vulkan_buffers.c
src/rse_graphics.c
src/math.c
src/mesh_controller.c
src/vulkan_commons.c
src/vulkan_image.c
src/vulkan_render_pass.c
src/vulkan_swapchain.c
src/text_renderer.c
src/pipeline_builder.c
src/descriptor_builder.c
)
target_link_libraries(graphics PUBLIC
utilities
${Vulkan_LIBRARIES}
glfw
${freetype}
)
-58
View File
@@ -1,58 +0,0 @@
rse_graphics_sources = [
'src/descriptor_builder.c',
'src/font_manager.c',
'src/mesh_controller.c',
'src/pipeline_builder.c',
'src/renderer.c',
'src/rse_graphics.c',
'src/vma_port.cpp',
'src/vulkan_base.c',
'src/vulkan_buffers.c',
'src/vulkan_commands.c',
'src/vulkan_image.c',
'src/vulkan_render_pass.c',
'src/vulkan_swapchain.c',
'src/window.c',
]
vulkan_dep = dependency('Vulkan')
sdl3_dep = dependency('sdl3', version: '>=3.4.0', default_options : ['warning_level=0', 'werror=false'])
freetype_dep = dependency('freetype2', version: '>=22.1.16')
glm_dep = dependency('cglm', version: '>=0.9.6', method: 'cmake')
vma_dep = dependency('VulkanMemoryAllocator', version: '>=3.3.0')
stb = dependency('stb',
fallback: ['stb', 'stb_dep'])
rse_graphics_lib = shared_library(
'rse_graphics',
rse_graphics_sources,
override_options: [
'warning_level=3',
'werror=true'
],
include_directories: ['../'],
dependencies: [
vulkan_dep,
vma_dep,
sdl3_dep,
freetype_dep,
glm_dep,
stb
],
link_with: rse_utilities_lib,
install: true,
)
rse_graphics_lib_dep = declare_dependency(
link_with: rse_graphics_lib,
dependencies: [
vulkan_dep,
vma_dep,
glm_dep
],
include_directories: include_directories('.')
)
+5 -8
View File
@@ -1,9 +1,10 @@
#ifndef RSE_GRAPHICS_H #ifndef RSE_GRAPHICS_H
#define RSE_GRAPHICS_H #define RSE_GRAPHICS_H
#include "src/graphics_context.h"
#include "utilities/commons.h" #include "utilities/commons.h"
struct rse_graphics_context_t;
/** /**
* @brief Initializes graphics backend. This has to be called before any new mesh, texture or any other object is * @brief Initializes graphics backend. This has to be called before any new mesh, texture or any other object is
* added * added
@@ -11,24 +12,20 @@
* @param context Graphics context handle. Must be NULL! * @param context Graphics context handle. Must be NULL!
* @return rse_err_t RSE_ERROR_NO_ERROR on success * @return rse_err_t RSE_ERROR_NO_ERROR on success
*/ */
rse_err_t rse_graphics_init(struct graphics_context_t* context); rse_err_t rse_graphics_init(struct rse_graphics_context_t** context);
/** /**
* @brief Runs graphics engine. This has to be called after all objects are added to the scene * @brief Runs graphics engine. This has to be called after all objects are added to the scene
* *
* @return rse_err_t RSE_ERROR_NO_ERROR on success * @return rse_err_t RSE_ERROR_NO_ERROR on success
*/ */
rse_err_t rse_graphics_run(struct graphics_context_t* context); rse_err_t rse_graphics_run(struct rse_graphics_context_t* context);
rse_err_t rse_graphics_main_loop(struct graphics_context_t* context);
/** /**
* @brief Custom function for testing engine. TODO: Remove it whe releasing * @brief Custom function for testing engine. TODO: Remove it whe releasing
* *
*/ */
rse_err_t rse_graphics_test_function(struct graphics_context_t* context); void rse_graphics_test_function(struct rse_graphics_context_t* context);
void rse_graphics_deinit(struct graphics_context_t* context);
#endif /* RSE_GRAPHICS_H */ #endif /* RSE_GRAPHICS_H */
+17 -4
View File
@@ -1,8 +1,21 @@
#version 450 #version 450
layout(set = 0, binding = 0) uniform sampler2D overlay_tex; /* Each time texture count is changed this value also need to change. I have to fix it somehow. */
layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 out_color; #extension GL_EXT_nonuniform_qualifier : enable
layout(set = 1, binding = 1) uniform sampler2D texSampler[];
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 2) in flat uint instanceTextureId;
layout(location = 0) out vec4 outColor;
void main() { void main() {
out_color = texture(overlay_tex, uv); /* vec4 sampled = vec4(1.0, 1.0, 1.0, texture(texSampler[instanceTextureId], fragTexCoord).r);*/
/* if (sampled.a < 0.1)
discard;
outColor = sampled; */
outColor = vec4(fragColor, texture(texSampler[instanceTextureId], fragTexCoord).r);
} }
-13
View File
@@ -1,13 +0,0 @@
#version 450
layout(location = 0) out vec2 uv;
vec2 positions[3] = vec2[](
vec2(-1.0, -1.0),
vec2(3.0, -1.0),
vec2(-1.0, 3.0)
);
void main() {
uv = (positions[gl_VertexIndex] + 1.0) * 0.5;
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
}
+16 -24
View File
@@ -1,27 +1,20 @@
#version 450 #version 450
layout(push_constant) uniform pc { layout(binding = 0) uniform UniformBufferObject {
mat4 model; mat4 model;
mat4 view; mat4 view;
mat4 proj; mat4 proj;
} ubo; } ubo;
struct InstanceData
{
vec3 pos;
vec3 rot;
float scale;
uint texture_id;
};
layout(set=0, binding = 0, std430) readonly buffer InstanceBuffer {
InstanceData instances[];
};
layout(location = 0) in vec3 inPosition; layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor; layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord; layout(location = 2) in vec2 inTexCoord;
layout(location = 3) in vec3 instancePosition;
layout(location = 4) in vec3 instanceRotation;
layout(location = 5) in float instanceScale;
layout(location = 6) in uint instanceTextureId;
layout(location = 0) out vec3 fragColor; layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord; layout(location = 1) out vec2 fragTexCoord;
layout(location = 2) out uint outInstanceTextureId; layout(location = 2) out uint outInstanceTextureId;
@@ -29,29 +22,28 @@ layout(location = 2) out uint outInstanceTextureId;
void main() void main()
{ {
InstanceData instance = instances[gl_InstanceIndex];
mat4 gRotMat; mat4 gRotMat;
mat3 mx, my, mz; mat3 mx, my, mz;
// rotate around x // rotate around x
float s = sin(instance.rot.x + 0); float s = sin(instanceRotation.x + 0);
float c = cos(instance.rot.x + 0); float c = cos(instanceRotation.x + 0);
mx[0] = vec3(1, 0.0, 0.0); mx[0] = vec3(1, 0.0, 0.0);
mx[1] = vec3(0, c, -s); mx[1] = vec3(0, c, -s);
mx[2] = vec3(0.0, s, c); mx[2] = vec3(0.0, s, c);
// rotate around y // rotate around y
s = sin(instance.rot.y + 0); s = sin(instanceRotation.y + 0);
c = cos(instance.rot.y + 0); c = cos(instanceRotation.y + 0);
my[0] = vec3(c, 0.0, s); my[0] = vec3(c, 0.0, s);
my[1] = vec3(0.0, 1.0, 0.0); my[1] = vec3(0.0, 1.0, 0.0);
my[2] = vec3(-s, 0.0, c); my[2] = vec3(-s, 0.0, c);
// rot around z // rot around z
s = sin(instance.rot.z + 0); s = sin(instanceRotation.z + 0);
c = cos(instance.rot.z + 0); c = cos(instanceRotation.z + 0);
mz[0] = vec3(c, -s, 0.0); mz[0] = vec3(c, -s, 0.0);
mz[1] = vec3(s, c, 0.0); mz[1] = vec3(s, c, 0.0);
@@ -59,19 +51,19 @@ void main()
mat3 rotMat = mz * my * mx; mat3 rotMat = mz * my * mx;
s = sin(instance.rot.y + 0); s = sin(instanceRotation.y + 0);
c = cos(instance.rot.y + 0); c = cos(instanceRotation.y + 0);
gRotMat[0] = vec4(c, 0.0, s, 0.0); gRotMat[0] = vec4(c, 0.0, s, 0.0);
gRotMat[1] = vec4(0.0, 1.0, 0.0, 0.0); gRotMat[1] = vec4(0.0, 1.0, 0.0, 0.0);
gRotMat[2] = vec4(-s, 0.0, c, 0.0); gRotMat[2] = vec4(-s, 0.0, c, 0.0);
gRotMat[3] = vec4(0.0, 0.0, 0.0, 1.0); gRotMat[3] = vec4(0.0, 0.0, 0.0, 1.0);
vec4 locPos = vec4(inPosition.xyz * rotMat, 1.0); vec4 locPos = vec4(inPosition.xyz * rotMat, 1.0);
vec4 pos = vec4((locPos.xyz * instance.scale) + instance.pos, 1.0); vec4 pos = vec4((locPos.xyz * instanceScale) + instancePosition, 1.0);
gl_Position = ubo.proj * ubo.view * ubo.model * pos; gl_Position = ubo.proj * ubo.view * ubo.model * pos;
fragColor = inColor; fragColor = inColor;
fragTexCoord = inTexCoord; fragTexCoord = inTexCoord;
outInstanceTextureId = instance.texture_id; outInstanceTextureId = instanceTextureId;
} }
+1 -1
View File
@@ -1,7 +1,7 @@
#version 450 #version 450
#extension GL_EXT_nonuniform_qualifier : enable #extension GL_EXT_nonuniform_qualifier : enable
layout(set = 0, binding = 1) uniform sampler2D texSampler[]; layout(set = 1, binding = 1) uniform sampler2D texSampler[];
layout(location = 0) in vec3 fragColor; layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord; layout(location = 1) in vec2 fragTexCoord;
+280 -156
View File
@@ -11,195 +11,319 @@
#include "descriptor_builder.h" #include "descriptor_builder.h"
#include <SDL3/SDL_log.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "src/graphics_context.h" #include "src/graphics_context.h"
#include "src/vulkan_commons.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/linked_list.h"
#include "utilities/localization.h"
#include "utilities/logger.h"
#include "vulkan/vulkan_core.h" #include "vulkan/vulkan_core.h"
#include "vulkan_errors.h"
#define POOL_MAX_SETS (32U) RSE_LINKED_LIST_DEFINE(descriptor_set_layout_list_t, VkDescriptorSetLayoutBinding);
rse_err_t descriptor_pool_add_type(struct graphics_context_t* context, VkDescriptorType type, uint32_t count) struct descriptor_t
{ {
context->descriptor_data.pool_sizes[context->descriptor_data.pool_sizes_count].type = type; uint32_t binding;
context->descriptor_data.pool_sizes[context->descriptor_data.pool_sizes_count].descriptorCount = count; VkDescriptorType type;
uint32_t descriptor_count;
VkShaderStageFlags shader_stages;
VkSampler immutable_sampler;
};
context->descriptor_data.pool_sizes_count++; RSE_LINKED_LIST_DEFINE(descriptor_set_list_t, struct descriptor_t);
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_pool_initialize(struct graphics_context_t* context) struct descriptor_set_handle_t
{ {
VkDescriptorPoolCreateInfo create_info = {0}; VkDescriptorSet descriptor_sets;
VkDescriptorPool descriptor_pool;
VkDescriptorSetLayout descriptor_sets_layout;
};
create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; /* FIXME: When new descriptors are introduced to Vulkan, this enum has to be
create_info.pNext = NULL; * updated */
create_info.flags = 0U; enum descriptor_type_id_t
create_info.maxSets = POOL_MAX_SETS; {
create_info.poolSizeCount = context->descriptor_data.pool_sizes_count; DESCRIPTOR_TYPE_SAMPLER_ID,
create_info.pPoolSizes = context->descriptor_data.pool_sizes; DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER_ID,
DESCRIPTOR_TYPE_SAMPLED_IMAGE_ID,
DESCRIPTOR_TYPE_STORAGE_IMAGE_ID,
DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER_ID,
DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER_ID,
DESCRIPTOR_TYPE_UNIFORM_BUFFER_ID,
DESCRIPTOR_TYPE_STORAGE_BUFFER_ID,
DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC_ID,
DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC_ID,
DESCRIPTOR_TYPE_INPUT_ATTACHMENT_ID,
DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_ID,
DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR_ID,
DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV_ID,
DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM_ID,
DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM_ID,
DESCRIPTOR_TYPE_MUTABLE_EXT_ID,
DESCRIPTOR_TYPE_ID_MAX_ENUM,
};
if (vkCreateDescriptorPool(context->vulkan_handles.device, rse_err_t descriptor_add_set(struct descriptor_set_list_t* descriptor_set_list, uint32_t binding, VkDescriptorType type,
&create_info, uint32_t descriptor_count, VkShaderStageFlags shader_stages, VkSampler sampler)
NULL, {
&context->descriptor_data.descriptor_pool) != VK_SUCCESS) { struct descriptor_t descriptor_sets = {.binding = binding,
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create descriptor pool"); .type = type,
return RSE_ERROR_INTERNAL_ERROR; .descriptor_count = descriptor_count,
} .shader_stages = shader_stages,
.immutable_sampler = sampler};
RSE_LINKED_LIST_PUSH_BACK(descriptor_set_list, descriptor_sets);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t descriptor_create_new_set(struct graphics_context_t* context, rse_id_t* set_id) rse_err_t descriptors_build(struct graphics_context_t* context, struct descriptor_set_list_t* descriptors,
{ struct descriptor_set_handle_t* descriptor_set_handle)
if (context->descriptor_data.descriptor_sets_count >= DESCRIPTOR_MAX_SETS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Reached maximum number of available descriptor sets");
return RSE_ERROR_INTERNAL_ERROR;
}
*set_id = context->descriptor_data.descriptor_sets_count++;
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_add_layout(struct descriptor_set_layout_bindings_t* layout_bindings,
uint32_t binding,
VkDescriptorType type,
uint32_t descriptor_count,
VkShaderStageFlags shader_stages)
{
layout_bindings->layout_bindings[layout_bindings->layout_bindings_count].binding = binding;
layout_bindings->layout_bindings[layout_bindings->layout_bindings_count].descriptorType = type;
layout_bindings->layout_bindings[layout_bindings->layout_bindings_count].descriptorCount = descriptor_count;
layout_bindings->layout_bindings[layout_bindings->layout_bindings_count].stageFlags = shader_stages;
layout_bindings->layout_bindings_count++;
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_set_finish(struct graphics_context_t* context,
struct descriptor_set_layout_bindings_t* layout_bindings,
rse_id_t set_id)
{
VkDescriptorSetLayoutCreateInfo create_info = {0};
create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
create_info.bindingCount = layout_bindings->layout_bindings_count;
create_info.pBindings = layout_bindings->layout_bindings;
if (VK_SUCCESS != vkCreateDescriptorSetLayout(context->vulkan_handles.device,
&create_info,
NULL,
&context->descriptor_data.set_layouts[set_id])) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create descriptor sets layout");
return RSE_ERROR_INTERNAL_ERROR;
}
layout_bindings->layout_bindings_count = 0U;
rse_memset(layout_bindings->layout_bindings, 0, sizeof(VkDescriptorSetLayoutBinding) * DESCRIPTOR_MAX_SETS);
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_build_sets(struct graphics_context_t* context)
{ {
size_t iterator = 0U;
size_t pool_size_count = 0U;
uint32_t pool_max_sets = 0U;
uint32_t sets_per_type = 0U;
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
VkDescriptorPoolCreateInfo pool_create_info = {0};
VkDescriptorPoolCreateFlags pool_create_flags = 0U; // TODO: Flags not set anywhere
VkDescriptorPoolSize pool_sizes[DESCRIPTOR_TYPE_ID_MAX_ENUM] = {0};
VkDescriptorPoolSize requested_pool_sizes[DESCRIPTOR_TYPE_ID_MAX_ENUM] = {0};
VkDescriptorSetAllocateInfo alloc_info = {0}; VkDescriptorSetAllocateInfo alloc_info = {0};
VkDescriptorSetLayoutBinding layout_bindings[DESCRIPTOR_TYPE_ID_MAX_ENUM] = {0};
VkDescriptorSetLayoutCreateInfo layout_create_info = {0};
VkDescriptorSetLayout set_layout = VK_NULL_HANDLE;
VkDescriptorSet descriptor_sets = VK_NULL_HANDLE;
if (NULL == context) { if (NULL == context) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided NULL context"); LOGF(_("Provided NULL context\n"));
return RSE_ERROR_NULL_POINTER; return RSE_ERROR_NULL_POINTER;
} }
if (context->descriptor_data.descriptor_pool == VK_NULL_HANDLE) { iterator = 0U;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, RSE_LINKED_LIST_FOREACH(descriptors, descriptor)
"Requested descriptor build, but descriptor pool has not been initialized"); {
switch (descriptor->data.type) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
pool_sizes[DESCRIPTOR_TYPE_SAMPLER_ID].type = VK_DESCRIPTOR_TYPE_SAMPLER;
pool_sizes[DESCRIPTOR_TYPE_SAMPLER_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
pool_sizes[DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER_ID].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_sizes[DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
pool_sizes[DESCRIPTOR_TYPE_SAMPLED_IMAGE_ID].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
pool_sizes[DESCRIPTOR_TYPE_SAMPLED_IMAGE_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
pool_sizes[DESCRIPTOR_TYPE_STORAGE_IMAGE_ID].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
pool_sizes[DESCRIPTOR_TYPE_STORAGE_IMAGE_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
pool_sizes[DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER_ID].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
pool_sizes[DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
pool_sizes[DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER_ID].type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
pool_sizes[DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_ID].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_ID].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC_ID].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC_ID].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
pool_sizes[DESCRIPTOR_TYPE_INPUT_ATTACHMENT_ID].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
pool_sizes[DESCRIPTOR_TYPE_INPUT_ATTACHMENT_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:
pool_sizes[DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_ID].type = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK;
pool_sizes[DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR_ID].type =
VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV:
pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV_ID].type =
VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV;
pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM:
pool_sizes[DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM_ID].type =
VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM;
pool_sizes[DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM:
pool_sizes[DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM_ID].type = VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM;
pool_sizes[DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM_ID].descriptorCount++;
break;
case VK_DESCRIPTOR_TYPE_MUTABLE_EXT:
pool_sizes[DESCRIPTOR_TYPE_MUTABLE_EXT_ID].type = VK_DESCRIPTOR_TYPE_MUTABLE_EXT;
pool_sizes[DESCRIPTOR_TYPE_MUTABLE_EXT_ID].descriptorCount++;
break;
default:
LOGF(_("Invalid descriptor type provided\n"));
return VULKAN_ERROR_UNKNOWN_DESCRIPTOR_TYPE;
}
layout_bindings[iterator].binding = descriptor->data.binding;
layout_bindings[iterator].descriptorCount = descriptor->data.descriptor_count;
layout_bindings[iterator].descriptorType = descriptor->data.type;
layout_bindings[iterator].pImmutableSamplers = descriptor->data.immutable_sampler;
layout_bindings[iterator].stageFlags = descriptor->data.shader_stages;
iterator++;
}
layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layout_create_info.bindingCount = iterator;
layout_create_info.pBindings = layout_bindings;
if (VK_SUCCESS != vkCreateDescriptorSetLayout(context->device, &layout_create_info, NULL, &set_layout)) {
LOGF(_("Failed to create descriptor sets layout\n"));
return VULKAN_ERROR_DESCRIPTOR_SET_LAYOUT_CREATION_FAILED;
}
/* Another loop. Seems bit too many? TODO: Optimize this later.
* What I'm doing here is calculating minimum viable number of pool max sets. This is a bit tricky, as
* (unintuitively) we have to select the MINIMUM number of sets, not maximum. This makes sense, but is difficult to
* wrap headr around */
RSE_LINKED_LIST_FOREACH(descriptors, descriptor)
{
switch (descriptor->data.type) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
sets_per_type =
pool_sizes[DESCRIPTOR_TYPE_SAMPLER_ID].descriptorCount / descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
sets_per_type =
pool_sizes[DESCRIPTOR_TYPE_SAMPLED_IMAGE_ID].descriptorCount / descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
sets_per_type =
pool_sizes[DESCRIPTOR_TYPE_STORAGE_IMAGE_ID].descriptorCount / descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
sets_per_type =
pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_ID].descriptorCount / descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
sets_per_type =
pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_ID].descriptorCount / descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
sets_per_type =
pool_sizes[DESCRIPTOR_TYPE_INPUT_ATTACHMENT_ID].descriptorCount / descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM:
sets_per_type = pool_sizes[DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM_ID].descriptorCount /
descriptor->data.descriptor_count;
break;
case VK_DESCRIPTOR_TYPE_MUTABLE_EXT:
sets_per_type =
pool_sizes[DESCRIPTOR_TYPE_MUTABLE_EXT_ID].descriptorCount / descriptor->data.descriptor_count;
break;
default:
LOGF(_("Invalid descriptor type provided\n"));
return VULKAN_ERROR_UNKNOWN_DESCRIPTOR_TYPE;
}
if (pool_max_sets == 0 || pool_max_sets > sets_per_type) {
pool_max_sets = sets_per_type;
}
}
/* Allocate pool based on layout */
/* create array of required descriptors */
for (iterator = 0U; iterator < DESCRIPTOR_TYPE_ID_MAX_ENUM; ++iterator) {
if (pool_sizes[iterator].descriptorCount > 0) {
requested_pool_sizes[pool_size_count].type = pool_sizes[iterator].type;
requested_pool_sizes[pool_size_count].descriptorCount = pool_sizes[iterator].descriptorCount;
pool_size_count++;
}
}
pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_create_info.pNext = NULL;
pool_create_info.flags = pool_create_flags;
pool_create_info.maxSets = pool_max_sets;
pool_create_info.poolSizeCount = pool_size_count;
pool_create_info.pPoolSizes = requested_pool_sizes;
if (VK_SUCCESS != vkCreateDescriptorPool(context->device, &pool_create_info, NULL, &descriptor_pool)) {
LOGF(_("Failed to create descriptor pool\n"));
return VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED;
} }
/* Allocate descriptors from the pool */ /* Allocate descriptors from the pool */
alloc_info.descriptorPool = context->descriptor_data.descriptor_pool; alloc_info.descriptorPool = &descriptor_pool;
alloc_info.descriptorSetCount = context->descriptor_data.descriptor_sets_count; alloc_info.descriptorSetCount =
alloc_info.pSetLayouts = context->descriptor_data.set_layouts; pool_max_sets; /* TODO: Always allocating maximum number of sets? This might get weird */
alloc_info.pSetLayouts = set_layout;
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
if (VK_SUCCESS != vkAllocateDescriptorSets(context->vulkan_handles.device, if (VK_SUCCESS != vkAllocateDescriptorSets(context->device, &alloc_info, &descriptor_sets)) {
&alloc_info, LOGF(_("Failed to create descriptor sets\n"));
context->descriptor_data.descriptor_sets)) { return VULKAN_ERROR_DESCRIPTOR_SET_CREATION_FAILED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create descriptor sets");
return RSE_ERROR_INTERNAL_ERROR;
} }
descriptor_set_handle->descriptor_pool = descriptor_pool;
descriptor_set_handle->descriptor_sets = descriptor_sets;
descriptor_set_handle->descriptor_sets_layout = set_layout;
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t descriptor_attach_images(struct graphics_context_t* context, rse_err_t descriptor_get_layouts()
rse_id_t descriptor_set_id,
rse_id_t* texture_ids,
size_t images_count,
VkSampler sampler,
uint32_t binding)
{
size_t iter = 0U;
VkDescriptorImageInfo* image_infos = NULL;
VkWriteDescriptorSet write_set = {0};
rse_malloc(image_infos, sizeof(VkDescriptorImageInfo) * images_count);
for (iter = 0U; iter < images_count; ++iter) {
image_infos[iter].sampler = sampler;
image_infos[iter].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[iter].imageView = context->texture_images[texture_ids[iter]].image_view;
}
write_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_set.pImageInfo = image_infos;
write_set.dstSet = context->descriptor_data.descriptor_sets[descriptor_set_id];
write_set.dstBinding = binding;
write_set.dstArrayElement = 0U;
write_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; // TODO: Maybe not only samplers?
write_set.descriptorCount = images_count;
vkUpdateDescriptorSets(context->vulkan_handles.device, 1U, &write_set, 0, NULL);
rse_free(image_infos);
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_attach_buffer(struct graphics_context_t* context,
rse_id_t descriptor_set_id,
struct vulkan_buffer_t* buffer,
uint32_t binding,
VkDescriptorType type)
{
VkDescriptorBufferInfo buffer_info = {0};
VkWriteDescriptorSet write_set = {0};
buffer_info.buffer = buffer->buffer;
buffer_info.offset = 0U;
buffer_info.range = buffer->allocated_size;
write_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_set.pBufferInfo = &buffer_info;
write_set.dstSet = context->descriptor_data.descriptor_sets[descriptor_set_id];
write_set.dstBinding = binding;
write_set.dstArrayElement = 0U;
write_set.descriptorType = type;
write_set.descriptorCount = 1;
vkUpdateDescriptorSets(context->vulkan_handles.device, 1U, &write_set, 0, NULL);
return RSE_ERROR_NO_ERROR;
}
void destroy_descriptors(struct graphics_context_t* context)
{
size_t iter = 0U;
for (iter = 0U; iter < context->descriptor_data.descriptor_sets_count; ++iter) {
vkDestroyDescriptorSetLayout(context->vulkan_handles.device, context->descriptor_data.set_layouts[iter], NULL);
}
vkDestroyDescriptorPool(context->vulkan_handles.device, context->descriptor_data.descriptor_pool, NULL);
}
+14 -43
View File
@@ -12,61 +12,32 @@
#ifndef DESCRIPTOR_BUILDER_H #ifndef DESCRIPTOR_BUILDER_H
#define DESCRIPTOR_BUILDER_H #define DESCRIPTOR_BUILDER_H
#include <stdint.h>
#include <vulkan/vulkan_core.h>
#include "graphics_context.h"
struct descriptor_set_handle_t; struct descriptor_set_handle_t;
struct descriptor_set_list_t; struct descriptor_set_list_t;
rse_err_t descriptor_pool_add_type(struct graphics_context_t* context, VkDescriptorType type, uint32_t count);
rse_err_t descriptor_pool_initialize(struct graphics_context_t* context);
rse_err_t descriptor_create_new_set(struct graphics_context_t* context, rse_id_t* set_id);
rse_err_t descriptor_add_layout(struct descriptor_set_layout_bindings_t* layout_bindings,
uint32_t binding,
VkDescriptorType type,
uint32_t descriptor_count,
VkShaderStageFlags shader_stages);
rse_err_t descriptor_set_finish(struct graphics_context_t* context,
struct descriptor_set_layout_bindings_t* layout_bindings,
rse_id_t set_id);
rse_err_t descriptor_build_sets(struct graphics_context_t* context);
/** /**
* @brief Add descriptor set to provided list * @brief Add descriptor set to provided list
* *
* @param descriptor_set_handle Handle for given descriptor set * @param descriptor_set_list List to be filled
* @param binding Descriptors binding * @param binding Descriptors binding
* @param type Type of descriptors * @param type Type of descriptors
* @param descriptor_count Number of descriptors in this set * @param descriptor_count Number of descriptors in this set
* @param shader_stages In what stages this descriptors will be used * @param shader_stages In what stages this descriptors will be used
* @param sampler If descriptor set is a sampler, provide it here
* @return rse_err_t RSE_ERROR_NO_ERROR on success, error code on failure * @return rse_err_t RSE_ERROR_NO_ERROR on success, error code on failure
*/ */
rse_err_t descriptor_add_set(struct descriptor_set_handle_t* descriptor_set_handle, rse_err_t descriptor_add_set(struct descriptor_set_list_t* descriptor_set_list, uint32_t binding, VkDescriptorType type,
uint32_t binding, uint32_t descriptor_count, VkShaderStageFlags shader_stages, VkSampler sampler);
VkDescriptorType type,
uint32_t descriptor_count,
VkShaderStageFlags shader_stages);
rse_err_t descriptor_attach_images(struct graphics_context_t* context, /**
rse_id_t descriptor_set_id, * @brief Allocate descriptor sets from provided list. The data will be stored in opaque descriptor handle
rse_id_t* texture_ids, *
size_t images_count, * @param context Graphics context
VkSampler sampler, * @param descriptors List of descriptors to build descriptor handle from.
uint32_t binding); * @param descriptor_set_handle Opaque descriptor handle object.
* @return rse_err_t RSE_ERROR_NO_ERROR on success, error code on failure
rse_err_t descriptor_attach_buffer(struct graphics_context_t* context, */
rse_id_t descriptor_set_id, rse_err_t descriptors_build(struct graphics_context_t* context, struct descriptor_set_list_t* descriptors,
struct vulkan_buffer_t* buffer, struct descriptor_set_handle_t* descriptor_set_handle);
uint32_t binding,
VkDescriptorType type);
void destroy_descriptors(struct graphics_context_t* context);
#endif // !DESCRIPTOR_BUILDER_H #endif // !DESCRIPTOR_BUILDER_H
+26
View File
@@ -0,0 +1,26 @@
/**
* @file font_errors.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief
* @version 0.1
* @date 2023-10-06
*
* @copyright Copyright (c) 2023
*
*/
#ifndef FONT_ERRORS_H
#define FONT_ERRORS_H
#define RSE_FONT_MODULE_ID 0x0400U
/* Error codes */
enum window_error_t {
FONT_ERROR_INIT_FAILED = RSE_FONT_MODULE_ID,
FONT_ERROR_LOAD_FAILED,
FONT_ERROR_CHANGE_SIZE_FAILED,
FONT_ERROR_LOAD_GLYPH_FAILED,
FONT_ERROR_RENDER_GLYPH_FAILED
};
#endif /* WINDOW_ERRORS_H */
-229
View File
@@ -1,229 +0,0 @@
/**
* @file font_manager.c
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Font manager. Load fonts from file and render it on the scene
* @version 0.1
* @date 2025-10-08
*
* @copyright Copyright (c) 2025
*
*/
#include "font_manager.h"
#include <SDL3/SDL_log.h>
#include <assert.h>
#include <cglm/types.h>
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/fttypes.h>
#include <stdint.h>
#include <string.h>
#include <vulkan/vulkan_core.h>
#include "freetype/freetype.h"
#include "graphics_context.h"
#include "mesh_controller.h"
#include "src/vulkan_commons.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/file_utils.h"
#include "vulkan_image.h"
#define FONTS_START_CHARACTER_CODE (33U)
FT_Library g_font_library = NULL;
FT_Face g_font_faces[FONTS_MAX_COUNT] = {NULL};
size_t g_font_free_index = 0U;
rse_err_t fonts_init(void)
{
FT_Error error = FT_Err_Ok;
error = FT_Init_FreeType(&g_font_library);
if (error != FT_Err_Ok) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize FreeType2. Error code: %d", error);
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path, rse_id_t* font_id)
{
FT_Error error = FT_Err_Ok;
rse_err_t ret = RSE_ERROR_NO_ERROR;
char real_path[MAX_PATH_LENGTH] = {0};
assert(context != NULL);
assert(file_path != NULL);
assert(font_id != NULL);
assert(g_font_library != NULL);
assert(g_font_free_index < FONTS_MAX_COUNT);
strncpy(real_path, file_path, MAX_PATH_LENGTH);
if ((ret = file_append_full_path(real_path, MAX_PATH_LENGTH)) != RSE_ERROR_NO_ERROR) {
return ret;
}
error = FT_New_Face(g_font_library, real_path, 0, &g_font_faces[g_font_free_index]);
if (error != FT_Err_Ok) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to load new font face. Error code: %d", error);
return RSE_ERROR_INTERNAL_ERROR;
}
*font_id = g_font_free_index++;
return RSE_ERROR_NO_ERROR;
}
rse_err_t fonts_set_font_size(struct graphics_context_t* context, const rse_id_t font_id, const uint32_t font_size)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
// size_t iter = 0U;
FT_UInt glyph_index = 0U;
FT_Error error = FT_Err_Ok;
FT_GlyphSlot glyph = NULL;
struct vertex_t vertices[4] = {0};
uint16_t indices[] = {0, 1, 2, 2, 3, 0};
struct sized_font_data_t* font = NULL;
assert(context != NULL);
assert(font_id < FONTS_MAX_COUNT);
assert(g_font_faces[font_id] != NULL);
error = FT_Set_Pixel_Sizes(g_font_faces[font_id], 0, font_size);
if (error != FT_Err_Ok) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to set font size. Error code: %d", error);
return RSE_ERROR_INTERNAL_ERROR;
}
font = &context->fonts_data.fonts[font_id];
font->font_size = font_size;
// for (iter = 0U; iter < FONT_CHARACTERS_COUNT; ++iter) {
// glyph_index = iter + FONTS_START_CHARACTER_CODE;
glyph_index = 33 + FONTS_START_CHARACTER_CODE;
error = FT_Load_Char(g_font_faces[font_id], glyph_index, FT_LOAD_RENDER);
if (error != FT_Err_Ok) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to load glyph with id %d. Error code: %d",
glyph_index,
error);
// continue;
return RSE_ERROR_INTERNAL_ERROR;
}
glyph = g_font_faces[font_id]->glyph;
STATUS_CHECK(load_texture_from_bitmat(context,
glyph->bitmap.buffer,
glyph->bitmap.width,
glyph->bitmap.rows,
&font->font_characters[33].texture_id,
VK_FORMAT_R8_UNORM));
vertices[0].pos[0] = 0.0 - glyph->bitmap.width;
vertices[0].pos[1] = glyph->bitmap.rows;
vertices[0].pos[2] = 0.0;
vertices[0].color[0] = 1.0;
vertices[0].color[1] = 1.0;
vertices[0].color[2] = 1.0;
vertices[0].tex_coords[0] = 0.0;
vertices[0].tex_coords[1] = 1.0;
vertices[1].pos[0] = glyph->bitmap.width;
vertices[1].pos[1] = glyph->bitmap.rows;
vertices[1].pos[2] = 0.0;
vertices[1].color[0] = 1.0;
vertices[1].color[1] = 1.0;
vertices[1].color[2] = 1.0;
vertices[1].tex_coords[0] = 1.0;
vertices[1].tex_coords[1] = 1.0;
vertices[2].pos[0] = glyph->bitmap.width;
vertices[2].pos[1] = 0.0 - glyph->bitmap.rows;
vertices[2].pos[2] = 0.0;
vertices[2].color[0] = 1.0;
vertices[2].color[1] = 1.0;
vertices[2].color[2] = 1.0;
vertices[2].tex_coords[0] = 1.0;
vertices[2].tex_coords[1] = 0.0;
vertices[3].pos[0] = 0.0 - glyph->bitmap.width;
vertices[3].pos[1] = 0.0 - glyph->bitmap.rows;
vertices[3].pos[2] = 0.0;
vertices[3].color[0] = 1.0;
vertices[3].color[1] = 1.0;
vertices[3].color[2] = 1.0;
vertices[3].tex_coords[0] = 0.0;
vertices[3].tex_coords[1] = 0.0;
STATUS_CHECK(mesh_create(context, vertices, indices, 4, 6, &font->font_characters[33].mesh_id));
// }
return RSE_ERROR_NO_ERROR;
}
rse_err_t print_debug_text(struct graphics_context_t* context, rse_id_t font_id, char* text)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
(void)text;
STATUS_CHECK(mesh_create_instance(
context,
context->fonts_data.fonts[font_id].font_characters[33].mesh_id,
(struct instance_data_t){{0.0f, 0.0f, 2.4f},
{0.0f, 0.0f, 0.0},
1.0f,
context->fonts_data.fonts[font_id].font_characters[33].texture_id}));
return status;
}
// rse_err_t print_debug_text(struct graphics_context_t* context, char* text)
// {
// rse_err_t status = RSE_ERROR_NO_ERROR;
// size_t width, height;
// size_t iter = 0U;
// int number_of_quads = 0;
// char buffer[99999] = {0};
// float *v0, *v2 = NULL;
//
// int min_x, min_y, max_x, max_y = 0;
// int yy, xx;
//
// width = context->swapchain_data.swapchain_extent.width;
// height = context->swapchain_data.swapchain_extent.height;
//
// context->debug_overlay.pixels_size = width * height * sizeof(uint32_t);
//
// rse_memset(context->debug_overlay.pixels, 0, context->debug_overlay.pixels_size);
//
// number_of_quads = stb_easy_font_print(10.0, 20.0, text, NULL, (void*)buffer, sizeof(buffer));
//
// for (iter = 0U; iter < number_of_quads * 4; ++iter) {
// v0 = (float*)&buffer[iter * 16 + 0];
// v2 = (float*)&buffer[iter * 16 + 32];
//
// min_x = v0[0];
// min_y = v0[1];
// max_x = v2[0];
// max_y = v2[1];
//
// for (yy = min_y; yy < max_y; ++yy) {
// for (xx = min_x; xx < max_x; ++xx) {
// if (xx >= 0 && yy >= 0 && xx < (int)width && yy < (int)height) {
// context->debug_overlay.pixels[yy * width + xx] = 0xFFFFFFFF; // RGBA white
// }
// }
// }
// }
//
// STATUS_CHECK(texture_update_image(context, (unsigned char*)context->debug_overlay.pixels, width, height,
// context->debug_overlay.texture_id, VK_FORMAT_R8G8B8A8_UNORM));
//
// return RSE_ERROR_NO_ERROR;
// }
-23
View File
@@ -1,23 +0,0 @@
/**
* @file font_manager.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Font manager. Load fonts from file and render it on the scene
* @version 0.1
* @date 2025-10-08
*
* @copyright Copyright (c) 2025
*
*/
#ifndef FONT_MANAGER_H
#define FONT_MANAGER_H
#include "utilities/commons.h"
#include "graphics_context.h"
rse_err_t fonts_init(void);
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path, rse_id_t* font_id);
rse_err_t fonts_set_font_size(struct graphics_context_t* context, const rse_id_t font_id, const uint32_t font_size);
rse_err_t print_debug_text(struct graphics_context_t* context, rse_id_t font_id, char *text);
#endif /* FONT_MANAGER_H */
+58 -223
View File
@@ -1,6 +1,6 @@
/** /**
* @file graphics_context.h * @file graphics_context.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz) * @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Graphics context for Red Scarf Engine * @brief Graphics context for Red Scarf Engine
* @version 0.1 * @version 0.1
* @date 2024-05-22 * @date 2024-05-22
@@ -12,24 +12,18 @@
#ifndef GRAPHICS_CONTEXT_H #ifndef GRAPHICS_CONTEXT_H
#define GRAPHICS_CONTEXT_H #define GRAPHICS_CONTEXT_H
#include <SDL3/SDL.h> #include <GLFW/glfw3.h>
#include <SDL3/SDL_render.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <vma/vk_mem_alloc.h>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include "utilities/entity.h" #include "utilities/linked_list.h"
#include "utilities/vector.h"
#include "vk_mem_alloc.h"
#include "vulkan/vulkan_core.h" #include "vulkan/vulkan_core.h"
#include "vulkan_commons.h" #include "vulkan_commons.h"
#define MAX_MESH_NUMBER 256U #define MAX_MESH_NUMBER 256
/* FIXME: This is the total number of instances * meshes. It shouldn't be so low */ #define MAX_INSTANCE_NUMBER 256
#define MAX_INSTANCE_NUMBER (MAX_MESH_NUMBER * 256U)
#define SWAPCHAIN_IMAGE_COUNT (3U)
/** /**
* @brief Maximum number of textures that can be loaded * @brief Maximum number of textures that can be loaded
@@ -37,37 +31,9 @@
*/ */
#define RSE_MAX_IMAGE_COUNT 256 #define RSE_MAX_IMAGE_COUNT 256
#define MAX_VULKAN_BUFFERS_COUNT (32U)
#define MAX_PIPELINE_COUNT 16 #define MAX_PIPELINE_COUNT 16
#define MAX_CREATE_INFOS (32U)
#define MAX_SHADER_STAGES 12U
#define MAX_SHADER_MODULES_COUNT (128U)
#define MAX_VERTEX_BINDINGS_COUNT (32U)
#define MAX_SHADER_ATTRIBUTES_COUNT (32U)
#define MAX_DYNAMIC_STATES_COUNT (32U)
#define MAX_PUSH_CONSTANTS_COUNT (32U)
#define DESCRIPTOR_MAX_SETS (32U) RSE_LINKED_LIST_DEFINE(shader_module_list_t, VkShaderModule);
#define DESCRIPTOR_TYPE_ID_MAX_ENUM (21U)
#define RENDERER_MAX_COUNT MAX_PIPELINE_COUNT
#define FONT_CHARACTERS_COUNT (94U)
#define FONTS_MAX_COUNT (32U)
struct graphics_context_t;
enum QUEUE_FAMILY_INDEX {
QUEUE_FAMILY_INDEX_GRAPHICS,
QUEUE_FAMILY_INDEX_PRESENTATION,
QUEUE_FAMILY_INDEX_LAST_INDEX,
};
enum RENDER_TARGET {
RENDER_TARGET_3D,
RENDER_TARGET_2D,
RENDER_TARGET_COUNT
};
/** /**
* @brief Represents data within vertex or instance buffer. * @brief Represents data within vertex or instance buffer.
@@ -79,11 +45,6 @@ struct buffer_data_t
size_t buffer_offset; size_t buffer_offset;
}; };
struct vulkan_buffers_t {
size_t vulkan_buffers_count;
struct vulkan_buffer_t vulkan_buffers[MAX_VULKAN_BUFFERS_COUNT];
};
/** /**
* @brief Wrapper for Vulkan buffer. Holds allocation data; * @brief Wrapper for Vulkan buffer. Holds allocation data;
* *
@@ -98,7 +59,16 @@ struct vulkan_image_t
VmaAllocationInfo allocation_info; VmaAllocationInfo allocation_info;
}; };
RSE_VECTOR_DEFINE(instance_vector_t, struct instance_data_t); /**
* @brief Represents instance of a mesh and shows where it's located in instance buffer
*
*/
struct internal_instance_data_t
{
uint8_t instance_taken;
uint8_t instance_buffer_offset;
};
/** /**
* @brief Mesh data, with unique id and dynamic arrays holding vertices and indices. * @brief Mesh data, with unique id and dynamic arrays holding vertices and indices.
* One mesh can have multiple instances. * One mesh can have multiple instances.
@@ -106,197 +76,62 @@ RSE_VECTOR_DEFINE(instance_vector_t, struct instance_data_t);
*/ */
struct mesh_t struct mesh_t
{ {
size_t vertex_count; uint8_t is_mesh_id_taken;
bool is_dirty; uint32_t unique_id;
size_t entities_count; uint8_t instance_count;
entity_t* entities; /* Which entities are connected to this mesh */ struct buffer_data_t vertex_data;
size_t index_count; struct buffer_data_t index_data;
size_t instances_count; struct internal_instance_data_t instance_data[MAX_INSTANCE_NUMBER];
struct instance_vector_t instances_data;
};
struct shader_modules_t
{
uint16_t shader_modules_count;
VkShaderModule shader_modules[MAX_SHADER_MODULES_COUNT];
VkShaderStageFlagBits shaders_stages[MAX_SHADER_MODULES_COUNT];
};
struct required_dynamic_states_t {
size_t dynamic_states_count;
VkDynamicState dynamic_states[MAX_DYNAMIC_STATES_COUNT];
};
struct push_constant_ranges_t
{
size_t push_constant_ranges_count;
VkPushConstantRange push_constant_ranges[MAX_PUSH_CONSTANTS_COUNT];
};
struct pipeline_infos_t
{
size_t pipeline_create_infos_count;
VkGraphicsPipelineCreateInfo create_infos[MAX_CREATE_INFOS];
};
struct pipeline_t
{
VkPipeline pipeline;
uint8_t id;
/* Shader stages */
size_t shader_stages_count;
VkPipelineShaderStageCreateInfo shader_stages[MAX_SHADER_STAGES];
/* Input assembly */
VkPipelineInputAssemblyStateCreateInfo input_assembly;
/* Viewport state */
VkPipelineViewportStateCreateInfo viewport_state;
/* Rasterizer */
VkPipelineRasterizationStateCreateInfo rasterizer;
/* Multisampling */
VkPipelineMultisampleStateCreateInfo multisampling;
/* Color blending */
VkPipelineColorBlendAttachmentState color_blend_attachment;
VkPipelineColorBlendStateCreateInfo color_blending;
/* Depth stencil */
VkPipelineDepthStencilStateCreateInfo depth_stencil;
VkPipelineLayout pipeline_layout;
size_t vertex_bindings_count;
VkVertexInputBindingDescription vertex_bindings[MAX_VERTEX_BINDINGS_COUNT];
size_t shader_attributes_count;
VkVertexInputAttributeDescription shader_attributes[MAX_SHADER_ATTRIBUTES_COUNT];
size_t dynamic_states_count;
VkDynamicState dynamic_states[MAX_DYNAMIC_STATES_COUNT];
struct push_constant_ranges_t push_constant_ranges;
struct required_dynamic_states_t required_dynamic_states;
struct descriptor_set_handle_t* descriptor_sets;
}; };
struct pipeline_internal_t struct pipeline_internal_t
{ {
rse_id_t descriptor_set_ids[MAX_PIPELINE_COUNT];
size_t pipelines_count; size_t pipelines_count;
size_t pipeline_layouts_count;
VkPipeline pipelines[MAX_PIPELINE_COUNT]; VkPipeline pipelines[MAX_PIPELINE_COUNT];
VkPipelineLayout pipeline_layouts[MAX_PIPELINE_COUNT]; VkPipelineLayout pipeline_layout;
};
struct descriptor_data_t
{
uint32_t pool_sizes_count;
uint32_t descriptor_sets_count;
VkDescriptorPool descriptor_pool;
VkDescriptorPoolSize pool_sizes[DESCRIPTOR_TYPE_ID_MAX_ENUM];
VkDescriptorSet descriptor_sets[DESCRIPTOR_MAX_SETS];
VkDescriptorSetLayout set_layouts[DESCRIPTOR_MAX_SETS];
};
struct descriptor_set_layout_bindings_t
{
VkDescriptorSetLayoutBinding layout_bindings[DESCRIPTOR_MAX_SETS];
uint32_t layout_bindings_count;
};
struct render_target_t
{
struct vulkan_buffer_t vertex_buffer;
struct vulkan_buffer_t index_buffer;
size_t instances_data_size;
struct vulkan_buffer_t instance_buffer;
struct vulkan_buffer_t draw_indirect_command_buffer;
};
struct swapchain_data_t
{
VkSwapchainKHR swapchain;
VkImageView swapchain_image_views[SWAPCHAIN_IMAGE_COUNT];
VkExtent2D swapchain_extent;
VkFramebuffer swapchain_framebuffers[SWAPCHAIN_IMAGE_COUNT];
VkImage swapchain_images[SWAPCHAIN_IMAGE_COUNT];
};
struct vulkan_handles
{
VkSampler sampler;
VkPhysicalDevice physical_device;
VkDevice device;
VmaAllocator allocator;
VkSurfaceKHR surface;
VkInstance instance;
VkQueue graphics_queue;
VkQueue present_queue;
VkCommandPool command_pool;
VkCommandBuffer command_buffers[SWAP_BUFFER_COUNT];
VkRenderPass render_pass;
VkSemaphore image_available_semaphores[SWAP_BUFFER_COUNT];
VkSemaphore render_finished_semaphores[SWAP_BUFFER_COUNT];
VkSemaphore timeline_semaphore;
#ifndef NDEBUG
VkDebugUtilsMessengerEXT debug_messenger;
#endif // !NDEBUG
};
struct mesh_data_t {
uint32_t mesh_count;
struct mesh_t mesh_bucket[MAX_MESH_NUMBER];
};
struct debug_overlay_t
{
rse_id_t texture_id;
uint32_t* pixels;
size_t pixels_size;
struct vulkan_image_t image_handle;
VkSampler sampler;
};
typedef rse_err_t(*renderer_function_t)(struct graphics_context_t* context, rse_id_t renderer_id);
struct renderer_t {
renderer_function_t render_function;
};
struct renderer_data_t {
size_t renderers_count;
struct renderer_t renderers[RENDERER_MAX_COUNT];
};
struct font_character_data_t {
rse_id_t mesh_id;
rse_id_t texture_id;
};
struct sized_font_data_t {
uint8_t font_size;
struct font_character_data_t font_characters[FONT_CHARACTERS_COUNT];
};
struct fonts_data_t {
struct sized_font_data_t fonts[FONTS_MAX_COUNT];
}; };
struct graphics_context_t struct graphics_context_t
{ {
bool is_framebuffer_resized; GLFWwindow* window_handle;
uint32_t queue_family_indices[QUEUE_FAMILY_INDEX_LAST_INDEX]; VkDevice device;
uint32_t current_frame; VkSurfaceKHR surface;
VkInstance instance;
VkQueue graphics_queue;
VmaAllocator allocator;
VkPhysicalDevice physical_device;
VkCommandPool command_pool;
VkCommandBuffer command_buffers[SWAP_BUFFER_COUNT];
VkImageView* swapchain_image_views;
VkExtent2D swapchain_extent;
VkRenderPass render_pass;
VkFramebuffer* swapchain_framebuffers;
VkSwapchainKHR swapchain;
uint32_t swapchain_images_count; uint32_t swapchain_images_count;
uint64_t frame_finished_idx; VkImage* swapchain_images;
SDL_Window* window_handle; VkQueue present_queue;
struct mvp_data_t mvp_data; VkSemaphore image_available_semaphores[SWAP_BUFFER_COUNT];
struct renderer_data_t renderer_data; VkSemaphore render_finished_semaphores[SWAP_BUFFER_COUNT];
struct debug_overlay_t debug_overlay; VkFence in_flight_fences[SWAP_BUFFER_COUNT];
struct vulkan_handles vulkan_handles;
struct swapchain_data_t swapchain_data;
struct descriptor_data_t descriptor_data;
struct pipeline_internal_t pipelines_data; struct pipeline_internal_t pipelines_data;
struct render_target_t render_targets[RENDER_TARGET_COUNT]; #ifndef NDEBUG
struct vulkan_buffers_t uniform_buffers; VkDebugUtilsMessengerEXT debug_messenger;
#endif // !NDEBUG
bool is_framebuffer_resized; // TODO: Consider moving this to heap
uint32_t current_frame;
uint32_t queue_family_indices[1];
struct vulkan_buffer_t uniform_buffers[SWAP_BUFFER_COUNT];
/* GPU visible buffer */
size_t vertex_offset;
struct vulkan_buffer_t vertex_buffer;
struct vulkan_buffer_t index_buffer;
struct vulkan_buffer_t instance_buffers[MAX_MESH_NUMBER];
struct vulkan_buffer_t draw_indirect_command_buffer;
struct vulkan_image_t texture_images[RSE_MAX_IMAGE_COUNT]; struct vulkan_image_t texture_images[RSE_MAX_IMAGE_COUNT];
struct vulkan_image_t depth_image; struct vulkan_image_t depth_image;
struct vulkan_image_t color_image; struct vulkan_image_t color_image;
struct shader_modules_t shader_modules; struct shader_module_list_t* shader_modules;
struct mesh_data_t mesh_data; struct mesh_t meshes[MAX_MESH_NUMBER];
struct fonts_data_t fonts_data;
}; };
#endif /* GRAPHICS_CONTEXT_H */ #endif /* GRAPHICS_CONTEXT_H */
+148
View File
@@ -0,0 +1,148 @@
#include "math.h"
#include <math.h>
#define PI 3.14159265359f
static struct vec3_t substract_vec3(const struct vec3_t* vec1, const struct vec3_t* vec2)
{
struct vec3_t result;
result.x = vec1->x - vec2->x;
result.y = vec1->y - vec2->y;
result.z = vec1->z - vec2->z;
return result;
}
static float length_vec3(const struct vec3_t* vec)
{
return sqrt((vec->x * vec->x)
+(vec->y * vec->y)
+(vec->z * vec->z));
}
static float scalar_cross_vec3(const struct vec3_t* vec1, const struct vec3_t* vec2)
{
return (vec1->x * vec2->x)
+(vec1->y * vec2->y)
+(vec1->z * vec2->z);
}
float math_deg_to_radians(float degrees)
{
return degrees * (PI/180.0f);
}
struct vec3_t math_cross_vec3(const struct vec3_t* vec1, const struct vec3_t* vec2)
{
struct vec3_t result;
result.x = (vec1->y * vec2->z) - (vec1->z * vec2->y);
result.y = (vec1->z * vec2->x) - (vec1->x * vec2->z);
result.z = (vec1->x * vec2->y) - (vec1->y * vec2->x);
return result;
}
struct vec3_t math_normalize_vec3(const struct vec3_t* vec)
{
struct vec3_t result = {0};
float vec_length = 0.0f;
vec_length = length_vec3(vec);
result.x = vec->x / vec_length;
result.y = vec->y / vec_length;
result.z = vec->z / vec_length;
return result;
}
struct mat4_t math_uniform_mat4()
{
struct mat4_t result= {0.0f};
result.x_1 = 1.0f;
result.y_2 = 1.0f;
result.z_3 = 1.0f;
result.w_4 = 1.0f;
return result;
}
struct mat4_t math_perspective(float fovy, float aspect_ratio, float z_near, float z_far)
{
float tan_half_fovy = 0;
struct mat4_t result = {0};
tan_half_fovy = tan(fovy / 2.0f);
result.x_1 = -1.0f / (aspect_ratio * tan_half_fovy);
result.y_2 = 1.0f / (tan_half_fovy);
result.z_3 = - (z_far + z_near) / (z_far - z_near);
result.w_3 = -1.0f;
result.z_4 = - (2.0f * z_far * z_near) / (z_far - z_near);
return result;
}
struct mat4_t math_look_at(const struct vec3_t* eye, const struct vec3_t* center, const struct vec3_t* up)
{
struct mat4_t result = {0.0f};
struct vec3_t forward_vec = {0};
struct vec3_t right_vec = {0};
struct vec3_t up_vec = {0};
struct vec3_t tmp = substract_vec3(eye, center);
forward_vec = math_normalize_vec3(&tmp);
tmp = math_cross_vec3(up, &forward_vec);
right_vec = math_normalize_vec3(&tmp);
up_vec = math_cross_vec3(&forward_vec, &right_vec);
result.x_1 = right_vec.x;
result.x_2 = right_vec.y;
result.x_3 = right_vec.z;
result.y_1 = up_vec.x;
result.y_2 = up_vec.y;
result.y_3 = up_vec.z;
result.z_1 = forward_vec.x;
result.z_2 = forward_vec.y;
result.z_3 = forward_vec.z;
result.x_4 = -scalar_cross_vec3(&right_vec, eye);
result.y_4 = -scalar_cross_vec3(&up_vec, eye);
result.z_4 = -scalar_cross_vec3(&forward_vec, eye);
result.w_1 = 0;
result.w_2 = 0;
result.w_3 = 0;
result.w_4 = 1;
return result;
}
struct mat4_t math_rotate(struct mat4_t matrix, float radians, struct vec3_t rotation_vector)
{
struct mat4_t result = math_uniform_mat4();
float cos = cosf(radians);
float sin = sinf(radians);
//TODO: Switch to quaternion
result.x_1 = cos + powf(rotation_vector.x, 2.0f) * (1 - cos);
result.x_2 = (rotation_vector.x * rotation_vector.y * (1 - cos)) - (rotation_vector.z * sin);
result.x_3 = (rotation_vector.x * rotation_vector.z * (1 - cos)) + (rotation_vector.y * sin);
result.y_1 = (rotation_vector.y * rotation_vector.x * (1 - cos)) + rotation_vector.z * sin;
result.y_2 = cos + (powf(rotation_vector.y, 2) * (1 - cos));
result.y_3 = (rotation_vector.y * rotation_vector.z * (1 - cos)) - (rotation_vector.x * sin);
result.z_1 = (rotation_vector.z * rotation_vector.x * (1 - cos)) - (rotation_vector.y * sin);
result.z_2 = (rotation_vector.z * rotation_vector.y * (1 - cos)) + (rotation_vector.x * sin);
result.z_3 = cos + (powf(rotation_vector.z, 2.0f) * (1 - cos));
return result;
}
+122
View File
@@ -0,0 +1,122 @@
/**
* @file math.h
* @author Piotr Krygier (piotr.krygier@)
* @brief RedScarfEngine math operations
* @version 0.1
* @date 2023-08-07
*
* @copyright Copyright (c) 2023
*
*/
#ifndef MATH_H
#define MATH_H
/**
* @brief Simple representation of vector
*
*/
struct vec3_t {
float x;
float y;
float z;
};
/* 4x4 matrix.
* [x_1 | x_2 | x_3 | x_4]
* [y_1 | y_2 | y_3 | y_4]
* [z_1 | x_2 | z_3 | z_4]
* [z_1 | w_2 | w_3 | w_4]
*
* Vulkan and OpenGL expects matrix to be passed by columns, this is why members are created in this order.
* */
struct mat4_t {
/* First column */
float x_1;
float y_1;
float z_1;
float w_1;
/* Second column */
float x_2;
float y_2;
float z_2;
float w_2;
/* Third column */
float x_3;
float y_3;
float z_3;
float w_3;
/* Fourth column */
float x_4;
float y_4;
float z_4;
float w_4;
};
/**
* @brief Calculate radians from provided degree
*
* @param degrees degrees
* @return float radians
*/
float math_deg_to_radians(float degrees);
/**
* @brief Create an uniform matrix, the one with ones on diagonal
*
* @return struct mat4_t Diagonal matrix
*/
struct mat4_t math_uniform_mat4();
/**
* @brief Normalizes provided vector. Changes the values inside the vector to range 0.0...1.0
*
* @param vec Vector to normalize
* @return struct vec3_t Normalized vector
*/
struct vec3_t math_normalize_vec3(const struct vec3_t* vec);
/**
* @brief Creates vector cross product, a vector, using right hand rule. Note, that order of vectors is important
*
* @param vec1 Input vector
* @param vec2 Input vector
* @return struct vec3_t Resulting cross product vector
*/
struct vec3_t math_cross_vec3(const struct vec3_t* vec1, const struct vec3_t* vec2);
/**
* @brief Calculates transformation matrix for viewing object from and eye cooridinates
*
* @param eye Location of the camera or the eye
* @param center Where are we looking at
* @param up Where "up" is located. Usually (0, 1, 0), since Y is up in most cases
* @return struct mat4_t Resulting lootAtMatrix
*/
struct mat4_t math_look_at(const struct vec3_t* eye, const struct vec3_t* center, const struct vec3_t* up);
/**
* @brief Creates perspective matrix, used in projection
*
* @param fovy_rad Field of view, in radians
* @param aspect_ratio Aspect ratio (usally screen width / height)
* @param z_near How near objects we can see
* @param z_far How far objects we can see
* @return struct mat4_t Resulting perspective matrix
*/
struct mat4_t math_perspective(float fovy_rad, float aspect_ratio, float z_near, float z_far);
/**
* @brief Rotate matrix around vector for radians degrees
*
* @param matrix Matrix to rotate
* @param radians Degrees
* @param rotation_vector Rotation vector
* @return struct mat4_t resulting matrix
*/
struct mat4_t math_rotate(struct mat4_t matrix, float radians, struct vec3_t rotation_vector);
#endif /* MATH_H */
+72 -190
View File
@@ -1,209 +1,91 @@
/** #include "mesh_controller.h"
* @file mesh_controller.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz) #include "vulkan_buffers.h"
* @brief Graphics context for Red Scarf Engine #include "vulkan_errors.h"
* @version 0.1 #include "utilities/errors_common.h"
* @date 2025-09-23 #include "utilities/logger.h"
* #include "utilities/localization.h"
* @copyright Copyright (c) 2025
*
*/
#include <SDL3/SDL_log.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include "src/graphics_context.h"
#include "src/vulkan_commons.h"
#include "utilities/cgltf.h"
#include "utilities/commons.h"
#include "utilities/entity.h"
#include "utilities/errors_common.h"
#include "utilities/file_utils.h"
#include "vulkan_buffers.h"
#include "vulkan_image.h"
rse_err_t mesh_create(struct graphics_context_t* context, #define MESH_ID_FREE 0
struct vertex_t* vertices, #define MESH_ID_TAKEN 1
uint16_t create_mesh(struct graphics_context_t* context, struct vertex_t* vertices,
uint16_t* indices, uint16_t* indices,
size_t vertices_count, size_t vertices_count,
size_t indices_count, size_t indices_count)
rse_id_t* mesh_id)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; size_t iter = 0U;
if (mesh_id == NULL) { for(iter = 0U; iter < MAX_MESH_NUMBER; ++iter) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided mesh id handle is NULL"); if(context->meshes[iter].is_mesh_id_taken == MESH_ID_FREE) {
return RSE_ERROR_NULL_POINTER; break;
}
} }
*mesh_id = context->mesh_data.mesh_count; if(iter >= MAX_MESH_NUMBER) {
LOGE(_("Could not allocate new mesh, reached maximum.\n"));
if (context->mesh_data.mesh_count > MAX_MESH_NUMBER) { return MAX_MESH_NUMBER;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Could not allocate new mesh, reached maximum.");
return RSE_ERROR_INTERNAL_ERROR;
} }
STATUS_CHECK(add_vertices(context, *mesh_id, vertices_count, vertices, indices_count, indices)); context->meshes[iter].is_mesh_id_taken = MESH_ID_TAKEN;
context->mesh_data.mesh_count++; context->meshes[iter].unique_id = iter;
context->meshes[iter].vertex_data.count = vertices_count;
context->meshes[iter].index_data.count = indices_count;
context->meshes[iter].instance_count = 0;
memset(context->meshes[iter].instance_data, 0, sizeof(struct internal_instance_data_t) * MAX_INSTANCE_NUMBER);
return status; add_vertices(context, iter, vertices, indices, &context->meshes[iter].vertex_data, &context->meshes[iter].index_data);
return iter;
} }
rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char* file_name, rse_id_t* mesh_id, rse_id_t* texture_id) rse_err_t create_mesh_instance(struct graphics_context_t* context, uint16_t mesh_id,
{
struct vertex_t* vertices = NULL;
uint16_t* indices = NULL;
size_t indices_count = 0U;
size_t mesh_index = 0U;
size_t primitive_index = 0U;
size_t attribute_index = 0U;
cgltf_mesh* mesh = NULL;
cgltf_primitive* primitive = NULL;
cgltf_attribute* attribute = NULL;
size_t* position_data_offset = NULL;
size_t* color_data_offset = NULL;
size_t* texture_coord_data_offset = NULL;
size_t* indices_data_offset = NULL;
size_t* texture_data_offset = NULL;
int* texture_data_size = NULL;
unsigned char *texture_pixel_buffer = NULL;
size_t data_count = 0U;
size_t data_index = 0U;
size_t tmp_offset = 0U;
uint8_t* bin = NULL;
(void)context;
(void)mesh_id;
cgltf_data* data = NULL;
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(file_load_gltf(file_name, &data));
for (mesh_index = 0U; mesh_index < data->meshes_count; ++mesh_index) {
mesh = &data->meshes[mesh_index];
for (primitive_index = 0U; primitive_index < mesh->primitives_count; ++primitive_index) {
primitive = &mesh->primitives[primitive_index];
if (primitive->indices == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU,
"No indices for mesh found. Currently only indexed drawing is supported");
return RSE_ERROR_INVALID_PARAM;
} else {
rse_malloc(indices_data_offset, sizeof(size_t));
*indices_data_offset = primitive->indices->buffer_view->offset;
indices_count = primitive->indices->count;
rse_malloc(indices, (sizeof(uint16_t) * indices_count));
}
if (primitive->material != NULL) {
if (primitive->material->has_pbr_metallic_roughness) {
rse_malloc(texture_data_offset, sizeof(size_t));
rse_malloc(texture_data_size, sizeof(int));
*texture_data_offset = primitive->material->pbr_metallic_roughness.base_color_texture.texture->image->buffer_view->offset;
*texture_data_size = primitive->material->pbr_metallic_roughness.base_color_texture.texture->image->buffer_view->size;
}
}
for (attribute_index = 0; attribute_index < primitive->attributes_count; ++attribute_index) {
attribute = &primitive->attributes[attribute_index];
tmp_offset = attribute->data->buffer_view->offset;
switch (attribute->type) {
case cgltf_attribute_type_position:
rse_malloc(position_data_offset, sizeof(size_t));
*position_data_offset = tmp_offset;
data_count = attribute->data->count;
break;
case cgltf_attribute_type_color:
rse_malloc(color_data_offset, sizeof(size_t));
*color_data_offset = tmp_offset;
break;
case cgltf_attribute_type_texcoord:
rse_malloc(texture_coord_data_offset, sizeof(size_t));
*texture_coord_data_offset = tmp_offset;
break;
default:
break;
}
}
}
if (position_data_offset == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to retrieve position data for mesh %s", file_name);
status = RSE_ERROR_INVALID_PARAM;
goto exit;
}
if (data_count == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failure: data count for mesh is 0");
status = RSE_ERROR_INVALID_PARAM;
goto exit;
}
rse_malloc(vertices, sizeof(struct vertex_t) * data_count);
bin = (uint8_t*)data->bin;
for (data_index = 0U; data_index < data_count; ++data_index) {
rse_memcpy(vertices[data_index].pos,
(bin + *position_data_offset + (sizeof(vec3) * data_index)),
sizeof(vec3));
if (color_data_offset != NULL) {
rse_memcpy(vertices[data_index].color,
(bin + *color_data_offset + (sizeof(vec3) * data_index)),
sizeof(vec3));
} else {
vertices[data_index].color[0] = 1.0;
vertices[data_index].color[1] = 1.0;
vertices[data_index].color[2] = 1.0;
}
if (texture_coord_data_offset != NULL) {
rse_memcpy(vertices[data_index].tex_coords,
(bin + *texture_coord_data_offset + (sizeof(vec2) * data_index)),
sizeof(vec2));
}
}
if (texture_data_offset != NULL) {
rse_malloc(texture_pixel_buffer, *texture_data_size);
rse_memcpy(texture_pixel_buffer, (bin + *texture_data_offset), *texture_data_size);
STATUS_CHECK(texture_load_from_buffer(context, texture_pixel_buffer, *texture_data_size, texture_id));
}
rse_memcpy(indices, (bin + *indices_data_offset), (sizeof(uint16_t) * indices_count));
STATUS_CHECK(mesh_create(context, vertices, indices, data_count, indices_count, mesh_id));
}
exit:
rse_free(position_data_offset);
rse_free(color_data_offset);
rse_free(texture_coord_data_offset);
rse_free(indices_data_offset);
rse_free(texture_data_size);
rse_free(texture_data_offset);
rse_free(texture_pixel_buffer);
rse_free(vertices);
rse_free(indices);
cgltf_free(data);
return status;
}
rse_err_t mesh_create_instance(struct graphics_context_t* context,
entity_t entity,
struct instance_data_t instance_data) struct instance_data_t instance_data)
{ {
if (context->mesh_data.mesh_bucket[entity].instances_count >= MAX_INSTANCE_NUMBER) { size_t iter = 0U;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Could not allocate new mesh, reached maximum.");
return RSE_ERROR_INTERNAL_ERROR; for(iter = 0U; iter < MAX_INSTANCE_NUMBER; ++iter) {
if(context->meshes[mesh_id].instance_data[iter].instance_taken == MESH_ID_FREE) {
break;
}
} }
return mesh_add_instance(context, entity, &instance_data); if(iter >= MAX_INSTANCE_NUMBER) {
LOGE(_("Could not allocate new mesh, reached maximum.\n"));
return VULKAN_ERROR_MAX_INSTANCE_COUNT_REACHED;
}
context->meshes[mesh_id].instance_data[iter].instance_taken = MESH_ID_TAKEN;
if(iter >= context->meshes[mesh_id].instance_count) {
context->meshes[mesh_id].instance_count++;
}
update_mesh_instances(context, mesh_id, &instance_data);
return RSE_ERROR_NO_ERROR;
}
size_t get_vertices_count(struct graphics_context_t* context, uint16_t mesh_id)
{
return context->meshes[mesh_id].vertex_data.count;
}
size_t get_indices_count(struct graphics_context_t* context, uint16_t mesh_id)
{
return context->meshes[mesh_id].index_data.count;
}
size_t get_instances_count(struct graphics_context_t* context, uint16_t mesh_id)
{
return context->meshes[mesh_id].instance_count;
}
size_t get_vertex_offset(struct graphics_context_t* context, uint16_t mesh_id)
{
return context->meshes[mesh_id].vertex_data.buffer_offset;
} }
+18 -14
View File
@@ -12,7 +12,6 @@
#ifndef MESH_CONTROLLER_H #ifndef MESH_CONTROLLER_H
#define MESH_CONTROLLER_H #define MESH_CONTROLLER_H
#include "utilities/entity.h"
#include "vulkan_commons.h" #include "vulkan_commons.h"
#include "graphics_context.h" #include "graphics_context.h"
#include "utilities/commons.h" #include "utilities/commons.h"
@@ -26,37 +25,42 @@
* @param indices_num indices count * @param indices_num indices count
* @return uint16_t Mesh identifier. Can be useful for getting vertices, indices and instances * @return uint16_t Mesh identifier. Can be useful for getting vertices, indices and instances
*/ */
rse_err_t mesh_create(struct graphics_context_t* context, struct vertex_t* vertices, uint16_t create_mesh(struct graphics_context_t* context, struct vertex_t* vertices,
uint16_t* indices, uint16_t* indices,
size_t vertices_count, size_t vertices_num,
size_t indices_count, rse_id_t* mesh_id); size_t indices_num);
/** /**
* @brief Create a instance for the selected mesh. Instance can have its own location, rotation and scale * @brief Create a instance for the selected mesh. Instance can have its own location, rotation and scale
* *
* @param context Graphics context * @param mesh_id Mesh id
* @param entity Entity we wat to update * @param instance_data Transformation info
* @param instance_data New instance information
*/ */
rse_err_t mesh_create_instance(struct graphics_context_t* context, entity_t entity, rse_err_t create_mesh_instance(struct graphics_context_t* context, uint16_t mesh_id,
struct instance_data_t instance_data); struct instance_data_t instance_data);
rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char* file_name, rse_id_t* mesh_id, rse_id_t* texture_id);
/** /**
* @brief Get the vertex count for mesh with mesh_id ID * @brief Get the vertex count for mesh with mesh_id ID
* *
* @param mesh_id Mesh identifier * @param mesh_id Mesh identifier
* @return size_t Number of vertices * @return size_t Number of vertices
*/ */
// size_t get_vertices_count(struct graphics_context_t* context, rse_id_t mesh_id); size_t get_vertices_count(struct graphics_context_t* context, uint16_t mesh_id);
/** /**
* @brief Get the index count for mesh with mesh_id ID * @brief Get the index count for mesh with mesh_id ID
* *
w @param mesh_id Mesh identifier * @param mesh_id Mesh identifier
* @return size_t Number of indices * @return size_t Number of indices
*/ */
// size_t get_indices_count(struct graphics_context_t* context, rse_id_t mesh_id); size_t get_indices_count(struct graphics_context_t* context, uint16_t mesh_id);
/**
* @brief Get the instance count for mesh with mesh_id ID
*
* @param mesh_id Mesh identifier
* @return size_t Number of instances
*/
size_t get_instances_count(struct graphics_context_t* context, uint16_t mesh_id);
/** /**
* @brief Get vertex offset in vertex buffer for selected mesh * @brief Get vertex offset in vertex buffer for selected mesh
@@ -64,6 +68,6 @@ rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char*
* @param mesh_id Mesh identifies * @param mesh_id Mesh identifies
* @return size_t Offset * @return size_t Offset
*/ */
// size_t get_vertex_offset(struct graphics_context_t* context, rse_id_t mesh_id); size_t get_vertex_offset(struct graphics_context_t* context, uint16_t mesh_id);
#endif /* MESH_CONTROLLER_H */ #endif /* MESH_CONTROLLER_H */
-6
View File
@@ -1,6 +0,0 @@
#include "utilities/commons.h"
rse_err_t object_load_gltf(const char* file_path)
{
}
+228 -157
View File
@@ -12,69 +12,79 @@
#include "pipeline_builder.h" #include "pipeline_builder.h"
#include <assert.h> #include <assert.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "SDL3/SDL_log.h" #include "descriptor_builder.h"
#include "src/graphics_context.h" #include "src/graphics_context.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/file_utils.h" #include "utilities/file_utils.h"
#include "utilities/linked_list.h"
#include "utilities/localization.h"
#include "vulkan/vulkan_core.h" #include "vulkan/vulkan_core.h"
#include "vulkan_errors.h"
#include "vulkan_image.h"
#define MAX_BINDLESS_RESOURCES 1000 #define MAX_BINDLESS_RESOURCES 1000
rse_err_t shader_create_module(struct graphics_context_t* context, RSE_LINKED_LIST_DEFINE(vertex_binding_list_t, VkVertexInputBindingDescription);
rse_id_t* shader_module_id, RSE_LINKED_LIST_DEFINE(shader_attribute_list_t, VkVertexInputAttributeDescription);
const char* shader_path, RSE_LINKED_LIST_DEFINE(dynamic_state_list_t, VkDynamicState);
const VkShaderStageFlagBits shader_stage) RSE_LINKED_LIST_DEFINE(pipeline_infos_list_t, VkGraphicsPipelineCreateInfo);
RSE_STACK_DEFINE(required_dynamic_states_t, VkDynamicState, 16U);
struct pipeline_t
{
VkPipeline pipeline;
uint8_t id;
/* Shader stages */
size_t shader_stages_count;
VkPipelineShaderStageCreateInfo shader_stages[MAX_SHADER_STAGES];
/* Input assembly */
VkPipelineInputAssemblyStateCreateInfo input_assembly;
/* Viewport state */
VkPipelineViewportStateCreateInfo viewport_state;
/* Rasterizer */
VkPipelineRasterizationStateCreateInfo rasterizer;
/* Multisampling */
VkPipelineMultisampleStateCreateInfo multisampling;
/* Color blending */
VkPipelineColorBlendAttachmentState color_blend_attachment;
VkPipelineColorBlendStateCreateInfo color_blending;
/* Depth stencil */
VkPipelineDepthStencilStateCreateInfo depth_stencil;
struct vertex_binding_list_t* vertex_bindings;
struct shader_attribute_list_t* shader_attributes;
struct dynamic_state_list_t* dynamic_states;
struct required_dynamic_states_t required_dynamic_states;
};
/**
* @brief Creates shader module
*
* @param binary_data Shader compiled code
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
static rse_err_t create_shader_module(struct graphics_context_t* context, struct pipeline_t* pipeline,
const char* binary_data, size_t data_size)
{ {
unsigned char* buffer;
size_t buffer_size;
VkShaderModuleCreateInfo create_info = {0}; VkShaderModuleCreateInfo create_info = {0};
if (shader_module_id == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Please provide valid pointer to shader module ID\n");
}
/* Check if shader module is valid */
if (shader_stage >= VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid shader stage in pipeline");
return RSE_ERROR_INTERNAL_ERROR;
}
/* Read shader file */
file_read_bytes(shader_path, &buffer_size, NULL);
rse_malloc(buffer, buffer_size);
file_read_bytes(shader_path, &buffer_size, buffer);
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.pNext = NULL; create_info.pNext = NULL;
create_info.flags = 0U; create_info.flags = 0U;
create_info.codeSize = buffer_size; create_info.codeSize = data_size;
create_info.pCode = (const uint32_t*)(buffer); create_info.pCode = (const uint32_t*)(binary_data);
if (context->shader_modules.shader_modules_count > MAX_SHADER_MODULES_COUNT) { RSE_LINKED_LIST_PUSH_FRONT(context->shader_modules, (VkShaderModule){0});
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Reached maximum number of available shader modules");
return RSE_ERROR_INTERNAL_ERROR;
}
if (VK_SUCCESS != if (VK_SUCCESS !=
vkCreateShaderModule(context->vulkan_handles.device, vkCreateShaderModule(context->device, &create_info, NULL, &RSE_LINKED_LIST_FRONT(context->shader_modules))) {
&create_info, LOGF(_("Failed to create a shader\n"));
NULL, return VULKAN_ERROR_SHADER_CREATION_FAILED;
&context->shader_modules.shader_modules[context->shader_modules.shader_modules_count])) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create a shader");
return RSE_ERROR_INTERNAL_ERROR;
} }
context->shader_modules.shaders_stages[context->shader_modules.shader_modules_count] = shader_stage;
*shader_module_id = context->shader_modules.shader_modules_count++;
rse_free(buffer);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
@@ -102,13 +112,11 @@ static rse_err_t pipeline_add_viewport_state(struct pipeline_t* pipeline)
pipeline->viewport_state.pScissors = NULL; /* This is set during rendering */ pipeline->viewport_state.pScissors = NULL; /* This is set during rendering */
if (pipeline->viewport_state.pViewports == NULL || pipeline->viewport_state.viewportCount == 0U) { if (pipeline->viewport_state.pViewports == NULL || pipeline->viewport_state.viewportCount == 0U) {
pipeline->required_dynamic_states.dynamic_states[pipeline->required_dynamic_states.dynamic_states_count++] = RSE_STACK_PUSH(pipeline->required_dynamic_states, VK_DYNAMIC_STATE_VIEWPORT);
VK_DYNAMIC_STATE_VIEWPORT;
} }
if (pipeline->viewport_state.pScissors == NULL || pipeline->viewport_state.scissorCount == 0U) { if (pipeline->viewport_state.pScissors == NULL || pipeline->viewport_state.scissorCount == 0U) {
pipeline->required_dynamic_states.dynamic_states[pipeline->required_dynamic_states.dynamic_states_count++] = RSE_STACK_PUSH(pipeline->required_dynamic_states, VK_DYNAMIC_STATE_SCISSOR);
VK_DYNAMIC_STATE_SCISSOR;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
@@ -195,29 +203,33 @@ static rse_err_t pipeline_add_depth_stencil(struct pipeline_t* pipeline)
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t pipeline_add_descriptor_sets(struct graphics_context_t* context, /**
struct pipeline_t* pipeline, * @brief Create a Pipeline Layout
rse_id_t descriptor_set_id) *
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED
*/
static rse_err_t create_pipeline_layout(struct graphics_context_t* context,
struct descriptors_set_handle_t* descriptor_set_handle)
{ {
uint32_t descriptor_set_layouts_count = 0;
VkDescriptorSetLayout descriptor_set_layouts = VK_NULL_HANDLE;
VkPipelineLayoutCreateInfo create_info = {0}; VkPipelineLayoutCreateInfo create_info = {0};
create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
create_info.pNext = NULL; create_info.pNext = NULL;
create_info.flags = 0U; create_info.flags = 0U;
create_info.setLayoutCount = 1; // FIXME: Make sure we can enable more than one layout create_info.setLayoutCount = context->pipelines_data.descriptor_set_layouts_count;
create_info.pSetLayouts = &context->descriptor_data.set_layouts[descriptor_set_id]; create_info.pSetLayouts = context->pipelines_data.descriptor_set_layouts;
create_info.pushConstantRangeCount = pipeline->push_constant_ranges.push_constant_ranges_count; create_info.pushConstantRangeCount = 0U;
create_info.pPushConstantRanges = pipeline->push_constant_ranges.push_constant_ranges; create_info.pPushConstantRanges = NULL;
if (VK_SUCCESS != if (VK_SUCCESS !=
vkCreatePipelineLayout(context->vulkan_handles.device, &create_info, NULL, &pipeline->pipeline_layout)) { vkCreatePipelineLayout(context->device, &create_info, NULL, &context->pipelines_data.pipeline_layout)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create pipeline layout"); LOGF(_("Failed to create pipeline layout\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED;
} }
context->pipelines_data.descriptor_set_ids[context->pipelines_data.pipelines_count] = descriptor_set_id;
context->pipelines_data.pipeline_layouts[context->pipelines_data.pipeline_layouts_count++] =
pipeline->pipeline_layout;
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
@@ -228,107 +240,139 @@ rse_err_t pipeline_add_descriptor_sets(struct graphics_context_t* context,
* *
* @param[in/out] pipelines List of pipelines to clean up * @param[in/out] pipelines List of pipelines to clean up
*/ */
static void pipeline_infos_cleanup(struct pipeline_infos_t* pipelines) static void pipeline_builder_cleanup(struct pipeline_infos_list_t* pipelines)
{ {
size_t pipeline_count = pipelines->pipeline_create_infos_count; size_t pipeline_count = RSE_LINKED_LIST_SIZE(pipelines);
VkVertexInputBindingDescription* vertex_bindings = NULL;
VkVertexInputAttributeDescription* shader_attributes = NULL;
VkDynamicState* dynamic_states = NULL;
for (size_t i = 0; i < pipeline_count; i++) { for (size_t i = 0; i < pipeline_count; i++) {
VkGraphicsPipelineCreateInfo* pipeline_info = &pipelines->create_infos[i]; VkGraphicsPipelineCreateInfo* pipeline_info = &RSE_LINKED_LIST_FRONT(pipelines);
if (pipeline_info != NULL) {
vertex_bindings =
(VkVertexInputBindingDescription*)pipeline_info->pVertexInputState->pVertexBindingDescriptions;
shader_attributes =
(VkVertexInputAttributeDescription*)pipeline_info->pVertexInputState->pVertexAttributeDescriptions;
dynamic_states = (VkDynamicState*)pipeline_info->pDynamicState->pDynamicStates;
rse_free((VkPipelineVertexInputStateCreateInfo*)pipeline_info->pVertexInputState); rse_free((VkPipelineVertexInputStateCreateInfo*)pipeline_info->pVertexInputState);
rse_free((VkPipelineDynamicStateCreateInfo*)pipeline_info->pDynamicState); rse_free((VkPipelineDynamicStateCreateInfo*)pipeline_info->pDynamicState);
rse_free(vertex_bindings);
rse_free(shader_attributes);
rse_free(dynamic_states);
}
RSE_LINKED_LIST_POP_FRONT(pipelines);
} }
pipelines->pipeline_create_infos_count = 0; if (RSE_LINKED_LIST_IS_EMPTY(pipelines) == 0) {
LOGF(_("Failed to clean up pipelines\n"));
}
} }
rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, VkDynamicState dynamic_state) rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, VkDynamicState dynamic_state)
{ {
if (dynamic_state >= VK_DYNAMIC_STATE_MAX_ENUM) { if (dynamic_state >= VK_DYNAMIC_STATE_MAX_ENUM) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid dynamic state in pipeline"); LOGF(_("Invalid dynamic state in pipeline\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_PIPELINE_INVALID_DYNAMIC_STATE;
} }
// TODO: This was "Push Front" when this was still a list. This might be relevant RSE_LINKED_LIST_PUSH_FRONT(pipeline->dynamic_states, dynamic_state);
pipeline->dynamic_states[pipeline->dynamic_states_count++] = dynamic_state;
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t pipeline_builder_init(struct pipeline_t* pipeline) rse_err_t pipeline_builder_init(struct pipeline_t** pipeline)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
struct pipeline_t* pipeline_ptr = NULL;
if (pipeline == NULL) { if (*pipeline != NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided NULL pointer"); LOGF(
return RSE_ERROR_INTERNAL_ERROR; _("Pipeline is not NULL. This function will initialize everything "
"for you, no need for manual allocation\n"));
return VULKAN_ERROR_PIPELINE_ALREADY_INITIALIZED;
} }
rse_memset(pipeline, 0, sizeof(struct pipeline_t)); rse_malloc(pipeline_ptr, sizeof(struct pipeline_t));
rse_memset(pipeline_ptr, 0, sizeof(struct pipeline_t));
*pipeline = pipeline_ptr;
return status; return status;
} }
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, struct pipeline_t* pipeline,
struct pipeline_t* pipeline, const char* shader_path, const VkShaderStageFlagBits shader_stage)
const rse_id_t shader_id)
{ {
size_t i = 0; size_t i = 0;
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
char* buffer = NULL;
size_t buffer_size = 0U;
/* Check if we have enough space for new shader stages */ /* Check if we have enough space for new shader stages */
if (pipeline->shader_stages_count + 1 > MAX_SHADER_STAGES) { if (pipeline->shader_stages_count + 1 > MAX_SHADER_STAGES) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Too many shader stages in pipeline"); LOGF(_("Too many shader stages in pipeline\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_PIPELINE_TOO_MANY_SHADER_STAGES;
} }
i = pipeline->shader_stages_count++; i = pipeline->shader_stages_count++;
/* Check if shader module is valid */
if (shader_stage >= VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM) {
LOGF(_("Invalid shader stage in pipeline\n"));
return VULKAN_ERROR_PIPELINE_INVALID_SHADER_STAGE;
}
/* Read shader file */
read_file(shader_path, &buffer_size, NULL);
rse_malloc(buffer, buffer_size);
read_file(shader_path, &buffer_size, buffer);
STATUS_CHECK(create_shader_module(context, pipeline, buffer, buffer_size));
free(buffer);
pipeline->shader_stages[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; pipeline->shader_stages[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pipeline->shader_stages[i].pNext = NULL; pipeline->shader_stages[i].pNext = NULL;
pipeline->shader_stages[i].flags = 0U; pipeline->shader_stages[i].flags = 0U;
pipeline->shader_stages[i].stage = context->shader_modules.shaders_stages[shader_id]; pipeline->shader_stages[i].stage = shader_stage;
/* This is automatically updated during shader module creation */ pipeline->shader_stages[i].module = RSE_LINKED_LIST_FRONT(context->shader_modules); /* This is automatically updated
pipeline->shader_stages[i].module = context->shader_modules.shader_modules[shader_id]; during shader module creation */
pipeline->shader_stages[i].pName = "main"; pipeline->shader_stages[i].pName = "main";
pipeline->shader_stages[i].pSpecializationInfo = NULL; pipeline->shader_stages[i].pSpecializationInfo = NULL;
return status; return status;
} }
rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline, rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline, const uint8_t binding, const uint8_t stride,
const uint8_t binding,
const uint8_t stride,
const VkVertexInputRate input_rate) const VkVertexInputRate input_rate)
{ {
VkVertexInputBindingDescription binding_pipeline_description = {0}; VkVertexInputBindingDescription binding_pipeline_description = {0};
if (input_rate >= VK_VERTEX_INPUT_RATE_MAX_ENUM) { if (input_rate >= VK_VERTEX_INPUT_RATE_MAX_ENUM) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid vertex input rate"); LOGF(_("Invalid vertex input rate\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_PIPELINE_INVALID_VERTEX_INPUT_RATE;
} }
binding_pipeline_description.binding = binding; binding_pipeline_description.binding = binding;
binding_pipeline_description.stride = stride; binding_pipeline_description.stride = stride;
binding_pipeline_description.inputRate = input_rate; binding_pipeline_description.inputRate = input_rate;
pipeline->vertex_bindings[pipeline->vertex_bindings_count++] = binding_pipeline_description; RSE_LINKED_LIST_PUSH_FRONT(pipeline->vertex_bindings, binding_pipeline_description);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline, rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline, const uint8_t binding,
const uint8_t binding, const uint8_t location, const VkFormat format, const uint8_t offset)
const uint8_t location,
const VkFormat format,
const uint8_t offset)
{ {
VkVertexInputAttributeDescription attribute_description = {0}; VkVertexInputAttributeDescription attribute_description = {0};
if (binding >= 16U) { if (binding >= 16U) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid binding in pipeline attribute description"); LOGF(_("Invalid binding in pipeline attribute description\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_PIPELINE_INVALID_BINDING;
} }
if (format >= VK_FORMAT_MAX_ENUM) { if (format >= VK_FORMAT_MAX_ENUM) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid format in pipeline attribute description"); LOGF(_("Invalid format in pipeline attribute description\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_PIPELINE_INVALID_FORMAT;
} }
attribute_description.binding = binding; attribute_description.binding = binding;
@@ -336,87 +380,114 @@ rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline,
attribute_description.format = format; attribute_description.format = format;
attribute_description.offset = offset; attribute_description.offset = offset;
pipeline->shader_attributes[pipeline->shader_attributes_count++] = attribute_description; RSE_LINKED_LIST_PUSH_FRONT(pipeline->shader_attributes, attribute_description);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t pipeline_add_push_constants(struct pipeline_t* pipeline, rse_err_t pipelines_build(struct graphics_context_t* context, struct pipeline_infos_list_t* pipelines_infos_list)
VkShaderStageFlagBits shader_stage,
uint32_t offset,
uint32_t size)
{
pipeline->push_constant_ranges.push_constant_ranges[pipeline->push_constant_ranges.push_constant_ranges_count]
.stageFlags = shader_stage;
pipeline->push_constant_ranges.push_constant_ranges[pipeline->push_constant_ranges.push_constant_ranges_count]
.offset = offset;
pipeline->push_constant_ranges.push_constant_ranges[pipeline->push_constant_ranges.push_constant_ranges_count]
.size = size;
pipeline->push_constant_ranges.push_constant_ranges_count++;
return RSE_ERROR_NO_ERROR;
}
rse_err_t pipelines_build(struct graphics_context_t* context, struct pipeline_infos_t* pipelines_infos_list)
{ {
assert(NULL != context); assert(NULL != context);
assert(NULL != pipelines_infos_list); assert(NULL != pipelines_infos_list);
size_t i = 0U;
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
size_t pipelines_count = RSE_LINKED_LIST_SIZE(pipelines_infos_list);
VkGraphicsPipelineCreateInfo* pipeline_infos = NULL;
if (VK_SUCCESS != context->pipelines_data.pipelines_count = pipelines_count;
vkCreateGraphicsPipelines(context->vulkan_handles.device,
VK_NULL_HANDLE, STATUS_CHECK(create_pipeline_layout(context));
pipelines_infos_list->pipeline_create_infos_count,
pipelines_infos_list->create_infos, rse_malloc(pipeline_infos, sizeof(VkGraphicsPipelineCreateInfo) * pipelines_count);
NULL,
&context->pipelines_data.pipelines[context->pipelines_data.pipelines_count])) { RSE_LINKED_LIST_TO_ARRAY(pipelines_infos_list, pipeline_infos);
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create graphics pipelines");
status = RSE_ERROR_INTERNAL_ERROR; /* FIXME: layout is created to late. Maybe move it somewhere else so we
* don't need this loop? */
for (i = 0; i < pipelines_count; ++i) {
pipeline_infos[i].layout = context->pipelines_data.pipeline_layout;
} }
context->pipelines_data.pipelines_count += pipelines_infos_list->pipeline_create_infos_count; if (VK_SUCCESS != vkCreateGraphicsPipelines(context->device, VK_NULL_HANDLE, pipelines_count, pipeline_infos, NULL,
pipeline_infos_cleanup(pipelines_infos_list); context->pipelines_data.pipelines)) {
rse_memset(pipelines_infos_list, 0, sizeof(struct pipeline_infos_t)); LOGF(_("Failed to create graphics pipelines\n"));
status = VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED;
}
rse_free(pipeline_infos);
pipeline_builder_cleanup(pipelines_infos_list);
return status; return status;
} }
rse_err_t pipeline_add(const struct graphics_context_t* context, rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline_t* pipeline,
struct pipeline_t* pipeline, struct pipeline_infos_list_t** pipelines_infos)
struct pipeline_infos_t* pipelines_infos)
{ {
/* Check for send and required dynamic states */ /* Check for send and required dynamic states */
size_t iter = 0U;
uint8_t dynamic_state_found = 0;
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
VkGraphicsPipelineCreateInfo pipeline_info = {0}; VkGraphicsPipelineCreateInfo pipeline_info = {0};
size_t vertex_bindings_count = 0U;
VkVertexInputBindingDescription* vertex_bindings = NULL;
VkPipelineVertexInputStateCreateInfo* vertex_bindings_ci = NULL; VkPipelineVertexInputStateCreateInfo* vertex_bindings_ci = NULL;
size_t shader_attributes_count = 0U;
VkVertexInputAttributeDescription* shader_attributes = NULL;
size_t dynamic_states_count = 0U;
VkDynamicState* dynamic_states = NULL;
VkPipelineDynamicStateCreateInfo* dynamic_states_ci = NULL; VkPipelineDynamicStateCreateInfo* dynamic_states_ci = NULL;
while (pipeline->required_dynamic_states.dynamic_states_count > 0) { uint8_t dynamic_state_found = 0;
while (RSE_STACK_IS_EMPTY(pipeline->required_dynamic_states) == 0) {
dynamic_state_found = 0; dynamic_state_found = 0;
VkDynamicState dynamic_state = VkDynamicState dynamic_state = RSE_STACK_POP(pipeline->required_dynamic_states);
pipeline->required_dynamic_states.dynamic_states[pipeline->required_dynamic_states.dynamic_states_count--]; RSE_LINKED_LIST_FOREACH(pipeline->dynamic_states, dynamic_state_ptr)
for (iter = 0U; iter < pipeline->dynamic_states_count; ++iter) { {
if (pipeline->dynamic_states[iter] == dynamic_state) { if (dynamic_state_ptr->data == dynamic_state) {
dynamic_state_found = 1; dynamic_state_found = 1;
break; break;
} }
} }
if (dynamic_state_found == 0) { if (dynamic_state_found == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Missing dynamic state in pipeline"); LOGF(_("Missing dynamic state in pipeline\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_PIPELINE_MISSING_DYNAMIC_STATE;
} }
} }
/* Check for shader modules */
if (RSE_LINKED_LIST_IS_EMPTY(context->shader_modules) == 1) {
LOGF(_("Missing shader modules in pipeline\n"));
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_MODULES;
}
/* Check for required shader stages */ /* Check for required shader stages */
if (pipeline->shader_stages_count == 0) { if (pipeline->shader_stages_count == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Missing shader stages in pipeline. There must be at least one."); LOGF(
return RSE_ERROR_INTERNAL_ERROR; _("Missing shader stages in pipeline. There must be at least "
"one.\n"));
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_STAGES;
} }
/* Check for required vertex bindings */
if (pipeline->vertex_bindings == NULL) {
LOGF(_("Missing vertex bindings in pipeline\n"));
return VULKAN_ERROR_PIPELINE_MISSING_VERTEX_BINDINGS;
}
/* Check for required shader attributes */
if (pipeline->shader_attributes == NULL) {
LOGF(_("Missing shader attributes in pipeline\n"));
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_ATTRIBUTES;
}
/* Convert linked list to array */
vertex_bindings_count = RSE_LINKED_LIST_TO_ARRAY(pipeline->vertex_bindings, vertex_bindings);
shader_attributes_count = RSE_LINKED_LIST_TO_ARRAY(pipeline->shader_attributes, shader_attributes);
dynamic_states_count = RSE_LINKED_LIST_TO_ARRAY(pipeline->dynamic_states, dynamic_states);
/* Build constant pipeline. TODO: Some parts may be customizable */ /* Build constant pipeline. TODO: Some parts may be customizable */
pipeline_add_input_assembly(pipeline); pipeline_add_input_assembly(pipeline);
pipeline_add_viewport_state(pipeline); pipeline_add_viewport_state(pipeline);
@@ -435,10 +506,10 @@ rse_err_t pipeline_add(const struct graphics_context_t* context,
vertex_bindings_ci->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_bindings_ci->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_bindings_ci->pNext = NULL; vertex_bindings_ci->pNext = NULL;
vertex_bindings_ci->flags = 0U; vertex_bindings_ci->flags = 0U;
vertex_bindings_ci->vertexBindingDescriptionCount = pipeline->vertex_bindings_count; vertex_bindings_ci->vertexBindingDescriptionCount = vertex_bindings_count;
vertex_bindings_ci->pVertexBindingDescriptions = pipeline->vertex_bindings; vertex_bindings_ci->pVertexBindingDescriptions = vertex_bindings;
vertex_bindings_ci->vertexAttributeDescriptionCount = pipeline->shader_attributes_count; vertex_bindings_ci->vertexAttributeDescriptionCount = shader_attributes_count;
vertex_bindings_ci->pVertexAttributeDescriptions = pipeline->shader_attributes; vertex_bindings_ci->pVertexAttributeDescriptions = shader_attributes;
pipeline_info.pVertexInputState = vertex_bindings_ci; pipeline_info.pVertexInputState = vertex_bindings_ci;
pipeline_info.pInputAssemblyState = &pipeline->input_assembly; pipeline_info.pInputAssemblyState = &pipeline->input_assembly;
@@ -453,17 +524,16 @@ rse_err_t pipeline_add(const struct graphics_context_t* context,
dynamic_states_ci->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_states_ci->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_states_ci->pNext = NULL; dynamic_states_ci->pNext = NULL;
dynamic_states_ci->flags = 0U; dynamic_states_ci->flags = 0U;
dynamic_states_ci->dynamicStateCount = pipeline->dynamic_states_count; dynamic_states_ci->dynamicStateCount = dynamic_states_count;
dynamic_states_ci->pDynamicStates = pipeline->dynamic_states; dynamic_states_ci->pDynamicStates = dynamic_states;
pipeline_info.pDynamicState = dynamic_states_ci; pipeline_info.pDynamicState = dynamic_states_ci;
pipeline_info.layout = pipeline->pipeline_layout; pipeline_info.layout = context->pipelines_data.pipeline_layout;
pipeline_info.renderPass = context->vulkan_handles.render_pass; pipeline_info.renderPass = context->render_pass;
pipeline_info.subpass = 0; pipeline_info.subpass = 0;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // TODO: Add possiblity do derive from another pipeline pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // TODO: Add possiblity do derive from another pipeline
pipeline_info.basePipelineIndex = -1; pipeline_info.basePipelineIndex = -1;
RSE_LINKED_LIST_PUSH_BACK(*pipelines_infos, pipeline_info);
pipelines_infos->create_infos[pipelines_infos->pipeline_create_infos_count++] = pipeline_info;
return status; return status;
} }
@@ -471,17 +541,18 @@ rse_err_t pipeline_add(const struct graphics_context_t* context,
void destroy_pipelines(struct graphics_context_t* context) void destroy_pipelines(struct graphics_context_t* context)
{ {
size_t i = 0; size_t i = 0;
size_t shader_modules_count = RSE_LINKED_LIST_SIZE(context->shader_modules);
for (i = 0; i < context->pipelines_data.pipelines_count; i++) { for (i = 0; i < context->pipelines_data.pipelines_count; i++) {
if (context->pipelines_data.pipeline_layouts[i] != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(context->vulkan_handles.device, context->pipelines_data.pipeline_layouts[i], NULL);
}
if (context->pipelines_data.pipelines[i] != VK_NULL_HANDLE) { if (context->pipelines_data.pipelines[i] != VK_NULL_HANDLE) {
vkDestroyPipeline(context->vulkan_handles.device, context->pipelines_data.pipelines[i], NULL); vkDestroyPipeline(context->device, context->pipelines_data.pipelines[i], NULL);
} }
} }
for (i = 0; i < context->shader_modules.shader_modules_count; i++) { for (i = 0; i < shader_modules_count; i++) {
vkDestroyShaderModule(context->vulkan_handles.device, context->shader_modules.shader_modules[i], NULL); if (RSE_LINKED_LIST_FRONT(context->shader_modules) != VK_NULL_HANDLE) {
vkDestroyShaderModule(context->device, RSE_LINKED_LIST_FRONT(context->shader_modules), NULL);
}
RSE_LINKED_LIST_POP_FRONT(context->shader_modules);
} }
} }
+19 -46
View File
@@ -12,37 +12,38 @@
#ifndef PIPELINE_BUILDER_H #ifndef PIPELINE_BUILDER_H
#define PIPELINE_BUILDER_H #define PIPELINE_BUILDER_H
#include <stdint.h>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
#include "graphics_context.h" #include "graphics_context.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/linked_list.h"
#include "utilities/stack.h"
rse_err_t shader_create_module(struct graphics_context_t* context, #define MAX_SHADER_STAGES 12U
rse_id_t* shader_module_id,
const char* shader_path, struct pipeline_t;
const VkShaderStageFlagBits shader_stage); struct pipeline_infos_list_t;
/** /**
* @brief Initialize pipeline builder * @brief Initialize pipeline builder
* *
* @param[out] pipeline Pipeline to initialize. Must not be NULL * @param[out] pipeline Pipeline to initialize. Must be NULL
* @return RSE_SUCCESS on success, error code otherwise * @return RSE_SUCCESS on success, error code otherwise
*/ */
rse_err_t pipeline_builder_init(struct pipeline_t* pipeline); rse_err_t pipeline_builder_init(struct pipeline_t** pipeline);
/** /**
* @brief Add shader stages to pipeline * @brief Add shader stages to pipeline
* *
* @param[in] context Graphics context handle * @param[in] context Graphics context handle
* @param[in] pipeline Pipeline to build * @param[in] pipeline Pipeline to build
* @param[in] shader_id ID of the shader we want to add to pipeline * @param[in] shader_path Path to the shader file
* @param[in] shader_stage Shader stage to add
* @return RSE_SUCCESS on success, error code otherwise * @return RSE_SUCCESS on success, error code otherwise
*/ */
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, struct pipeline_t* pipeline,
struct pipeline_t* pipeline, const char* shader_path, const VkShaderStageFlagBits shader_stage);
const rse_id_t shader_id);
/** /**
* @brief Add vertex input binding to pipeline * @brief Add vertex input binding to pipeline
@@ -53,9 +54,7 @@ rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context,
* @param[in] input_rate Input rate for vertex data * @param[in] input_rate Input rate for vertex data
* @return RSE_SUCCESS on success, error code otherwise * @return RSE_SUCCESS on success, error code otherwise
*/ */
rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline, rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline, const uint8_t binding, const uint8_t stride,
const uint8_t binding,
const uint8_t stride,
const VkVertexInputRate input_rate); const VkVertexInputRate input_rate);
/** /**
@@ -68,11 +67,8 @@ rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline,
* @param[in] offset Offset of the attribute in bytes * @param[in] offset Offset of the attribute in bytes
* @return RSE_SUCCESS on success, error code otherwise * @return RSE_SUCCESS on success, error code otherwise
*/ */
rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline, rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline, const uint8_t binding,
const uint8_t binding, const uint8_t location, const VkFormat format, const uint8_t offset);
const uint8_t location,
const VkFormat format,
const uint8_t offset);
/** /**
* @brief Add dynamic state to pipeline * @brief Add dynamic state to pipeline
@@ -83,49 +79,26 @@ rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline,
*/ */
rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, const VkDynamicState dynamic_state); rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, const VkDynamicState dynamic_state);
/**
* @brief Create a Pipeline Layout
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED
*/
rse_err_t pipeline_add_descriptor_sets(struct graphics_context_t* context,
struct pipeline_t* pipeline,
uint32_t descriptor_set_id);
/**
* @brief Add push constants to pipeline
*
* @return RSE_ERROR_NO_ERROR on success
*
*/
rse_err_t pipeline_add_push_constants(struct pipeline_t* pipeline,
VkShaderStageFlagBits shader_stage,
uint32_t offset,
uint32_t size);
/** /**
* @brief Build pipeline from added components. This function should be called after adding all necessary components. * @brief Build pipeline from added components. This function should be called after adding all necessary components.
* After call, pipeline is ready to be used and all added components are cleared, ready for next pipeline to be built. * After call, pipeline is ready to be used and all added components are cleared, ready for next pipeline to be built.
* *
* param[in/out] context Graphics context handle * param[in/out] context Graphics context handle
* @param[in] pipeline Pipelines list to build * @param[in] pipeline Pipelines list to build
* @param[in] descriptor_set_handle Filled handle with descriptor sets
* @return RSE_SUCCESS on success, error code otherwise * @return RSE_SUCCESS on success, error code otherwise
*/ */
rse_err_t pipelines_build(struct graphics_context_t* context, struct pipeline_infos_t* pipelines); rse_err_t pipelines_build(struct graphics_context_t* context, struct pipeline_infos_list_t* pipelines);
/** /**
* @brief Add pipeline to list of pipelines * @brief Add pipeline to list of pipelines
* *
* @param[in] context Graphics context handle * @param[in] context Graphics context handle
* @param[in] pipeline Pipeline to add * @param[in] pipeline Pipeline to add
* @param[out] pipeline_infos Array of pipelines infos, where new info should be added * @param[out] pipeline_info List of pipelines infos, where new info should be added
* @return RSE_SUCCESS on success, error code otherwise * @return RSE_SUCCESS on success, error code otherwise
*/ */
rse_err_t pipeline_add(const struct graphics_context_t* context, rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline_t* pipeline,
struct pipeline_t* pipeline, struct pipeline_infos_list_t** pipelines);
struct pipeline_infos_t* pipelines_infos);
/** /**
* @brief Destroy all pipelines * @brief Destroy all pipelines
-41
View File
@@ -1,41 +0,0 @@
/**
* @file renderer.c
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief
* @version 0.1
* @date 2025-10-15
*
* @copyright Copyright (c) 2025
*
*/
#include "renderer.h"
#include <SDL3/SDL_log.h>
#include <stdint.h>
#include "src/graphics_context.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
rse_err_t renderer_get_new_id(struct graphics_context_t* context, uint32_t* id)
{
if (context->renderer_data.renderers_count >= RENDERER_MAX_COUNT) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Reached maximum number of renderers");
return RSE_ERROR_INTERNAL_ERROR;
}
*id = context->renderer_data.renderers_count++;
return RSE_ERROR_NO_ERROR;
}
rse_err_t renderer_set_render_function(struct graphics_context_t* context,
uint32_t renderer_id,
renderer_function_t renderer_function)
{
context->renderer_data.renderers[renderer_id].render_function = renderer_function;
return RSE_ERROR_NO_ERROR;
}
-24
View File
@@ -1,24 +0,0 @@
/**
* @file renderer.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief
* @version 0.1
* @date 2025-10-15
*
* @copyright Copyright (c) 2025
*
*/
#ifndef RENDERER_H
#define RENDERER_H
#include "src/graphics_context.h"
#include "utilities/commons.h"
rse_err_t renderer_get_new_id(struct graphics_context_t* context, uint32_t* id);
rse_err_t renderer_set_render_function(struct graphics_context_t* context,
uint32_t renderer_id,
renderer_function_t renderer_function);
#endif /* !RENDERER_H */
+88 -245
View File
@@ -1,35 +1,16 @@
#include "rse_graphics.h" #include "rse_graphics.h"
#ifdef __unix__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wsign-compare"
#endif
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_render.h>
#ifdef __unix__
#pragma GCC diagnostic pop
#endif
#include <cglm/util.h>
#include <stdint.h>
#include <stdlib.h>
#include <vulkan/vulkan_core.h>
#include "descriptor_builder.h" #include "descriptor_builder.h"
#include "font_manager.h"
#include "graphics_context.h" #include "graphics_context.h"
#include "math.h"
#include "mesh_controller.h" #include "mesh_controller.h"
#include "pipeline_builder.h" #include "pipeline_builder.h"
#include "src/renderer.h"
#include "src/vulkan_buffers.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/time_utils.h" #include "utilities/localization.h"
#include "utilities/logger.h"
#include "vulkan_base.h" #include "vulkan_base.h"
#include "vulkan_commons.h" #include "vulkan_commons.h"
#include "vulkan_image.h"
#include "window.h" #include "window.h"
struct rse_graphics_context_t struct rse_graphics_context_t
@@ -37,268 +18,130 @@ struct rse_graphics_context_t
struct graphics_context_t* context; struct graphics_context_t* context;
}; };
static void model_view_projection_update(struct graphics_context_t* context) rse_err_t rse_graphics_init(struct rse_graphics_context_t** context)
{
vec3 eye = {0};
vec3 center = {0};
vec3 up = {0};
vec3 rotation_vec = {0.0f, 1.0f, 0.0f};
float fovy;
float z_near;
float z_far;
static float rotation = 0.0;
eye[2] = -4.0f;
up[1] = 1.0f;
fovy = glm_rad(45.0f);
z_near = 0.1f;
z_far = 20.0f;
glm_mat4_identity(context->mvp_data.model);
glm_rotate(context->mvp_data.model, rotation, rotation_vec);
glm_lookat(eye, center, up, context->mvp_data.view);
glm_perspective(
fovy,
context->swapchain_data.swapchain_extent.width / (float)context->swapchain_data.swapchain_extent.height,
z_near,
z_far,
context->mvp_data.proj);
rotation += time_get_dt() * glm_rad(120.0);
}
static rse_err_t render_static_mesh(struct graphics_context_t* context, uint32_t renderer_id)
{
VkCommandBuffer command_buffer = context->vulkan_handles.command_buffers[context->current_frame];
VkDeviceSize offsets[] = {0};
uint32_t mvp_offset = 0;
uint32_t mvp_size = sizeof(struct mvp_data_t);
(void)renderer_id;
vkCmdPushConstants(command_buffer,
context->pipelines_data.pipeline_layouts[0],
VK_SHADER_STAGE_VERTEX_BIT,
mvp_offset,
mvp_size,
&context->mvp_data);
model_view_projection_update(context);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, context->pipelines_data.pipelines[0]);
vkCmdBindIndexBuffer(command_buffer,
context->render_targets[RENDER_TARGET_3D].index_buffer.buffer,
0,
VK_INDEX_TYPE_UINT16);
vkCmdBindVertexBuffers(command_buffer,
0,
1,
&context->render_targets[RENDER_TARGET_3D].vertex_buffer.buffer,
offsets);
/* FIXME: Currently we can only bind one descriptor set */
vkCmdBindDescriptorSets(command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
context->pipelines_data.pipeline_layouts[0],
0,
1,
&context->descriptor_data.descriptor_sets[0],
0,
NULL);
vkCmdDrawIndexedIndirect(command_buffer,
context->render_targets[RENDER_TARGET_3D].draw_indirect_command_buffer.buffer,
0,
context->mesh_data.mesh_count,
sizeof(VkDrawIndexedIndirectCommand));
return RSE_ERROR_NO_ERROR;
}
rse_err_t rse_graphics_init(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
struct rse_graphics_context_t* ctx_ptr = NULL;
SDL_Init(SDL_INIT_VIDEO); if (*context != NULL) {
LOGF(_("Graphics already initialized!\n"));
return RSE_ERROR_ALREADY_INITIALIZED;
}
STATUS_CHECK(window_init(&context->window_handle, &context->is_framebuffer_resized)); rse_malloc(*context, sizeof(struct rse_graphics_context_t));
STATUS_CHECK(vulkan_init(context)); ctx_ptr = *context;
rse_malloc(ctx_ptr->context, sizeof(struct graphics_context_t));
rse_memset(ctx_ptr->context, 0, sizeof(struct graphics_context_t));
STATUS_CHECK(window_init(&ctx_ptr->context->window_handle, &ctx_ptr->context->is_framebuffer_resized));
STATUS_CHECK(init_vulkan(ctx_ptr->context));
return status; return status;
} }
#define TEXTURES_COUNT (3U) void rse_graphics_test_function(struct rse_graphics_context_t* rse_context)
rse_err_t rse_graphics_test_function(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; struct graphics_context_t* context = rse_context->context;
rse_id_t textures[TEXTURES_COUNT] = {0}; // rse_err_t status = RSE_ERROR_NO_ERROR;
struct pipeline_t pipeline; uint16_t tex_id_2 = 0;
rse_id_t descriptor_set_handle = 0; size_t ubo_index = 0;
struct descriptor_set_layout_bindings_t layout_bindings = {0}; size_t sampler_index = 0;
struct pipeline_infos_t pipeline_infos = {0}; struct pipeline_t* pipeline = NULL;
rse_id_t mesh_id_1 = 0; struct descriptor_set_list_t* descriptor_set_list;
rse_id_t mesh_id_2 = 0; struct descriptor_set_handle_t* descriptor_set_handle;
rse_id_t avocado_id = 0; struct pipeline_infos_list_t* pipeline_infos = NULL;
// rse_id_t font_id = 0;
rse_id_t static_mesh_renderer = 0U;
rse_id_t vertex_shader_id, fragment_shader_id;
STATUS_CHECK(texture_load_from_file(context, "../../test_image.jpg", &textures[0])); // status = load_texture_from_file("../../test_image.jpg", &tex_id_2);
STATUS_CHECK(texture_load_from_file(context, "../../test_image_2.jpg", &textures[1])); // if (status != RSE_ERROR_NO_ERROR) {
// LOGE("Failed to do something. Check error messages. Error: %d", status);
// STATUS_CHECK(fonts_init()); // return;
// STATUS_CHECK(fonts_load_from_file(context, "../../NotoSansMono-Regular.ttf", &font_id)); // }
// STATUS_CHECK(fonts_set_font_size(context, font_id, 16)); // fonts_initialize();
// FIXME: Temporary array of vertices, for testing purposes // FIXME: Temporary array of vertices, for testing purposes
struct vertex_t vertices[] = { struct vertex_t vertices[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
}; {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}};
struct vertex_t vertices_2[] = { struct vertex_t vertices2[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{-0.7f, -0.7f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, {{0.5f, -0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{-0.7f, 0.7f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, {{0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{0.1f, 0.7f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}};
{{0.1f, -0.7f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
};
// FIXME: Temporary array of vertices, for testing purposes // FIXME: Temporary array of vertices, for testing purposes
uint16_t indices[] = {0, 1, 2, 2, 3, 0, 0, 3, 2, 2, 1, 0}; uint16_t indices[] = {0, 1, 2, 2, 3, 0};
STATUS_CHECK(mesh_create(context, vertices, indices, 4, 12, &mesh_id_1)); uint16_t mesh_id = create_mesh(context, vertices, indices, 6, 6);
if (RSE_ERROR_NO_ERROR != create_mesh_instance(
mesh_create_instance(context, context, mesh_id,
mesh_id_1, (struct instance_data_t){{0.0f, 0.0f, 2.4f}, {0.0f, 0.0f, math_deg_to_radians(45.0f)}, 1.0f, tex_id_2});
(struct instance_data_t){{0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0}, 0.5f, textures[0]})) {
exit(1);
}
if (RSE_ERROR_NO_ERROR != create_mesh_instance(
mesh_create_instance( context, mesh_id,
context, (struct instance_data_t){{-0.5f, 0.0f, -1.0f}, {0.0f, 0.0f, math_deg_to_radians(10.0f)}, 1.0f, tex_id_2});
mesh_id_1,
(struct instance_data_t){{-0.5f, 0.0f, -1.0f}, {0.0f, 0.0f, glm_rad(10.0f)}, 0.5f, textures[1]})) {
exit(1);
}
STATUS_CHECK(mesh_create(context, vertices_2, indices, 4, 6, &mesh_id_2)); mesh_id = create_mesh(context, vertices2, indices, 6, 6);
if (RSE_ERROR_NO_ERROR != create_mesh_instance(
mesh_create_instance( context, mesh_id,
context, (struct instance_data_t){{2.0f, 1.0f, 0.0f}, {0.0f, 0.0f, math_deg_to_radians(0.0f)}, 1.0f, tex_id_2});
mesh_id_2,
(struct instance_data_t){{0.5f, 0.0f, 1.0f}, {0.0f, 0.0f, glm_rad(0.0f)}, 0.5f, textures[1]})) {
exit(1);
}
mesh_create_from_file(context, "../../Avocado.glb", &avocado_id, &textures[2]); // fonts_load_from_file("/usr/share/fonts/JetBrainsMonoNerdFont/JetBrainsMonoNerdFont-Medium.ttf");
if (RSE_ERROR_NO_ERROR != // print_text("Hello World!", (struct vec3_t){-1.0f, 0.0f, 0.9f}, 0.3f);
mesh_create_instance(
context,
avocado_id,
(struct instance_data_t){{0.0f, 0.2f, 0.0f}, {0.0f, 0.0f, glm_rad(180.0f)}, 10.0f, textures[2]})) {
exit(1);
}
STATUS_CHECK(buffers_update(context));
STATUS_CHECK(sampler_create(context, &context->vulkan_handles.sampler));
STATUS_CHECK(descriptor_pool_add_type(context, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1));
STATUS_CHECK(descriptor_pool_add_type(context, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, TEXTURES_COUNT));
STATUS_CHECK(descriptor_pool_initialize(context));
/* Normal rendering */
STATUS_CHECK(descriptor_create_new_set(context, &descriptor_set_handle));
STATUS_CHECK(
descriptor_add_layout(&layout_bindings, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT));
STATUS_CHECK(descriptor_add_layout(&layout_bindings,
1,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
TEXTURES_COUNT,
VK_SHADER_STAGE_FRAGMENT_BIT));
STATUS_CHECK(descriptor_set_finish(context, &layout_bindings, descriptor_set_handle));
STATUS_CHECK(descriptor_build_sets(context));
STATUS_CHECK(descriptor_attach_buffer(context,
descriptor_set_handle,
&context->render_targets[RENDER_TARGET_3D].instance_buffer,
0,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
STATUS_CHECK(descriptor_attach_images(context,
descriptor_set_handle,
textures,
TEXTURES_COUNT,
context->vulkan_handles.sampler,
1));
/* Initialize pipeline */ /* Initialize pipeline */
pipeline_builder_init(&pipeline); pipeline_builder_init(&pipeline);
shader_create_module(context, &vertex_shader_id, "shader.vert.num", VK_SHADER_STAGE_VERTEX_BIT); pipeline_add_shader_stage(context, pipeline, "../graphics/shaders/shader.vert.num", VK_SHADER_STAGE_VERTEX_BIT);
pipeline_add_shader_stage(context, &pipeline, vertex_shader_id); pipeline_add_shader_stage(context, pipeline, "../graphics/shaders/solid_objects.frag.num",
shader_create_module(context, &fragment_shader_id, "solid_objects.frag.num", VK_SHADER_STAGE_FRAGMENT_BIT); VK_SHADER_STAGE_FRAGMENT_BIT);
pipeline_add_shader_stage(context, &pipeline, fragment_shader_id);
pipeline_add_vertex_input_binding(&pipeline, 0, sizeof(struct vertex_t), VK_VERTEX_INPUT_RATE_VERTEX); pipeline_add_vertex_input_binding(pipeline, 0, sizeof(struct vertex_t), VK_VERTEX_INPUT_RATE_VERTEX);
pipeline_add_vertex_input_binding(pipeline, 1, sizeof(struct vertex_t), VK_VERTEX_INPUT_RATE_INSTANCE);
pipeline_add_vertex_input_attribute(&pipeline, 0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(struct vertex_t, pos)); pipeline_add_vertex_input_attribute(pipeline, 0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(struct vertex_t, pos));
pipeline_add_vertex_input_attribute(&pipeline, 0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(struct vertex_t, color)); pipeline_add_vertex_input_attribute(pipeline, 0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(struct vertex_t, color));
pipeline_add_vertex_input_attribute(&pipeline, pipeline_add_vertex_input_attribute(pipeline, 0, 2, VK_FORMAT_R32G32_SFLOAT, offsetof(struct vertex_t, tex_coords));
0, pipeline_add_vertex_input_attribute(pipeline, 1, 3, VK_FORMAT_R32G32B32_SFLOAT,
2, offsetof(struct instance_data_t, pos));
VK_FORMAT_R32G32_SFLOAT, pipeline_add_vertex_input_attribute(pipeline, 1, 4, VK_FORMAT_R32G32B32_SFLOAT,
offsetof(struct vertex_t, tex_coords)); offsetof(struct instance_data_t, rot));
pipeline_add_vertex_input_attribute(pipeline, 1, 5, VK_FORMAT_R32_SFLOAT, offsetof(struct instance_data_t, scale));
pipeline_add_vertex_input_attribute(pipeline, 1, 6, VK_FORMAT_R32_UINT,
offsetof(struct instance_data_t, texture_id));
pipeline_add_dynamic_state(&pipeline, VK_DYNAMIC_STATE_VIEWPORT); pipeline_add_dynamic_state(pipeline, VK_DYNAMIC_STATE_VIEWPORT);
pipeline_add_dynamic_state(&pipeline, VK_DYNAMIC_STATE_SCISSOR); pipeline_add_dynamic_state(pipeline, VK_DYNAMIC_STATE_SCISSOR);
pipeline_add_push_constants(&pipeline, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(struct mvp_data_t)); VkSampler sampler = VK_NULL_HANDLE;
pipeline_add_descriptor_sets(context, &pipeline, descriptor_set_handle);
pipeline_add(context, &pipeline, &pipeline_infos); create_sampler(context, sampler);
pipelines_build(context, &pipeline_infos); descriptor_add_set(descriptor_set_list, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, NULL);
descriptor_add_set(descriptor_set_list, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1,
VK_SHADER_STAGE_FRAGMENT_BIT, sampler);
renderer_get_new_id(context, &static_mesh_renderer); descriptors_build(context, descriptor_set_list, descriptor_set_handle);
renderer_set_render_function(context, static_mesh_renderer, render_static_mesh); add_pipeline(context, pipeline, &pipeline_infos);
return RSE_ERROR_NO_ERROR; build_pipelines(context, pipeline_infos);
// TODO: Add a shader for text rendering
// pipeline_add_shader_stage("../graphics/shaders/on_screen_text.frag.num", VK_SHADER_STAGE_FRAGMENT_BIT);
} }
rse_err_t rse_graphics_run(struct graphics_context_t* context) rse_err_t rse_graphics_run(struct rse_graphics_context_t* rse_context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
struct graphics_context_t* context = rse_context->context;
STATUS_CHECK(vulkan_run(context)); STATUS_CHECK(run_vulkan(context));
STATUS_CHECK(window_loop(context));
return status; deinit_vulkan(context);
}
rse_err_t rse_graphics_main_loop(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(vulkan_draw_frame(context));
return status;
}
// rse_err_t rse_graphics_load_model(struct graphics_context_t* context, const char* model_path, rse_id_t* model_id)
// {
// rse_err_t status = RSE_ERROR_NO_ERROR;
//
// return status;
// }
void rse_graphics_deinit(struct graphics_context_t* context)
{
vulkan_deinit(context);
window_terminate(context->window_handle); window_terminate(context->window_handle);
rse_free(context->debug_overlay.pixels);
return status;
} }
+154
View File
@@ -0,0 +1,154 @@
#include "text_renderer.h"
#include <stdint.h>
#include <strings.h>
#include "freetype/fttypes.h"
#include "font_errors.h"
#include "src/mesh_controller.h"
#include "src/math.h"
#include "src/vulkan_commons.h"
#include "utilities/logger.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/localization.h"
#include "vulkan_image.h"
#include "vulkan/vulkan_core.h"
#include FT_FREETYPE_H
/* Load only ASCII characters that are needed. TODO: Add unicode characters */
#define FONT_CHARACTERS_COUNT 94
#define FONT_START_CHARACTER 32U
#define FONT_SIZE 200
/* Use this to scale font to 0.0 <-> 1.0 range */
#define FONT_SCALING 1.0f/FONT_SIZE
struct font_data_t {
uint16_t mesh_id;
uint16_t texture_id;
float width;
float height;
float bearing_x;
float bearing_y;
float advance;
};
struct font_data_t g_fonts_data[FONT_CHARACTERS_COUNT];
FT_Library g_library;
rse_err_t fonts_initialize()
{
FT_Error error;
memset(g_fonts_data, 0, sizeof(g_fonts_data));
error = FT_Init_FreeType(&g_library);
if (error) {
LOGF(_("Font initialization failed\n"));
return FONT_ERROR_INIT_FAILED;
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
FT_Error error;
FT_Face face;
size_t iter = 0U;
struct vertex_t font_vertices[4];
uint16_t font_indices[] = {0, 1, 2, 2, 3, 0};
error = FT_New_Face(g_library, file_path, 0, &face);
if (error) {
LOGF(_("Failed to load new font\n"));
return FONT_ERROR_LOAD_FAILED;
}
/* By default we set font size to FONT_SIZE.*/
error = FT_Set_Pixel_Sizes(face, 0, FONT_SIZE);
if (error) {
LOGF(_("Failed to change font size\n"));
return FONT_ERROR_CHANGE_SIZE_FAILED;
}
/* Convert font face to textured image */
/* Loading only needed ascii characters, from 40 (space) to 126 (~) */
for (iter = 0U; iter < face->num_glyphs && iter < FONT_CHARACTERS_COUNT; ++iter) {
error = FT_Load_Char(face, iter + FONT_START_CHARACTER, FT_LOAD_RENDER);
if (error) {
LOGF(_("Failed to load glyph into memeory\n"));
return FONT_ERROR_LOAD_GLYPH_FAILED;
}
g_fonts_data[iter].width = face->glyph->bitmap.width;
g_fonts_data[iter].height = face->glyph->bitmap.rows;
g_fonts_data[iter].bearing_x = face->glyph->bitmap_left;
g_fonts_data[iter].bearing_y = face->glyph->bitmap_top;
g_fonts_data[iter].advance = (face->glyph->advance.x >> 6);
/* Skip generating mesh if character is space */
if ((char)(iter + FONT_START_CHARACTER) == ' ') {
continue;
}
font_vertices[0] = (struct vertex_t) {
{g_fonts_data[iter].bearing_x, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}
};
font_vertices[1] = (struct vertex_t) {
{g_fonts_data[iter].bearing_x + g_fonts_data[iter].width, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}
};
font_vertices[2] = (struct vertex_t) {
{g_fonts_data[iter].bearing_x + g_fonts_data[iter].width, g_fonts_data[iter].height, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}
};
font_vertices[3] = (struct vertex_t) {
{g_fonts_data[iter].bearing_x, g_fonts_data[iter].height, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}
};
g_fonts_data[iter].mesh_id = create_mesh(context, font_vertices, font_indices, 8, 6);
STATUS_CHECK(load_texture_from_bitmat(context, face->glyph->bitmap.buffer,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
&g_fonts_data[iter].texture_id, VK_FORMAT_R8_SRGB));
}
return status;
}
rse_err_t print_text(struct graphics_context_t* context, const char* text, struct vec3_t position, float font_size)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
size_t iter = 0U;
size_t charactet_code = 0U;
float advance = 0.0f;
float x_pos = 0.0f;
float y_pos = 0.0f;
float scale = FONT_SCALING * font_size;
for (iter = 0U; iter < strlen(text); ++iter) {
if (text[iter] == ' ') {
advance += g_fonts_data[charactet_code].advance * scale;
continue;
}
charactet_code = (size_t)text[iter] - FONT_START_CHARACTER;
x_pos = position.x + advance - (g_fonts_data[charactet_code].bearing_x * scale);
y_pos = position.y - (g_fonts_data[charactet_code].bearing_y * scale);
create_mesh_instance(context, g_fonts_data[charactet_code].mesh_id, (struct instance_data_t){
{x_pos, y_pos, 0.0f},
{0.0f, 0.0f, math_deg_to_radians(0.0f)},
FONT_SCALING * font_size,
g_fonts_data[charactet_code].texture_id
});
advance += g_fonts_data[charactet_code].advance * scale;
}
return status;
}
+25
View File
@@ -0,0 +1,25 @@
/**
* @file text_renderer.h
* @author Piotr Krygier (piotr.krygier@meshsystems.com)
* @brief Load and render text on screen or in space
* @version 0.1
* @date 2024-02-02
*
* @copyright Copyright (c) 2023
*
*/
#ifndef TEXT_RENDERER_H
#define TEXT_RENDERER_H
#include "utilities/commons.h"
#include "graphics_context.h"
#include "math.h"
rse_err_t fonts_initialize(void);
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path);
rse_err_t print_text(struct graphics_context_t* context, const char* text, struct vec3_t position, float font_size);
#endif // !TEXT_RENDERER_H_
+2 -2
View File
@@ -14,7 +14,6 @@
#ifdef __unix__ #ifdef __unix__
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wpedantic" #pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wignored-qualifiers" #pragma GCC diagnostic ignored "-Wignored-qualifiers"
@@ -23,7 +22,8 @@
#pragma GCC diagnostic ignored "-Wswitch" #pragma GCC diagnostic ignored "-Wswitch"
#pragma GCC diagnostic ignored "-Wparentheses" #pragma GCC diagnostic ignored "-Wparentheses"
#endif #endif
#include "vk_mem_alloc.h" #include <cstdio>
#include "vma/vk_mem_alloc.h"
#ifdef __unix__ #ifdef __unix__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
+163 -247
View File
@@ -11,32 +11,32 @@
#include "vulkan_base.h" #include "vulkan_base.h"
#include <SDL3/SDL_error.h> #include <GLFW/glfw3.h>
#include <SDL3/SDL_log.h> #include "utilities/localization.h"
#include <SDL3/SDL_main.h> #include <vma/vk_mem_alloc.h>
#include <SDL3/SDL_vulkan.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <vk_mem_alloc.h>
#include "pipeline_builder.h"
#include "src/descriptor_builder.h"
#include "src/graphics_context.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/logger.h"
#include "vulkan/vulkan_core.h" #include "vulkan/vulkan_core.h"
#include "vulkan_buffers.h" #include "vulkan_errors.h"
#include "vulkan_commands.h"
#include "vulkan_commons.h" #include "vulkan_commons.h"
#include "vulkan_commands.h"
#include "vulkan_buffers.h"
#include "vulkan_image.h" #include "vulkan_image.h"
#include "vulkan_render_pass.h" #include "vulkan_render_pass.h"
#include "vulkan_swapchain.h" #include "vulkan_swapchain.h"
#include "pipeline_builder.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define APPLICATION_NAME "RedScarfEngine PoC" #define APPLICATION_NAME "RedScarfEngine PoC"
#define ENGINE_NAME "RedScarf Engine" #define ENGINE_NAME "RedScarf Engine"
#ifndef NDEBUG
/** /**
* @brief Calback for debug messenger * @brief Calback for debug messenger
* *
@@ -48,9 +48,9 @@
*/ */
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT message_type, VkDebugUtilsMessageTypeFlagsEXT message_type,
const VkDebugUtilsMessengerCallbackDataEXT* callback_data, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data)
void* user_data)
{ {
enum log_level_t log_level = LOGLEVEL_TRACE;
(void)message_severity; (void)message_severity;
(void)message_type; (void)message_type;
(void)user_data; (void)user_data;
@@ -59,15 +59,17 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverity
* here. We have to check flags and set severity to most important one */ * here. We have to check flags and set severity to most important one */
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage); log_level = LOGLEVEL_ERROR;
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { } else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage); log_level = LOGLEVEL_WARNING;
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) { } else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage); log_level = LOGLEVEL_INFO;
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) { } else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
SDL_LogTrace(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage); log_level = LOGLEVEL_TRACE;
} }
LOGGER(log_level, callback_data->pMessage);
return VK_FALSE; return VK_FALSE;
} }
@@ -80,8 +82,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverity
* @param debug_messenger Debug messanger handle * @param debug_messenger Debug messanger handle
* @return VkResult VK_SUCCESS on success * @return VkResult VK_SUCCESS on success
*/ */
static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info,
const VkDebugUtilsMessengerCreateInfoEXT* create_info,
const VkAllocationCallbacks* allocator, const VkAllocationCallbacks* allocator,
VkDebugUtilsMessengerEXT* debug_messenger) VkDebugUtilsMessengerEXT* debug_messenger)
{ {
@@ -109,9 +110,9 @@ static rse_err_t setup_debug_messenger(VkInstance instance, VkDebugUtilsMessenge
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
create_info.pNext = NULL; create_info.pNext = NULL;
create_info.messageSeverity = create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
@@ -120,8 +121,8 @@ static rse_err_t setup_debug_messenger(VkInstance instance, VkDebugUtilsMessenge
create_info.flags = 0U; create_info.flags = 0U;
if (CreateDebugUtilsMessengerEXT(instance, &create_info, NULL, messenger) != VK_SUCCESS) { if (CreateDebugUtilsMessengerEXT(instance, &create_info, NULL, messenger) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to setup debug messenger"); LOGF(_("Failed to setup debug messenger\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_DEBUG_MESSENGER_CREATION_FAILED;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
@@ -134,8 +135,7 @@ static rse_err_t setup_debug_messenger(VkInstance instance, VkDebugUtilsMessenge
* @param debug_messenger Debug messanger handle * @param debug_messenger Debug messanger handle
* @param allocator Memory allocator * @param allocator Memory allocator
*/ */
static void DestroyDebugUtilsMessengerEXT(VkInstance instance, static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debug_messenger,
VkDebugUtilsMessengerEXT debug_messenger,
const VkAllocationCallbacks* allocator) const VkAllocationCallbacks* allocator)
{ {
PFN_vkDestroyDebugUtilsMessengerEXT func = PFN_vkDestroyDebugUtilsMessengerEXT func =
@@ -145,6 +145,8 @@ static void DestroyDebugUtilsMessengerEXT(VkInstance instance,
} }
} }
#endif /* DEBUG */
/** /**
* @brief Create the Vulkan Instance object * @brief Create the Vulkan Instance object
* *
@@ -171,13 +173,16 @@ static rse_err_t create_instance(VkInstance* instance)
#ifdef WAYLAND #ifdef WAYLAND
"VK_KHR_wayland_surface", "VK_KHR_wayland_surface",
#else #else
"VK_KHR_xlib_surface", "VK_KHR_xcb_surface",
#endif /* WAYLAND */ #endif /* WAYLAND */
#elif defined(_WIN32) || defined(WIN32) #elif defined(_WIN32) || defined(WIN32)
"VK_KHR_win32_surface", "VK_KHR_win32_surface",
#endif /* _WIN32 || WIN32 */ #endif /* _WIN32 || WIN32 */
#ifndef NDEBUG
"VK_EXT_debug_utils", "VK_EXT_debug_utils",
#endif /* NDEBUG */
}; };
#ifndef NDEBUG #ifndef NDEBUG
uint32_t enabled_instance_extensions_count = 4; uint32_t enabled_instance_extensions_count = 4;
uint32_t enabled_instance_layers_count = 1; uint32_t enabled_instance_layers_count = 1;
@@ -199,47 +204,43 @@ static rse_err_t create_instance(VkInstance* instance)
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions_properties); vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions_properties);
#ifndef NDEBUG #ifndef NDEBUG
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "Supported Layers: "); LOGD("Supported Layers: \n");
for (i = 0; i < layersCount; i++) { for (i = 0; i < layersCount; i++) {
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "\t-%s", layer_properties[i].layerName); LOGD("\t-%s\n", layer_properties[i].layerName);
} }
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "Supported Extensions: "); LOGD("Supported Extensions: \n");
for (size_t i = 0; i < extensionCount; i++) { for (size_t i = 0; i < extensionCount; i++) {
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "\t-%s", extensions_properties[i].extensionName); LOGD("\t-%s\n", extensions_properties[i].extensionName);
} }
#endif #endif
/* Check if selected layers are supported*/ /* Check if selected layers are supported*/
for (i = 0; i < enabled_instance_layers_count; ++i) { for (i = 0; i < enabled_instance_layers_count; ++i) {
for (j = 0; i < layersCount; ++j) { for(j = 0; i < layersCount; ++j) {
if (strcmp(layer_properties[j].layerName, enabled_instance_layers_names[i]) == 0) { if(strcmp(layer_properties[j].layerName, enabled_instance_layers_names[i]) == 0) {
supported = 1; supported = 1;
break; break;
} }
} }
if (supported == 0) { if(supported == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Selected layer is not supported: %s", layer_properties[j].layerName); LOGF(_("Selected layer is not supported: %s\n"), layer_properties[j].layerName);
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_LAYER_NOT_SUPPORTED;
} }
supported = 0; supported = 0;
} }
/* Check if selected extensions are supported*/ /* Check if selected extensions are supported*/
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "Enabled extensions names:");
for (i = 0; i < enabled_instance_extensions_count; ++i) { for (i = 0; i < enabled_instance_extensions_count; ++i) {
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "\t-%s", enabled_instance_extensions_names[i]); for(j = 0; j < extensionCount; ++j) {
for (j = 0; j < extensionCount; ++j) { if(strcmp(extensions_properties[j].extensionName, enabled_instance_extensions_names[i]) == 0) {
if (strcmp(extensions_properties[j].extensionName, enabled_instance_extensions_names[i]) == 0) {
supported = 1; supported = 1;
break; break;
} }
} }
if (supported == 0) { if(supported == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, LOGF(_("Selected extension is not supported: %s\n"), extensions_properties[j].extensionName);
"Selected extension is not supported: %s", return VULKAN_ERROR_EXTENSION_NOT_SUPPORTED;
extensions_properties[j].extensionName);
return RSE_ERROR_INTERNAL_ERROR;
} }
supported = 0; supported = 0;
} }
@@ -253,21 +254,25 @@ static rse_err_t create_instance(VkInstance* instance)
application_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); application_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
application_info.pEngineName = ENGINE_NAME; application_info.pEngineName = ENGINE_NAME;
application_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); application_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
application_info.apiVersion = VK_API_VERSION_1_4; application_info.apiVersion = VK_API_VERSION_1_3;
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pNext = NULL; create_info.pNext = NULL;
create_info.flags = 0U; create_info.flags = 0U;
create_info.pApplicationInfo = &application_info; create_info.pApplicationInfo = &application_info;
create_info.enabledLayerCount = enabled_instance_layers_count; create_info.enabledLayerCount = enabled_instance_layers_count;
#ifndef NDEBUG
create_info.ppEnabledLayerNames = enabled_instance_layers_names; create_info.ppEnabledLayerNames = enabled_instance_layers_names;
#else
create_info.ppEnabledLayerNames = NULL;
#endif
create_info.enabledExtensionCount = enabled_instance_extensions_count; create_info.enabledExtensionCount = enabled_instance_extensions_count;
create_info.ppEnabledExtensionNames = enabled_instance_extensions_names; create_info.ppEnabledExtensionNames = enabled_instance_extensions_names;
result = vkCreateInstance(&create_info, NULL, instance); result = vkCreateInstance(&create_info, NULL, instance);
if (VK_SUCCESS != result) { if (VK_SUCCESS != result) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Vulkan instance initialization failed"); LOGF(_("Vulkan instance initialization failed\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_INSTANCE_INIT_FAILED;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
@@ -278,11 +283,11 @@ static rse_err_t create_instance(VkInstance* instance)
* *
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors: * @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
*/ */
static rse_err_t create_surface(VkInstance instance, SDL_Window* window_handle, VkSurfaceKHR* surface) static rse_err_t create_surface(VkInstance instance, GLFWwindow* window_handle, VkSurfaceKHR* surface)
{ {
if (true != SDL_Vulkan_CreateSurface(window_handle, instance, NULL, surface)) { if (VK_SUCCESS != glfwCreateWindowSurface(instance, window_handle, NULL, surface)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create window surface. Error: %s", SDL_GetError()); LOGF(_("Failed to create window surface\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_SURFACE_CREATION_FAILED;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
@@ -298,16 +303,15 @@ static rse_err_t pick_physical_device(VkInstance instance, VkPhysicalDevice* phy
{ {
uint32_t physical_device_count = 0U; uint32_t physical_device_count = 0U;
VkPhysicalDevice* physical_devices = NULL; VkPhysicalDevice* physical_devices = NULL;
VkPhysicalDeviceProperties device_properties = {0}; VkPhysicalDeviceProperties device_properties = {0};;
;
VkPhysicalDeviceFeatures device_features = {0}; VkPhysicalDeviceFeatures device_features = {0};
/* Get number of existing physical devices */ /* Get number of existing physical devices */
vkEnumeratePhysicalDevices(instance, &physical_device_count, NULL); vkEnumeratePhysicalDevices(instance, &physical_device_count, NULL);
if (0 == physical_device_count) { if (0 == physical_device_count) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "No physical graphical physical devices found in system"); LOGF(_("No physical graphical physical devices found in system\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND;
} }
rse_malloc(physical_devices, sizeof(VkPhysicalDevice) * physical_device_count); rse_malloc(physical_devices, sizeof(VkPhysicalDevice) * physical_device_count);
@@ -331,12 +335,10 @@ static rse_err_t pick_physical_device(VkInstance instance, VkPhysicalDevice* phy
if (VK_NULL_HANDLE == physical_device) { if (VK_NULL_HANDLE == physical_device) {
/* No suitable device found */ /* No suitable device found */
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "No suitable physical device found"); LOGF(_("No suitable physical device found\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND;
} }
rse_free(physical_devices);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
@@ -354,64 +356,39 @@ static rse_err_t create_device(struct graphics_context_t* context)
float* graphics_familiy_queue_priorities = NULL; float* graphics_familiy_queue_priorities = NULL;
float* presentation_familiy_queue_priorities = NULL; float* presentation_familiy_queue_priorities = NULL;
uint32_t device_queues_count = 0; uint32_t device_queues_count = 0;
int64_t graphics_family_idx = 0; uint32_t graphics_family_idx = 0;
uint32_t presentation_familiy_idx = 0; uint32_t presentation_familiy_idx = 0;
uint32_t queue_families_property_count = 0U; uint32_t queue_families_property_count = 0U;
uint32_t enabled_device_extensions_count = 2; uint32_t enabled_device_extensions_count = 2;
VkBool32 device_feature_supported = VK_FALSE; VkBool32 bindless_supported = VK_FALSE;
VkDeviceCreateInfo device_create_info = {0}; VkDeviceCreateInfo device_create_info = {0};
VkQueueFamilyProperties* queue_family_properties = NULL; VkQueueFamilyProperties* queue_family_properties = NULL;
VkDeviceQueueCreateInfo device_queue_createinfos[2] = {0}; VkDeviceQueueCreateInfo device_queue_createinfos[2] = {0};
VkPhysicalDeviceFeatures2 physical_features2 = {0}; VkPhysicalDeviceFeatures2 physical_features2 = {0};
VkPhysicalDeviceSynchronization2Features synchronization2_features = {0};
VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore_features = {0};
VkPhysicalDeviceDescriptorIndexingFeatures indexing_features = {0}; VkPhysicalDeviceDescriptorIndexingFeatures indexing_features = {0};
const char* enabled_device_extensions_names[] = { const char* enabled_device_extensions_names[] = {
"VK_KHR_swapchain", "VK_KHR_swapchain",
"VK_EXT_descriptor_indexing", "VK_EXT_descriptor_indexing",
}; };
/* Enable synchronization2 features */ /* Enable descriptori indexing for bindless textures */
synchronization2_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES;
/* Enable timeline semaphores feature*/
timeline_semaphore_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;
timeline_semaphore_features.pNext = &synchronization2_features;
/* Enable descriptor indexing for bindless textures */
indexing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT; indexing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
indexing_features.pNext = &timeline_semaphore_features;
physical_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; physical_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
physical_features2.pNext = &indexing_features; physical_features2.pNext = &indexing_features;
vkGetPhysicalDeviceFeatures2(context->vulkan_handles.physical_device, &physical_features2); vkGetPhysicalDeviceFeatures2(context->physical_device, &physical_features2);
device_feature_supported = indexing_features.descriptorBindingPartiallyBound && indexing_features.runtimeDescriptorArray; bindless_supported = indexing_features.descriptorBindingPartiallyBound && indexing_features.runtimeDescriptorArray;
if (device_feature_supported == VK_FALSE) { if (bindless_supported == VK_FALSE) {
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Required features for bindless design is not supported"); LOGF(_("Required features for bindless design not supported\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_MISSING_FEATURES_FOR_BINDLESS_DESIGN;
}
device_feature_supported = synchronization2_features.synchronization2;
if (device_feature_supported == VK_FALSE) {
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Required features for Synchronization2 is not supported");
return RSE_ERROR_INTERNAL_ERROR;
}
device_feature_supported = timeline_semaphore_features.timelineSemaphore;
if (device_feature_supported == VK_FALSE) {
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Required features for timeline semaphore is not supported");
return RSE_ERROR_INTERNAL_ERROR;
} }
/* Get information about supported queue families */ /* Get information about supported queue families */
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device, vkGetPhysicalDeviceQueueFamilyProperties(context->physical_device, &queue_families_property_count, NULL);
&queue_families_property_count,
NULL);
rse_malloc(queue_family_properties, sizeof(VkQueueFamilyProperties) * queue_families_property_count); rse_malloc(queue_family_properties, sizeof(VkQueueFamilyProperties) * queue_families_property_count);
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device, vkGetPhysicalDeviceQueueFamilyProperties(context->physical_device, &queue_families_property_count,
&queue_families_property_count,
queue_family_properties); queue_family_properties);
/* Get queue families that support graphics operations AND have most /* Get queue families that support graphics operations AND have most
@@ -419,12 +396,12 @@ static rse_err_t create_device(struct graphics_context_t* context)
{ {
size_t numberOfQueues = 0; size_t numberOfQueues = 0;
for (uint32_t familyIdx = 0; familyIdx < queue_families_property_count; ++familyIdx) { for (uint32_t familyIdx = 0; familyIdx < queue_families_property_count; ++familyIdx) {
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Checking queue: %d", familyIdx); LOGI("Checking queue: %ld", familyIdx);
if (queue_family_properties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) { if (queue_family_properties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
if (queue_family_properties[familyIdx].queueCount > numberOfQueues) { if (queue_family_properties[familyIdx].queueCount > numberOfQueues) {
graphics_family_idx = familyIdx; graphics_family_idx = familyIdx;
graphics_family_found = true; graphics_family_found = true;
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Setting graphics family idx to %d", familyIdx); LOGI("Setting graphics family idx to %ld", familyIdx);
numberOfQueues = queue_family_properties[familyIdx].queueCount; numberOfQueues = queue_family_properties[familyIdx].queueCount;
device_queues_count++; device_queues_count++;
} }
@@ -432,10 +409,7 @@ static rse_err_t create_device(struct graphics_context_t* context)
/* Check for family with surface support */ /* Check for family with surface support */
VkBool32 presentSupport = VK_FALSE; VkBool32 presentSupport = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(context->vulkan_handles.physical_device, vkGetPhysicalDeviceSurfaceSupportKHR(context->physical_device, familyIdx, context->surface, &presentSupport);
familyIdx,
context->vulkan_handles.surface,
&presentSupport);
if (presentSupport) { if (presentSupport) {
presentation_familiy_idx = familyIdx; presentation_familiy_idx = familyIdx;
presentation_familiy_found = true; presentation_familiy_found = true;
@@ -452,25 +426,23 @@ static rse_err_t create_device(struct graphics_context_t* context)
} }
/* This is not magic number ;P. We want to support exact number of required queues flags */ /* This is not magic number ;P. We want to support exact number of required queues flags */
if (graphics_family_idx < 0) { if (0 > graphics_family_idx) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Selected physical device does not support required queue capabilities"); LOGF(_("Selected physical device does not support required queue capabilities\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_QUEUE_NOT_SUPPORTED;
} }
context->queue_family_indices[QUEUE_FAMILY_INDEX_GRAPHICS] = graphics_family_idx; context->queue_family_indices[0] = graphics_family_idx;
context->queue_family_indices[QUEUE_FAMILY_INDEX_PRESENTATION] = presentation_familiy_idx;
rse_malloc(graphics_familiy_queue_priorities, rse_malloc(graphics_familiy_queue_priorities, sizeof(float) * queue_family_properties[graphics_family_idx].queueCount);
sizeof(float) * queue_family_properties[graphics_family_idx].queueCount);
for (size_t i = 0; i < queue_family_properties[graphics_family_idx].queueCount; i++) { for (size_t i = 0; i < queue_family_properties[graphics_family_idx].queueCount; i++) {
graphics_familiy_queue_priorities[i] = 1.0f; graphics_familiy_queue_priorities[i] = 1.0f;
} }
rse_malloc(presentation_familiy_queue_priorities, rse_malloc(presentation_familiy_queue_priorities, sizeof(float) * queue_family_properties[presentation_familiy_idx].queueCount);
sizeof(float) * queue_family_properties[presentation_familiy_idx].queueCount);
for (size_t i = 0; i < queue_family_properties[presentation_familiy_idx].queueCount; i++) { for (size_t i = 0; i < queue_family_properties[presentation_familiy_idx].queueCount; i++) {
presentation_familiy_queue_priorities[i] = 1.0f; presentation_familiy_queue_priorities[i] =
1.0f;
} }
device_queue_createinfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; device_queue_createinfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
@@ -498,19 +470,13 @@ static rse_err_t create_device(struct graphics_context_t* context)
device_create_info.ppEnabledExtensionNames = enabled_device_extensions_names; device_create_info.ppEnabledExtensionNames = enabled_device_extensions_names;
device_create_info.pEnabledFeatures = NULL; device_create_info.pEnabledFeatures = NULL;
if (VK_SUCCESS != vkCreateDevice(context->vulkan_handles.physical_device, if (VK_SUCCESS != vkCreateDevice(context->physical_device, &device_create_info, NULL, &context->device)) {
&device_create_info, LOGF(_("Failed to create logical device\n"));
NULL, return VULKAN_ERROR_DEVICE_CREATION_FAILED;
&context->vulkan_handles.device)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create logical device");
return RSE_ERROR_INTERNAL_ERROR;
} }
vkGetDeviceQueue(context->vulkan_handles.device, graphics_family_idx, 0, &context->vulkan_handles.graphics_queue); vkGetDeviceQueue(context->device, graphics_family_idx, 0, &context->graphics_queue);
vkGetDeviceQueue(context->vulkan_handles.device, vkGetDeviceQueue(context->device, presentation_familiy_idx, 0, &context->present_queue);
presentation_familiy_idx,
0,
&context->vulkan_handles.present_queue);
rse_free(presentation_familiy_queue_priorities); rse_free(presentation_familiy_queue_priorities);
rse_free(graphics_familiy_queue_priorities); rse_free(graphics_familiy_queue_priorities);
@@ -533,19 +499,20 @@ static rse_err_t create_memory_allocator(struct graphics_context_t* context)
VmaAllocatorCreateInfo allocator_create_info = {0}; VmaAllocatorCreateInfo allocator_create_info = {0};
allocator_create_info.vulkanApiVersion = VK_API_VERSION_1_3; allocator_create_info.vulkanApiVersion = VK_API_VERSION_1_3;
allocator_create_info.physicalDevice = context->vulkan_handles.physical_device; allocator_create_info.physicalDevice = context->physical_device;
allocator_create_info.device = context->vulkan_handles.device; allocator_create_info.device = context->device;
allocator_create_info.instance = context->vulkan_handles.instance; allocator_create_info.instance = context->instance;
allocator_create_info.pVulkanFunctions = &vulkan_functions; allocator_create_info.pVulkanFunctions = &vulkan_functions;
if (VK_SUCCESS != vmaCreateAllocator(&allocator_create_info, &context->vulkan_handles.allocator)) { if (VK_SUCCESS != vmaCreateAllocator(&allocator_create_info, &context->allocator)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create Vulkan Memory Allocator"); LOGF(_("Failed to create Vulkan Memory Allocator\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_ALLOCATOR_CREATION_FAILED;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
/** /**
* @brief Create a Sync Objects * @brief Create a Sync Objects
* *
@@ -555,57 +522,35 @@ static rse_err_t create_memory_allocator(struct graphics_context_t* context)
static rse_err_t create_sync_objects(struct graphics_context_t* context) static rse_err_t create_sync_objects(struct graphics_context_t* context)
{ {
VkSemaphoreCreateInfo semaphore_info = {0}; VkSemaphoreCreateInfo semaphore_info = {0};
VkSemaphoreTypeCreateInfo semaphore_type = {0};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphore_type.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; VkFenceCreateInfo fenceInfo = {0};
semaphore_type.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
semaphore_type.initialValue = 0; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
semaphore_type.pNext = NULL;
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
if (VK_SUCCESS != vkCreateSemaphore(context->vulkan_handles.device, if (VK_SUCCESS != vkCreateSemaphore(context->device, &semaphore_info, NULL, &context->image_available_semaphores[i]) ||
&semaphore_info, VK_SUCCESS != vkCreateSemaphore(context->device, &semaphore_info, NULL, &context->render_finished_semaphores[i]) ||
NULL, VK_SUCCESS != vkCreateFence(context->device, &fenceInfo, NULL, &context->in_flight_fences[i])) {
&context->vulkan_handles.image_available_semaphores[i]) || LOGF(_("Failed to create sync objects.\n"));
VK_SUCCESS != vkCreateSemaphore(context->vulkan_handles.device, return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED;
&semaphore_info,
NULL,
&context->vulkan_handles.render_finished_semaphores[i])) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create sync objects.");
return RSE_ERROR_INTERNAL_ERROR;
} }
} }
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphore_type.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
semaphore_type.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
semaphore_type.initialValue = 0;
semaphore_type.pNext = NULL;
semaphore_info.pNext = &semaphore_type;
if (VK_SUCCESS != vkCreateSemaphore(context->vulkan_handles.device,
&semaphore_info,
NULL,
&context->vulkan_handles.timeline_semaphore)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create timeline semaphore.");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t vulkan_init(struct graphics_context_t* context)
rse_err_t init_vulkan(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_instance(&context->vulkan_handles.instance)); STATUS_CHECK(create_instance(&context->instance));
STATUS_CHECK(setup_debug_messenger(context->vulkan_handles.instance, &context->vulkan_handles.debug_messenger)); #ifndef NDEBUG
STATUS_CHECK( STATUS_CHECK(setup_debug_messenger(context->instance, &context->debug_messenger));
create_surface(context->vulkan_handles.instance, context->window_handle, &context->vulkan_handles.surface)); #endif
STATUS_CHECK(pick_physical_device(context->vulkan_handles.instance, &context->vulkan_handles.physical_device)); STATUS_CHECK(create_surface(context->instance, context->window_handle, &context->surface));
STATUS_CHECK(pick_physical_device(context->instance, &context->physical_device));
STATUS_CHECK(create_device(context)); STATUS_CHECK(create_device(context));
STATUS_CHECK(create_memory_allocator(context)); STATUS_CHECK(create_memory_allocator(context));
STATUS_CHECK(create_render_pass(context)); STATUS_CHECK(create_render_pass(context));
@@ -617,135 +562,105 @@ rse_err_t vulkan_init(struct graphics_context_t* context)
return status; return status;
} }
rse_err_t vulkan_run(struct graphics_context_t* context) rse_err_t run_vulkan(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_sync_objects(context)); STATUS_CHECK(create_sync_objects(context));
init_time();
return status; return status;
} }
rse_err_t vulkan_draw_frame(struct graphics_context_t* context) rse_err_t draw_frame(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR;
uint32_t image_index = 0U; uint32_t image_index = 0U;
VkResult result = VK_FALSE; VkResult result = VK_FALSE;
VkSubmitInfo2 submit_info = {0}; VkSubmitInfo submit_info = {0};
VkSemaphore wait_semaphores[1];
VkPipelineStageFlags wait_stages[1];
VkSemaphore signal_semaphores[1];
VkPresentInfoKHR present_info = {0}; VkPresentInfoKHR present_info = {0};
VkSwapchainKHR swap_chains[1]; VkSwapchainKHR swap_chains[1];
VkSemaphoreWaitInfo timeline_wait_info = {0};
VkSemaphoreSubmitInfo timeline_signal_info = {0};
VkSemaphoreSubmitInfo binary_signal_info = {0};
VkSemaphoreSubmitInfo binary_wait_info = {0};
VkSemaphoreSubmitInfo signal_infos[2] = {0};
VkCommandBufferSubmitInfo cmd_buffer_submit_info = {0};
timeline_wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO; vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
timeline_wait_info.pNext = NULL;
timeline_wait_info.pSemaphores = &context->vulkan_handles.timeline_semaphore;
timeline_wait_info.semaphoreCount = 1;
timeline_wait_info.pValues = &context->frame_finished_idx;
vkWaitSemaphores(context->vulkan_handles.device, &timeline_wait_info, UINT64_MAX); update_time();
STATUS_CHECK(buffers_update(context)); result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphores[context->current_frame],
VK_NULL_HANDLE, &image_index);
result = vkAcquireNextImageKHR(context->vulkan_handles.device,
context->swapchain_data.swapchain,
UINT64_MAX,
context->vulkan_handles.image_available_semaphores[context->current_frame],
VK_NULL_HANDLE,
&image_index);
if (result == VK_ERROR_OUT_OF_DATE_KHR) { if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreate_swapchain(context); recreate_swapchain(context);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to acquire swapchain image"); LOGF(_("Failed to acquire swapchain image\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_ACQUIRE_SWAPCHAIN_FAILED;
} }
update_uniform_buffers(context);
/* Only reset the fence if we are submitting work */
vkResetFences(context->device, 1, &context->in_flight_fences[context->current_frame]);
reset_command_buffer(context); reset_command_buffer(context);
record_command_buffer(context, image_index); record_command_buffer(context, image_index);
context->frame_finished_idx++; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
binary_signal_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; wait_semaphores[0] = context->image_available_semaphores[context->current_frame];
binary_signal_info.pNext = NULL; wait_stages[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
binary_signal_info.semaphore = context->vulkan_handles.render_finished_semaphores[context->current_frame]; submit_info.waitSemaphoreCount = 1;
binary_signal_info.value = 0; submit_info.pWaitSemaphores = wait_semaphores;
binary_signal_info.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.pWaitDstStageMask = wait_stages;
timeline_signal_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; submit_info.commandBufferCount = 1;
timeline_signal_info.pNext = NULL; submit_info.pCommandBuffers = &context->command_buffers[context->current_frame];
timeline_signal_info.semaphore = context->vulkan_handles.timeline_semaphore;
timeline_signal_info.value = context->frame_finished_idx;
timeline_signal_info.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
signal_infos[0] = binary_signal_info; signal_semaphores[0] = context->render_finished_semaphores[context->current_frame];
signal_infos[1] = timeline_signal_info; submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores;
binary_wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; if (vkQueueSubmit(context->graphics_queue, 1, &submit_info, context->in_flight_fences[context->current_frame]) != VK_SUCCESS) {
binary_wait_info.pNext = NULL; LOGF(_("Failed to submit draw command buffer!\n"));
binary_wait_info.semaphore = context->vulkan_handles.image_available_semaphores[context->current_frame]; return VULKAN_ERROR_DRAW_FAILED;
binary_wait_info.value = 0;
binary_wait_info.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
cmd_buffer_submit_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
cmd_buffer_submit_info.pNext = NULL;
cmd_buffer_submit_info.commandBuffer = context->vulkan_handles.command_buffers[context->current_frame];
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
submit_info.pNext = NULL;
submit_info.commandBufferInfoCount = 1;
submit_info.pCommandBufferInfos = &cmd_buffer_submit_info;
submit_info.signalSemaphoreInfoCount = 2;
submit_info.pSignalSemaphoreInfos = signal_infos;
submit_info.waitSemaphoreInfoCount = 1;
submit_info.pWaitSemaphoreInfos = &binary_wait_info;
if (vkQueueSubmit2(context->vulkan_handles.graphics_queue, 1, &submit_info, NULL)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to submit draw command buffer!");
return RSE_ERROR_INTERNAL_ERROR;
} }
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1; present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &context->vulkan_handles.render_finished_semaphores[image_index]; present_info.pWaitSemaphores = signal_semaphores;
swap_chains[0] = context->swapchain_data.swapchain; swap_chains[0] = context->swapchain;
present_info.swapchainCount = 1; present_info.swapchainCount = 1;
present_info.pSwapchains = swap_chains; present_info.pSwapchains = swap_chains;
present_info.pImageIndices = &image_index; present_info.pImageIndices = &image_index;
result = vkQueuePresentKHR(context->vulkan_handles.present_queue, &present_info); result = vkQueuePresentKHR(context->present_queue, &present_info);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || context->is_framebuffer_resized) { if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || context->is_framebuffer_resized) {
context->is_framebuffer_resized = false; context->is_framebuffer_resized = false;
recreate_swapchain(context); recreate_swapchain(context);
} else if (result != VK_SUCCESS) { } else if (result != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to submit draw command buffer!"); LOGF(_("Failed to submit draw command buffer!\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_DRAW_FAILED;
} }
context->current_frame = (context->current_frame + 1) % SWAP_BUFFER_COUNT; context->current_frame = (context->current_frame + 1) % SWAP_BUFFER_COUNT;
return status; return RSE_ERROR_NO_ERROR;
} }
void vulkan_deinit(struct graphics_context_t* context) void deinit_vulkan(struct graphics_context_t* context)
{ {
vkDeviceWaitIdle(context->vulkan_handles.device);
destroy_buffers(context); destroy_buffers(context);
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
vkDestroySemaphore(context->vulkan_handles.device, context->vulkan_handles.image_available_semaphores[i], NULL); vkDestroySemaphore(context->device, context->image_available_semaphores[i], NULL);
vkDestroySemaphore(context->vulkan_handles.device, context->vulkan_handles.render_finished_semaphores[i], NULL); vkDestroySemaphore(context->device, context->render_finished_semaphores[i], NULL);
vkDestroyFence(context->device, context->in_flight_fences[i], NULL);
} }
vkDestroySemaphore(context->vulkan_handles.device, context->vulkan_handles.timeline_semaphore, NULL);
destroy_textures(context); destroy_textures(context);
@@ -754,11 +669,12 @@ void vulkan_deinit(struct graphics_context_t* context)
destroy_render_pass(context); destroy_render_pass(context);
cleanup_swapchain(context); cleanup_swapchain(context);
destroy_pipelines(context); destroy_pipelines(context);
destroy_descriptors(context);
vmaDestroyAllocator(context->vulkan_handles.allocator); vmaDestroyAllocator(context->allocator);
vkDestroyDevice(context->vulkan_handles.device, NULL); vkDestroyDevice(context->device, NULL);
DestroyDebugUtilsMessengerEXT(context->vulkan_handles.instance, context->vulkan_handles.debug_messenger, NULL); #ifndef NDEBUG
vkDestroySurfaceKHR(context->vulkan_handles.instance, context->vulkan_handles.surface, NULL); DestroyDebugUtilsMessengerEXT(context->instance, context->debug_messenger, NULL);
vkDestroyInstance(context->vulkan_handles.instance, NULL); #endif
vkDestroySurfaceKHR(context->instance, context->surface, NULL);
vkDestroyInstance(context->instance, NULL);
} }
+4 -4
View File
@@ -23,7 +23,7 @@
* @param context Graphics context handle * @param context Graphics context handle
* @return rse_err_t 0 on success. Status code on failure * @return rse_err_t 0 on success. Status code on failure
*/ */
rse_err_t vulkan_init(struct graphics_context_t* context); rse_err_t init_vulkan(struct graphics_context_t* context);
/** /**
* @brief Initialize Vulkan backend with descriptors * @brief Initialize Vulkan backend with descriptors
@@ -31,14 +31,14 @@ rse_err_t vulkan_init(struct graphics_context_t* context);
* @param context Graphics context handle * @param context Graphics context handle
* @return rse_err_t 0 on success. Status code on failure * @return rse_err_t 0 on success. Status code on failure
*/ */
rse_err_t vulkan_run(struct graphics_context_t* context); rse_err_t run_vulkan(struct graphics_context_t* context);
/** /**
* @brief Deinitialize Vulkan backend * @brief Deinitialize Vulkan backend
* *
* @param context Graphics context handle * @param context Graphics context handle
* */ * */
void vulkan_deinit(struct graphics_context_t* context); void deinit_vulkan(struct graphics_context_t* context);
/** /**
* @brief Draw frame on the screen. * @brief Draw frame on the screen.
@@ -46,6 +46,6 @@ void vulkan_deinit(struct graphics_context_t* context);
* @param context Graphics context * @param context Graphics context
* @return rse_err_t * @return rse_err_t
*/ */
rse_err_t vulkan_draw_frame(struct graphics_context_t* context); rse_err_t draw_frame(struct graphics_context_t* context);
#endif /* GRAPHICS_VULKANBASE_H */ #endif /* GRAPHICS_VULKANBASE_H */
+196 -216
View File
@@ -1,24 +1,20 @@
#include "vulkan_buffers.h" #include "vulkan_buffers.h"
#include <cglm/affine-pre.h>
#include <cglm/cam.h>
#include <cglm/cglm.h>
#include <cglm/mat4.h>
#include <cglm/util.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <vulkan/vulkan_core.h>
#include "src/graphics_context.h" #include "src/graphics_context.h"
#include "src/vulkan_commons.h" #include "src/vulkan_commons.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/linked_list.h"
#include "vulkan_errors.h"
#include "utilities/logger.h"
#include "utilities/localization.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/vector.h" #include "math.h"
#include "vulkan_commands.h" #include "vulkan_commands.h"
#include <stdint.h>
#include <string.h>
#define MAX_VERTEX_BUFFER_SIZE 33554432 /* 32 MB*/ #define MAX_VERTEX_BUFFER_SIZE 33554432 /* 32 MB*/
#define CMD_BUFFER_SIZE (MAX_INSTANCE_NUMBER * sizeof(VkDrawIndexedIndirectCommand))
/** /**
* @brief Copy one buffer's data to another * @brief Copy one buffer's data to another
@@ -27,18 +23,13 @@
* @param dst Destination buffer * @param dst Destination buffer
* @param size Size of buffer to copy * @param size Size of buffer to copy
*/ */
static rse_err_t copy_buffer(struct graphics_context_t* context, static void copy_buffer(struct graphics_context_t* context, VkBuffer src, VkBuffer dst, VkDeviceSize size, VkDeviceSize dest_offset)
VkBuffer src,
VkBuffer dst,
VkDeviceSize size,
VkDeviceSize dest_offset)
{ {
/* Vulkan buffers can only be copied using command buffers */ /* Vulkan buffers can only be copied using command buffers */
rse_err_t status = RSE_ERROR_NO_ERROR;
VkBufferCopy copy_region = {0}; VkBufferCopy copy_region = {0};
VkCommandBuffer command_buffer = VK_NULL_HANDLE; VkCommandBuffer command_buffer = {0};
STATUS_CHECK(begin_single_time_command(context, &command_buffer)); command_buffer = begin_single_time_command(context);
copy_region.srcOffset = 0U; copy_region.srcOffset = 0U;
copy_region.dstOffset = dest_offset; copy_region.dstOffset = dest_offset;
@@ -46,11 +37,10 @@ static rse_err_t copy_buffer(struct graphics_context_t* context,
vkCmdCopyBuffer(command_buffer, src, dst, 1, &copy_region); vkCmdCopyBuffer(command_buffer, src, dst, 1, &copy_region);
STATUS_CHECK(end_single_time_comands(context, command_buffer)); end_single_time_comands(context, command_buffer);
return status;
} }
/** /**
* @brief Create a buffer holding all vertex data * @brief Create a buffer holding all vertex data
* *
@@ -67,16 +57,14 @@ static rse_err_t create_vertex_buffer(struct graphics_context_t* context)
buffer_size = MAX_VERTEX_BUFFER_SIZE; buffer_size = MAX_VERTEX_BUFFER_SIZE;
/* Create Vertex Buffer*/ /* Create Vertex Buffer*/
STATUS_CHECK(create_buffer(context, STATUS_CHECK(create_buffer(context, buffer_size,
buffer_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
0, /* Will not be mapped with vmaMapMemory */ 0, /* Will not be mapped with vmaMapMemory */
&context->render_targets[RENDER_TARGET_3D].vertex_buffer)); &context->vertex_buffer));
context->render_targets[RENDER_TARGET_3D].vertex_buffer.allocated_size = 0; context->vertex_buffer.allocated_size = 0;
return RSE_ERROR_NO_ERROR;
return status;
} }
/** /**
@@ -94,19 +82,17 @@ static rse_err_t create_index_buffer(struct graphics_context_t* context)
buffer_size = MAX_VERTEX_BUFFER_SIZE; buffer_size = MAX_VERTEX_BUFFER_SIZE;
/* Create Index Buffer*/ /* Create Index Buffer*/
STATUS_CHECK(create_buffer(context, STATUS_CHECK(create_buffer(context, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
buffer_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
0, /* Will not be mapped with vmaMapMemory */ 0, /* Will not be mapped with vmaMapMemory */
&context->render_targets[RENDER_TARGET_3D].index_buffer)); &context->index_buffer));
context->render_targets[RENDER_TARGET_3D].index_buffer.allocated_size = 0; context->index_buffer.allocated_size = 0;
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
/** /**
* @brief Create instance buffer alongside draw indirect command buffer * @brief Create instance buffers
* *
* @param[in/out] context Graphics context handle * @param[in/out] context Graphics context handle
* @return RSE_ERROR_NO_ERROR on success. * @return RSE_ERROR_NO_ERROR on success.
@@ -114,51 +100,53 @@ static rse_err_t create_index_buffer(struct graphics_context_t* context)
static rse_err_t create_instance_buffers(struct graphics_context_t* context) static rse_err_t create_instance_buffers(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
size_t iter = 0U;
VkDeviceSize buffer_size; VkDeviceSize buffer_size;
buffer_size = sizeof(struct instance_data_t) * MAX_INSTANCE_NUMBER; buffer_size = sizeof(struct instance_data_t) * MAX_INSTANCE_NUMBER;
STATUS_CHECK( for (iter = 0; iter < MAX_MESH_NUMBER; ++iter) {
create_buffer(context, /* Create Instance Buffer */
buffer_size, STATUS_CHECK(create_buffer(context, buffer_size,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, &context->instance_buffers[iter]));
&context->render_targets[RENDER_TARGET_3D].instance_buffer));
/* TODO: Move creation to somewhere else? */
STATUS_CHECK(create_buffer(context,
CMD_BUFFER_SIZE,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
0,
&context->render_targets[RENDER_TARGET_3D].draw_indirect_command_buffer));
context->render_targets[RENDER_TARGET_3D].instance_buffer.allocated_size = 0; context->instance_buffers[iter].allocated_size = 0;
return RSE_ERROR_NO_ERROR; }
}
rse_err_t buffer_create_uniform(struct graphics_context_t* context, STATUS_CHECK(create_buffer(context, sizeof(VkDrawIndexedIndirectCommand) * MAX_MESH_NUMBER,
struct vulkan_buffer_t* buffer, VK_BUFFER_USAGE_TRANSFER_DST_BIT
VkDeviceSize buffer_size) | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
{ | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(
create_buffer(context,
buffer_size,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
buffer)); &context->draw_indirect_command_buffer));
return RSE_ERROR_NO_ERROR;
}
context->uniform_buffers.vulkan_buffers[context->uniform_buffers.vulkan_buffers_count++] = *buffer; /**
* @brief Create a Uniform Buffers
*
* @param context Graphics context handle
* @return rse_err_t RSE_ERROR_NO_ERROR on success.
*/
static rse_err_t create_uniform_buffer(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkDeviceSize buffer_size = sizeof(struct uniform_buffer_object_t);
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
STATUS_CHECK(create_buffer(context, buffer_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
&context->uniform_buffers[i]));
}
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t create_buffer(struct graphics_context_t* context, rse_err_t create_buffer(struct graphics_context_t* context, const VkDeviceSize size,
const VkDeviceSize size,
VkBufferUsageFlags buffer_usage, VkBufferUsageFlags buffer_usage,
VmaMemoryUsage memory_usage, VmaMemoryUsage memory_usage,
const VmaAllocationCreateFlags allocation_flags, const VmaAllocationCreateFlags allocation_flags,
@@ -185,170 +173,142 @@ rse_err_t create_buffer(struct graphics_context_t* context,
create_info.pUserData = VK_NULL_HANDLE; create_info.pUserData = VK_NULL_HANDLE;
create_info.priority = 0.0f; create_info.priority = 0.0f;
if (VK_SUCCESS != vmaCreateBuffer(context->vulkan_handles.allocator, if (VK_SUCCESS != vmaCreateBuffer(context->allocator,
&vertex_buffer_info, &vertex_buffer_info,
&create_info, &create_info,
&buffer->buffer, &buffer->buffer,
&buffer->allocation, &buffer->allocation,
&buffer->allocation_info)) { &buffer->allocation_info)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create a vertex buffer"); LOGF(_("Failed to create a vertex buffer\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_BUFFER_CREATION_FAILED;
} }
buffer->allocated_size = size;
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
void destroy_buffer(struct graphics_context_t* context, struct vulkan_buffer_t* buffer) void destroy_buffer(struct graphics_context_t* context, struct vulkan_buffer_t* buffer)
{ {
vmaDestroyBuffer(context->vulkan_handles.allocator, buffer->buffer, buffer->allocation); vmaDestroyBuffer(context->allocator, buffer->buffer, buffer->allocation);
} }
rse_err_t add_vertices(struct graphics_context_t* context,
uint16_t mesh_id,
size_t vertices_count,
const struct vertex_t* vertices,
size_t indices_count,
uint16_t* indices)
{
// static uint16_t last_index = 0U;
// uint16_t max_index = 0U;
// size_t idx = 0U;
rse_err_t status = RSE_ERROR_NO_ERROR;
struct vulkan_buffer_t staging_buffer;
size_t vertices_size = sizeof(vertices[0]) * vertices_count;
size_t indices_size = sizeof(indices[0]) * indices_count;
size_t staging_buffer_size = vertices_size > indices_size ? vertices_size : indices_size;
struct vulkan_buffer_t* vertex_buffer = &context->render_targets[RENDER_TARGET_3D].vertex_buffer; void update_uniform_buffers(struct graphics_context_t* context)
struct vulkan_buffer_t* index_buffer = &context->render_targets[RENDER_TARGET_3D].index_buffer; {
struct uniform_buffer_object_t ubo = {0};
struct vec3_t eye = {0};
struct vec3_t center = {0};
struct vec3_t up = {0};
struct vec3_t rotation_vec = {1.0f, 0.0f, 0.0f};
float fovy;
float z_near;
float z_far;
eye.z = -5.0f;
up.y = 1.0f;
fovy = math_deg_to_radians(45.0f);
z_near = 0.1f;
z_far = 20.0f;
ubo.model = math_rotate(math_uniform_mat4(), math_deg_to_radians(0.0f), rotation_vec);
ubo.view = math_look_at(&eye, &center, &up);
ubo.proj = math_perspective(fovy,
context->swapchain_extent.width / (float)context->swapchain_extent.height,
z_near,
z_far);
memcpy(context->uniform_buffers[context->current_frame].allocation_info.pMappedData, &ubo, sizeof(ubo));
}
rse_err_t add_vertices(struct graphics_context_t* context, uint16_t mesh_id,
const struct vertex_t* vertices,
const uint16_t* indices,
struct buffer_data_t* vertex_data,
struct buffer_data_t* index_data)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkDrawIndexedIndirectCommand draw_indirect_command;
struct vulkan_buffer_t staging_buffer;
size_t vertices_size = sizeof(vertices[0]) * vertex_data->count;
size_t indices_size = sizeof(indices[0]) * index_data->count;
struct vulkan_buffer_t* index_buffer = &context->index_buffer;
/* Creating staging buffer*/ /* Creating staging buffer*/
STATUS_CHECK( STATUS_CHECK(create_buffer(context, MAX_VERTEX_BUFFER_SIZE,
create_buffer(context,
staging_buffer_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, VMA_ALLOCATION_CREATE_MAPPED_BIT
| VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
&staging_buffer)); &staging_buffer));
/* Fill staging buffer with vertex data */ /* Fill staging buffer with vertex data */
memset(staging_buffer.allocation_info.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE);
memcpy(staging_buffer.allocation_info.pMappedData, vertices, vertices_size); memcpy(staging_buffer.allocation_info.pMappedData, vertices, vertices_size);
copy_buffer(context, staging_buffer.buffer, vertex_buffer->buffer, vertices_size, vertex_buffer->allocated_size); copy_buffer(context, staging_buffer.buffer,
vertex_buffer->allocated_size += vertices_size; context->vertex_buffer.buffer,
vertices_size,
context->vertex_buffer.allocated_size);
vertex_data->buffer_offset = context->vertex_offset;
context->vertex_offset += vertex_data->count;
context->vertex_buffer.allocated_size += vertices_size;
/* Fill staging buffer with index data */ /* Fill staging buffer with index data */
memset(staging_buffer.allocation_info.pMappedData, 0, indices_size);
memcpy(staging_buffer.allocation_info.pMappedData, indices, indices_size); memcpy(staging_buffer.allocation_info.pMappedData, indices, indices_size);
copy_buffer(context, staging_buffer.buffer, index_buffer->buffer, indices_size, index_buffer->allocated_size); copy_buffer(context, staging_buffer.buffer,
index_buffer->buffer,
indices_size,
index_buffer->allocated_size);
index_data->buffer_offset = index_buffer->allocated_size;
index_buffer->allocated_size += indices_size; index_buffer->allocated_size += indices_size;
destroy_buffer(context, &staging_buffer); destroy_buffer(context, &staging_buffer);
context->mesh_data.mesh_bucket[mesh_id].is_dirty = true; draw_indirect_command.indexCount = vertex_data->count;
context->mesh_data.mesh_bucket[mesh_id].vertex_count = vertices_count; draw_indirect_command.instanceCount = 0;
context->mesh_data.mesh_bucket[mesh_id].entities_count = 0; draw_indirect_command.firstIndex = 0;
context->mesh_data.mesh_bucket[mesh_id].instances_count = 0; draw_indirect_command.vertexOffset = vertex_data->buffer_offset;
context->mesh_data.mesh_bucket[mesh_id].index_count = indices_count; draw_indirect_command.firstInstance = 0;
memcpy(context->draw_indirect_command_buffer.allocation_info.pMappedData + (mesh_id * sizeof(VkDrawIndexedIndirectCommand)), &draw_indirect_command, sizeof(VkDrawIndexedIndirectCommand));
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t mesh_add_instance(struct graphics_context_t* context, uint32_t mesh_id, struct instance_data_t* instance_data) rse_err_t update_mesh_instances(struct graphics_context_t* context, uint16_t mesh_id, struct instance_data_t* instance_data)
{
/* For now only store information about instances. Upload them to instance buffer later */
RSE_VECTOR_PUSH_BACK(context->mesh_data.mesh_bucket[mesh_id].instances_data, *instance_data);
context->mesh_data.mesh_bucket[mesh_id].instances_count++;
context->mesh_data.mesh_bucket[mesh_id].is_dirty = true;
return RSE_ERROR_NO_ERROR;
}
rse_err_t buffers_update(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
bool dirty_found = false; VkDrawIndexedIndirectCommand draw_indirect_command;
char* instance_buffer_mapped = NULL;
size_t iter = 0U;
size_t buffer_offset = 0U;
VkDrawIndexedIndirectCommand* draw_indirect_commands_buffer = NULL;
VkDrawIndexedIndirectCommand* draw_indirect_command = NULL;
VkDeviceSize draw_indirect_commands_buffer_size =
sizeof(VkDrawIndexedIndirectCommand) * context->mesh_data.mesh_count;
struct vulkan_buffer_t staging_buffer; struct vulkan_buffer_t staging_buffer;
struct mesh_t* mesh = NULL; struct vulkan_buffer_t* instance_buffer = &context->instance_buffers[mesh_id];
uint32_t indices_count = 0U; size_t instance_size = sizeof(struct instance_data_t);
rse_malloc(draw_indirect_commands_buffer, draw_indirect_commands_buffer_size); /* Creating staging buffer*/
for (iter = 0U; iter < context->mesh_data.mesh_count; ++iter) { STATUS_CHECK(create_buffer(context, instance_size,
mesh = &context->mesh_data.mesh_bucket[iter];
draw_indirect_command = &draw_indirect_commands_buffer[iter];
/* TODO: I wonder if we save some time here actually by checking for dirty...*/
if ((dirty_found == true) || (context->mesh_data.mesh_bucket[iter].is_dirty == true)) {
memcpy((char*)context->render_targets[RENDER_TARGET_3D].instance_buffer.allocation_info.pMappedData +
buffer_offset,
mesh->instances_data.data,
mesh->instances_count * sizeof(struct instance_data_t));
dirty_found = true;
}
mesh->is_dirty = false;
buffer_offset += mesh->instances_count * sizeof(struct instance_data_t);
if (iter == 0) {
draw_indirect_command->firstIndex = 0;
draw_indirect_command->firstInstance = 0;
draw_indirect_command->vertexOffset = 0;
} else {
draw_indirect_command->firstIndex = indices_count;
draw_indirect_command->firstInstance = draw_indirect_commands_buffer[iter - 1].firstInstance +
draw_indirect_commands_buffer[iter - 1].instanceCount;
draw_indirect_command->vertexOffset = (context->mesh_data.mesh_bucket[iter - 1].vertex_count + draw_indirect_commands_buffer[iter-1].vertexOffset);
}
draw_indirect_command->indexCount = mesh->index_count;
draw_indirect_command->instanceCount = mesh->instances_count;
indices_count += mesh->index_count;
}
/* Copy Draw indirect commands into buffer */
if (dirty_found == true) {
STATUS_CHECK(
create_buffer(context,
draw_indirect_commands_buffer_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, VMA_ALLOCATION_CREATE_MAPPED_BIT
| VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
&staging_buffer)); &staging_buffer));
/* Copy command buffer information about instances */ /* Copy transformation information about instance */
memcpy(staging_buffer.allocation_info.pMappedData, memcpy(staging_buffer.allocation_info.pMappedData, instance_data, instance_size);
draw_indirect_commands_buffer, copy_buffer(context, staging_buffer.buffer,
draw_indirect_commands_buffer_size); instance_buffer->buffer,
copy_buffer(context, instance_size,
staging_buffer.buffer, instance_buffer->allocated_size);
context->render_targets[RENDER_TARGET_3D].draw_indirect_command_buffer.buffer,
draw_indirect_commands_buffer_size,
0);
destroy_buffer(context, &staging_buffer); destroy_buffer(context, &staging_buffer);
}
/* Update instance data uniform buffer */ instance_buffer->allocated_size += instance_size;
context->render_targets[RENDER_TARGET_3D].instance_buffer.allocated_size = buffer_offset;
buffer_offset = 0U;
instance_buffer_mapped =
(char*)context->render_targets[RENDER_TARGET_3D].instance_buffer.allocation_info.pMappedData;
for (iter = 0U; iter < context->mesh_data.mesh_count; ++iter) {
mesh = &context->mesh_data.mesh_bucket[iter];
rse_memcpy(instance_buffer_mapped + buffer_offset,
mesh->instances_data.data,
mesh->instances_count * sizeof(struct instance_data_t));
buffer_offset += mesh->instances_count * sizeof(struct instance_data_t);
}
rse_free(draw_indirect_commands_buffer); memcpy(&draw_indirect_command, context->draw_indirect_command_buffer.allocation_info.pMappedData + (mesh_id * sizeof(VkDrawIndexedIndirectCommand)), sizeof(VkDrawIndexedIndirectCommand));
draw_indirect_command.instanceCount++;
memcpy(context->draw_indirect_command_buffer.allocation_info.pMappedData + (mesh_id * sizeof(VkDrawIndexedIndirectCommand)), &draw_indirect_command, sizeof(VkDrawIndexedIndirectCommand));
return status; return RSE_ERROR_NO_ERROR;
} }
/** /**
@@ -361,13 +321,18 @@ rse_err_t buffers_update(struct graphics_context_t* context)
*/ */
rse_err_t record_command_buffer(struct graphics_context_t* context, uint32_t image_index) rse_err_t record_command_buffer(struct graphics_context_t* context, uint32_t image_index)
{ {
size_t iter = 0U; VkCommandBuffer command_buffer = context->command_buffers[context->current_frame];
VkCommandBuffer command_buffer = context->vulkan_handles.command_buffers[context->current_frame]; VkDescriptorSet* descriptor_sets = NULL;
VkCommandBufferBeginInfo begin_info = {0}; VkCommandBufferBeginInfo begin_info = {};
VkRenderPassBeginInfo render_pass_info = {0}; VkRenderPassBeginInfo render_pass_info = {};
VkViewport viewport = {0}; VkViewport viewport = {};
VkRect2D scissor = {0}; VkRect2D scissor = {};
VkClearValue clear_values[2] = {0}; /* For color and depth stencil */ VkDeviceSize offsets[] = {0};
VkClearValue clear_values[2] = {}; //For color and depth stencil
size_t i = 0U;
RSE_LINKED_LIST_TO_ARRAY(context->pipelines_data.descriptor_sets, descriptor_sets);
clear_values[0].color.float32[0] = 0.0f; clear_values[0].color.float32[0] = 0.0f;
clear_values[0].color.float32[1] = 0.0f; clear_values[0].color.float32[1] = 0.0f;
@@ -381,42 +346,58 @@ rse_err_t record_command_buffer(struct graphics_context_t* context, uint32_t ima
begin_info.pInheritanceInfo = NULL; begin_info.pInheritanceInfo = NULL;
if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) { if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to start recording command buffer"); LOGF(_("Failed to start recording command buffer\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED;
} }
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_info.renderPass = context->vulkan_handles.render_pass; render_pass_info.renderPass = context->render_pass;
render_pass_info.framebuffer = context->swapchain_data.swapchain_framebuffers[image_index]; render_pass_info.framebuffer = context->swapchain_framebuffers[image_index];
render_pass_info.renderArea.offset.x = 0; render_pass_info.renderArea.offset.x = 0;
render_pass_info.renderArea.offset.y = 0; render_pass_info.renderArea.offset.y = 0;
render_pass_info.renderArea.extent = context->swapchain_data.swapchain_extent; render_pass_info.renderArea.extent = context->swapchain_extent;
render_pass_info.clearValueCount = 2; render_pass_info.clearValueCount = 2;
render_pass_info.pClearValues = clear_values; render_pass_info.pClearValues = clear_values;
vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
/* TODO: Iterate over pipelines, do NOT hardocde 0 here */
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, context->pipelines_data.pipelines[0]);
viewport.x = 0.0f; viewport.x = 0.0f;
viewport.y = 0.0f; viewport.y = 0.0f;
viewport.width = (float)(context->swapchain_data.swapchain_extent.width); viewport.width = (float)(context->swapchain_extent.width);
viewport.height = (float)(context->swapchain_data.swapchain_extent.height); viewport.height = (float)(context->swapchain_extent.height);
viewport.minDepth = 0.0f; viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f; viewport.maxDepth = 1.0f;
vkCmdSetViewport(command_buffer, 0, 1, &viewport); vkCmdSetViewport(command_buffer, 0, 1, &viewport);
scissor.offset.x = 0; scissor.offset.x = 0;
scissor.offset.y = 0; scissor.offset.y = 0;
scissor.extent = context->swapchain_data.swapchain_extent; scissor.extent = context->swapchain_extent;
vkCmdSetScissor(command_buffer, 0, 1, &scissor); vkCmdSetScissor(command_buffer, 0, 1, &scissor);
for (iter = 0U; iter < context->pipelines_data.pipelines_count; ++iter) { vkCmdBindVertexBuffers(command_buffer, 0, 1, &context->vertex_buffer.buffer, offsets);
context->renderer_data.renderers[iter].render_function(context, iter); vkCmdBindIndexBuffer(command_buffer, context->index_buffer.buffer, 0, VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, context->pipelines_data.pipeline_layout, 0, 2,
descriptor_sets, 0, NULL);
for (i = 0; i < MAX_MESH_NUMBER; ++i) {
VkBuffer instance_buffer = {context->instance_buffers[i].buffer};
vkCmdBindVertexBuffers(command_buffer, 1, 1, &instance_buffer, offsets);
vkCmdDrawIndexedIndirect(command_buffer,
context->draw_indirect_command_buffer.buffer,
sizeof(VkDrawIndexedIndirectCommand) * i,
1,
0); /* Can be 0 since we are not doing multiple draws each loop */
} }
vkCmdEndRenderPass(command_buffer); vkCmdEndRenderPass(command_buffer);
if (VK_SUCCESS != vkEndCommandBuffer(command_buffer)) { if (VK_SUCCESS != vkEndCommandBuffer(command_buffer)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to record command buffer. Validate your commands."); LOGF(_("Failed to record command buffer. Validate your commands.\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
@@ -429,33 +410,32 @@ rse_err_t create_buffers(struct graphics_context_t* context)
STATUS_CHECK(create_vertex_buffer(context)); STATUS_CHECK(create_vertex_buffer(context));
STATUS_CHECK(create_index_buffer(context)); STATUS_CHECK(create_index_buffer(context));
STATUS_CHECK(create_instance_buffers(context)); STATUS_CHECK(create_instance_buffers(context));
STATUS_CHECK(create_uniform_buffer(context));
return status; return status;
} }
void reset_command_buffer(struct graphics_context_t* context) void reset_command_buffer(struct graphics_context_t* context)
{ {
vkResetCommandBuffer(context->vulkan_handles.command_buffers[context->current_frame], vkResetCommandBuffer(context->command_buffers[context->current_frame], /*VkCommandBufferResetFlagBits*/ 0);
/*VkCommandBufferResetFlagBits*/ 0);
} }
void destroy_buffers(struct graphics_context_t* context) void destroy_buffers(struct graphics_context_t* context)
{ {
size_t iter = 0U; size_t i = 0U;
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].vertex_buffer); for (i = 0; i < SWAP_BUFFER_COUNT; ++i) {
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].index_buffer); vmaDestroyBuffer(context->allocator,
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].draw_indirect_command_buffer); context->uniform_buffers[i].buffer,
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].instance_buffer); context->uniform_buffers[i].allocation);
for (iter = 0U; iter < context->uniform_buffers.vulkan_buffers_count; ++iter) {
vmaDestroyBuffer(context->vulkan_handles.allocator,
context->uniform_buffers.vulkan_buffers[iter].buffer,
context->uniform_buffers.vulkan_buffers[iter].allocation);
} }
context->uniform_buffers.vulkan_buffers_count = 0; vmaDestroyBuffer(context->allocator, context->vertex_buffer.buffer, context->vertex_buffer.allocation);
for (iter = 0; iter < context->mesh_data.mesh_count; ++iter) { vmaDestroyBuffer(context->allocator, context->index_buffer.buffer, context->index_buffer.allocation);
rse_free(context->mesh_data.mesh_bucket[iter].instances_data.data); vmaDestroyBuffer(context->allocator, context->draw_indirect_command_buffer.buffer, context->draw_indirect_command_buffer.allocation);
for(i = 0; i < MAX_MESH_NUMBER; ++i) {
vmaDestroyBuffer(context->allocator, context->instance_buffers[i].buffer, context->instance_buffers[i].allocation);
} }
} }
+23 -25
View File
@@ -1,12 +1,14 @@
#ifndef VULKAN_BUFFERS_H #ifndef VULKAN_BUFFERS_H
#define VULKAN_BUFFERS_H #define VULKAN_BUFFERS_H
#include <stdint.h>
#include <vk_mem_alloc.h>
#include "graphics_context.h"
#include "utilities/commons.h"
#include "vulkan_commons.h" #include "vulkan_commons.h"
#include "utilities/commons.h"
#include "graphics_context.h"
#include <vma/vk_mem_alloc.h>
#include <stdint.h>
/** /**
* @brief Create buffers needed by vulkan pipeline * @brief Create buffers needed by vulkan pipeline
@@ -17,26 +19,27 @@
rse_err_t create_buffers(struct graphics_context_t* context); rse_err_t create_buffers(struct graphics_context_t* context);
/** /**
* @brief Create a Uniform Buffers * @brief Update uniform buffers
* *
* @param context Graphics context handle * @param context Graphics context handle
* @param buffer Buffer handle to hold new buffer *
* @return rse_err_t RSE_ERROR_NO_ERROR on success.
*/ */
rse_err_t buffer_create_uniform(struct graphics_context_t* context, struct vulkan_buffer_t* buffer, void update_uniform_buffers(struct graphics_context_t* context);
VkDeviceSize buffer_size);
/** /**
* @brief Add vertices and indices to vulkan buffers * @brief Add vertices and indices to vulkan buffers
* *
* @param vertices_count Number of provided vertices
* @param vertices Vertices to add * @param vertices Vertices to add
* @param indices_count Number of provided indices
* @param indices Indices to add * @param indices Indices to add
* @param vertex_data Mutable vertex data. Count will be read and buffer offset will be set
* @param index_data Mutable index data. Count will be read and buffer offset will be set
* @return rse_err_t RSE_ERROR_NO_ERROR on success * @return rse_err_t RSE_ERROR_NO_ERROR on success
*/ */
rse_err_t add_vertices(struct graphics_context_t* context, uint16_t mesh_id, size_t vertices_count, rse_err_t add_vertices(struct graphics_context_t* context, uint16_t mesh_id,
const struct vertex_t* vertices, size_t indices_count, uint16_t* indices); const struct vertex_t* vertices,
const uint16_t* indices,
struct buffer_data_t* vertex_data,
struct buffer_data_t* index_data);
/** /**
* @brief Add instance data to vulkan buffers * @brief Add instance data to vulkan buffers
@@ -45,8 +48,7 @@ rse_err_t add_vertices(struct graphics_context_t* context, uint16_t mesh_id, siz
* @param instance_data Instance data * @param instance_data Instance data
* @return rse_err_t RSE_ERROR_NO_ERROR on success * @return rse_err_t RSE_ERROR_NO_ERROR on success
*/ */
rse_err_t mesh_add_instance(struct graphics_context_t* context, uint32_t mesh_id, rse_err_t update_mesh_instances(struct graphics_context_t* context, uint16_t mesh_id, struct instance_data_t* instance_data);
struct instance_data_t* instance_data);
/** /**
* @brief Record commands for given image index * @brief Record commands for given image index
@@ -71,6 +73,7 @@ void reset_command_buffer(struct graphics_context_t* context);
*/ */
void destroy_buffers(struct graphics_context_t* context); void destroy_buffers(struct graphics_context_t* context);
/** /**
* @brief Helper function. Creates a buffer object * @brief Helper function. Creates a buffer object
* *
@@ -82,17 +85,12 @@ void destroy_buffers(struct graphics_context_t* context);
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors: * @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED
*/ */
rse_err_t create_buffer(struct graphics_context_t* context, const VkDeviceSize size, VkBufferUsageFlags buffer_usage, rse_err_t create_buffer(struct graphics_context_t* context, const VkDeviceSize size,
VmaMemoryUsage memory_usage, const VmaAllocationCreateFlags allocation_flags, VkBufferUsageFlags buffer_usage,
VmaMemoryUsage memory_usage,
const VmaAllocationCreateFlags allocation_flags,
struct vulkan_buffer_t* buffer); struct vulkan_buffer_t* buffer);
/**
* @brief Update vulkan buffers. Instance data and all that. This must be called before running vulkan application
*
* @param context Graphics context
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
rse_err_t buffers_update(struct graphics_context_t* context);
/** /**
* @brief Destroy provided buffer * @brief Destroy provided buffer
+30 -47
View File
@@ -1,31 +1,31 @@
#include "vulkan_commands.h" #include "vulkan_commands.h"
#include <SDL3/SDL_log.h>
#include <vulkan/vulkan_core.h>
#include "src/graphics_context.h" #include "src/graphics_context.h"
#include "utilities/commons.h"
#include "vulkan_commons.h" #include "vulkan_commons.h"
#include "vulkan_errors.h"
#include "utilities/logger.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/localization.h"
/** /**
* @brief Create Command Pool * @brief Create Command Pools
* *
* @param context Graphics context handle * @param context Graphics context handle
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors: * @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED * VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED
*/ */
static rse_err_t create_command_pool(struct graphics_context_t* context) static rse_err_t create_command_pools(struct graphics_context_t* context)
{ {
VkCommandPoolCreateInfo create_info; VkCommandPoolCreateInfo create_info;
create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
create_info.pNext = NULL; create_info.pNext = NULL;
create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
create_info.queueFamilyIndex = context->queue_family_indices[QUEUE_FAMILY_INDEX_GRAPHICS]; create_info.queueFamilyIndex = context->queue_family_indices[0];
if (VK_SUCCESS != vkCreateCommandPool(context->vulkan_handles.device, &create_info, NULL, &context->vulkan_handles.command_pool)) { if (VK_SUCCESS != vkCreateCommandPool(context->device, &create_info, NULL, &context->command_pool)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create command pool"); LOGF(_("Failed to create command pool\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
@@ -45,27 +45,28 @@ static rse_err_t allocate_command_buffers(struct graphics_context_t* context)
/* Allocate memory for command buffers */ /* Allocate memory for command buffers */
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocate_info.pNext = NULL; allocate_info.pNext = NULL;
allocate_info.commandPool = context->vulkan_handles.command_pool; allocate_info.commandPool = context->command_pool;
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate_info.commandBufferCount = SWAP_BUFFER_COUNT; allocate_info.commandBufferCount = SWAP_BUFFER_COUNT;
if (VK_SUCCESS != vkAllocateCommandBuffers(context->vulkan_handles.device, if (VK_SUCCESS != vkAllocateCommandBuffers(context->device,
&allocate_info, &allocate_info,
context->vulkan_handles.command_buffers)) { context->command_buffers)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to allocate command buffers\n"); LOGF(_("Failed to allocate command buffers\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
rse_err_t begin_single_time_command(struct graphics_context_t* context, VkCommandBuffer* command_buffer) VkCommandBuffer begin_single_time_command(struct graphics_context_t* context)
{ {
VkCommandBufferBeginInfo begin_info = {0}; VkCommandBuffer command_buffer = VK_NULL_HANDLE;
VkCommandBufferAllocateInfo alloc_info = {0}; VkCommandBufferBeginInfo begin_info = {};
VkCommandBufferAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.pNext = NULL; alloc_info.pNext = NULL;
alloc_info.commandPool = context->vulkan_handles.command_pool; alloc_info.commandPool = context->command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = 1; alloc_info.commandBufferCount = 1;
@@ -74,21 +75,15 @@ rse_err_t begin_single_time_command(struct graphics_context_t* context, VkComman
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
begin_info.pInheritanceInfo = NULL; begin_info.pInheritanceInfo = NULL;
if (vkAllocateCommandBuffers(context->vulkan_handles.device, &alloc_info, command_buffer) != VK_SUCCESS) { vkAllocateCommandBuffers(context->device, &alloc_info, &command_buffer);
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to allocate new command buffer"); vkBeginCommandBuffer(command_buffer, &begin_info);
return RSE_ERROR_INTERNAL_ERROR;
}
if (vkBeginCommandBuffer(*command_buffer, &begin_info) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to begin command buffer");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR; return command_buffer;
} }
rse_err_t end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer) void end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer)
{ {
VkSubmitInfo submit_info = {0}; VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = NULL; submit_info.pNext = NULL;
@@ -100,30 +95,18 @@ rse_err_t end_single_time_comands(struct graphics_context_t* context, VkCommandB
submit_info.signalSemaphoreCount = 0; submit_info.signalSemaphoreCount = 0;
submit_info.pSignalSemaphores = NULL; submit_info.pSignalSemaphores = NULL;
if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) { vkEndCommandBuffer(command_buffer);
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to end command buffer");
return RSE_ERROR_INTERNAL_ERROR;
}
if (vkQueueSubmit(context->vulkan_handles.graphics_queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) { vkQueueSubmit(context->graphics_queue, 1, &submit_info, VK_NULL_HANDLE);
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed submit queue"); vkQueueWaitIdle(context->graphics_queue);
return RSE_ERROR_INTERNAL_ERROR;
}
if (vkQueueWaitIdle(context->vulkan_handles.graphics_queue) != VK_SUCCESS) { vkFreeCommandBuffers(context->device, context->command_pool, 1, &command_buffer);
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to wait for graphics queue");
return RSE_ERROR_INTERNAL_ERROR;
}
vkFreeCommandBuffers(context->vulkan_handles.device, context->vulkan_handles.command_pool, 1, &command_buffer);
return RSE_ERROR_NO_ERROR;
} }
rse_err_t init_commands(struct graphics_context_t* context) rse_err_t init_commands(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_command_pool(context)); STATUS_CHECK(create_command_pools(context));
STATUS_CHECK(allocate_command_buffers(context)); STATUS_CHECK(allocate_command_buffers(context));
return status; return status;
@@ -131,5 +114,5 @@ rse_err_t init_commands(struct graphics_context_t* context)
void destroy_commands(struct graphics_context_t* context) void destroy_commands(struct graphics_context_t* context)
{ {
vkDestroyCommandPool(context->vulkan_handles.device, context->vulkan_handles.command_pool, NULL); vkDestroyCommandPool(context->device, context->command_pool, NULL);
} }
+4 -6
View File
@@ -12,8 +12,7 @@
#ifndef VULKAN_COMMANDS_H #ifndef VULKAN_COMMANDS_H
#define VULKAN_COMMANDS_H #define VULKAN_COMMANDS_H
#include <vk_mem_alloc.h> #include <vma/vk_mem_alloc.h>
#include <vulkan/vulkan_core.h>
#include "utilities/commons.h" #include "utilities/commons.h"
#include "graphics_context.h" #include "graphics_context.h"
@@ -38,17 +37,16 @@ void destroy_commands(struct graphics_context_t* context);
* @brief Helper function, creates command buffer for recording a command. Useful for copying buffer na images. * @brief Helper function, creates command buffer for recording a command. Useful for copying buffer na images.
* *
* @param context Graphics context handle * @param context Graphics context handle
* @return RSE_ERROR_NO_ERROR on success * @return VkCommandBuffer Created command buffer, ready to be filled with commands.
*/ */
rse_err_t begin_single_time_command(struct graphics_context_t* context, VkCommandBuffer* command_buffer); VkCommandBuffer begin_single_time_command(struct graphics_context_t* context);
/** /**
* @brief Close command buffer and execute recorded command. * @brief Close command buffer and execute recorded command.
* *
* @param context Graphics context handle * @param context Graphics context handle
* @param command_buffer Command buffer for execution * @param command_buffer Command buffer for execution
* @return RSE_ERROR_NO_ERROR on success
*/ */
rse_err_t end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer); void end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer);
#endif /* VULKAN_COMMANDS_H */ #endif /* VULKAN_COMMANDS_H */
+24
View File
@@ -0,0 +1,24 @@
#include "vulkan_commons.h"
#include <time.h>
time_t g_last_frame_time;
time_t g_this_frame_time;
float g_float_delta_time;
void init_time()
{
time(&g_last_frame_time);
}
void update_time()
{
g_last_frame_time = g_this_frame_time;
time(&g_this_frame_time);
g_float_delta_time = (float)difftime(g_this_frame_time, g_last_frame_time);
}
float get_time_diff()
{
return g_float_delta_time;
}
+19 -15
View File
@@ -12,14 +12,14 @@
#ifndef VULKAN_GLOBAL_H #ifndef VULKAN_GLOBAL_H
#define VULKAN_GLOBAL_H #define VULKAN_GLOBAL_H
#include <cglm/types.h> #include "math.h"
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <vk_mem_alloc.h> #include <vma/vk_mem_alloc.h>
#include <cglm/cglm.h>
#include <stdalign.h> #include <stdalign.h>
#define SWAP_BUFFER_COUNT 3U #define SWAP_BUFFER_COUNT 2U
/** /**
* @brief Represents single vertex on the screen * @brief Represents single vertex on the screen
@@ -27,9 +27,9 @@
*/ */
struct vertex_t struct vertex_t
{ {
vec3 pos; struct vec3_t pos;
vec3 color; struct vec3_t color;
vec2 tex_coords; struct vec3_t tex_coords;
}; };
/** /**
@@ -44,18 +44,22 @@ struct vulkan_buffer_t
VmaAllocationInfo allocation_info; VmaAllocationInfo allocation_info;
}; };
struct mvp_data_t struct uniform_buffer_object_t
{ {
alignas(16) mat4 model; alignas(16) struct mat4_t model;
alignas(16) mat4 view; alignas(16) struct mat4_t view;
alignas(16) mat4 proj; alignas(16) struct mat4_t proj;
}; };
struct instance_data_t { struct instance_data_t {
alignas(16) vec3 pos; struct vec3_t pos;
alignas(16) vec3 rot; struct vec3_t rot;
alignas(4) float scale; float scale;
alignas(4) uint32_t texture_id; uint8_t texture_id;
}; };
void init_time();
void update_time();
float get_time_diff();
#endif /* VULKAN_GLOBAL_H */ #endif /* VULKAN_GLOBAL_H */
+163
View File
@@ -0,0 +1,163 @@
#include "vulkan_descriptors.h"
#include "utilities/errors_common.h"
#include "utilities/localization.h"
#include "utilities/logger.h"
#include "vulkan_commons.h"
#include "vulkan_errors.h"
#include "vulkan_image.h"
/**
* @brief Create a Descriptor Pool
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED
*/
static rse_err_t create_descriptor_pool(struct graphics_context_t* context)
{
VkDescriptorPoolSize pool_sizes[2] = {};
VkDescriptorPoolCreateInfo pool_info = {};
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
pool_sizes[0].descriptorCount = (uint32_t)(SWAP_BUFFER_COUNT);
pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_sizes[1].descriptorCount = MAX_BINDLESS_RESOURCES;
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_sizes[0];
pool_info.maxSets = (uint32_t)(SWAP_BUFFER_COUNT);
if (vkCreateDescriptorPool(context->device, &pool_info, NULL,
&context->descriptor_pool) != VK_SUCCESS) {
LOGF(_("Failed to create descriptor pool\n"));
return VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED;
}
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT;
pool_info.maxSets = MAX_BINDLESS_RESOURCES * 2;
pool_info.pPoolSizes = &pool_sizes[1];
if (vkCreateDescriptorPool(context->device, &pool_info, NULL,
&context->descriptor_pool_bindless) !=
VK_SUCCESS) {
LOGF(_("Failed to create descriptor pool\n"));
return VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED;
}
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create a Descriptor Sets
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED */
static rse_err_t create_descriptor_sets(struct graphics_context_t* context)
{
uint32_t max_binding = MAX_BINDLESS_RESOURCES - 1;
VkDescriptorSetLayout layouts[SWAP_BUFFER_COUNT] = {
context->descriptor_set_layout, context->descriptor_set_layout};
VkDescriptorSetLayout layouts_bindless[SWAP_BUFFER_COUNT] = {
context->descriptor_set_layout_bindless,
context->descriptor_set_layout_bindless};
VkDescriptorSetAllocateInfo allocInfo = {};
VkDescriptorSetAllocateInfo allocInfo_bindless = {};
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT count_info = {};
count_info.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT;
count_info.descriptorSetCount = 1;
count_info.pDescriptorCounts = &max_binding;
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = context->descriptor_pool;
allocInfo.descriptorSetCount = (uint32_t)(SWAP_BUFFER_COUNT);
allocInfo.pSetLayouts = layouts;
allocInfo_bindless = allocInfo;
allocInfo_bindless.descriptorPool = context->descriptor_pool_bindless;
allocInfo_bindless.pSetLayouts = layouts_bindless;
allocInfo_bindless.pNext = &count_info;
allocInfo_bindless.descriptorSetCount = 1;
if (vkAllocateDescriptorSets(context->device, &allocInfo,
context->descriptor_sets) != VK_SUCCESS) {
LOGF(_("Failed to allocate descriptor sets\n"));
return VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED;
}
if (vkAllocateDescriptorSets(context->device, &allocInfo_bindless,
&context->descriptor_sets_bindless) !=
VK_SUCCESS) {
LOGF(_("Failed to allocate descriptor sets\n"));
return VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED;
}
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
VkDescriptorBufferInfo buffer_info = {};
VkWriteDescriptorSet descriptor_writes[2] = {};
VkDescriptorImageInfo image_infos[RSE_MAX_IMAGE_COUNT] = {};
size_t image_iterator = 0U;
size_t image_info_iterator = 0U;
buffer_info.buffer = context->uniform_buffers[i].buffer;
buffer_info.offset = 0;
buffer_info.range = sizeof(struct uniform_buffer_object_t);
for (image_iterator = 0U; image_iterator < RSE_MAX_IMAGE_COUNT;
++image_iterator) {
if (image_exists(context, image_iterator)) {
image_infos[image_info_iterator].imageLayout =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[image_info_iterator].imageView =
context->swapchain_image_views[image_iterator];
image_infos[image_info_iterator].sampler = context->sampler;
image_info_iterator++;
}
}
descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_writes[0].dstSet = context->descriptor_sets[i];
descriptor_writes[0].dstBinding = 0;
descriptor_writes[0].dstArrayElement = 0;
descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptor_writes[0].descriptorCount = 1;
descriptor_writes[0].pBufferInfo = &buffer_info;
descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_writes[1].dstSet = context->descriptor_sets_bindless;
descriptor_writes[1].dstBinding = 1;
descriptor_writes[1].dstArrayElement = 0;
descriptor_writes[1].descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_writes[1].descriptorCount = image_info_iterator;
descriptor_writes[1].pImageInfo = image_infos;
vkUpdateDescriptorSets(context->device, 2, descriptor_writes, 0, NULL);
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t create_descriptors(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_descriptor_pool(context));
STATUS_CHECK(create_descriptor_sets(context));
return status;
}
void destroy_descriptors(struct graphics_context_t* context)
{
vkDestroyDescriptorPool(context->device, context->descriptor_pool, NULL);
vkDestroyDescriptorPool(context->device, context->descriptor_pool_bindless,
NULL);
vkDestroyDescriptorSetLayout(context->device,
context->descriptor_set_layout, NULL);
vkDestroyDescriptorSetLayout(context->device,
context->descriptor_set_layout_bindless, NULL);
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef VULKAN_DESCRIPTORS_H
#define VULKAN_DESCRIPTORS_H
#include "utilities/commons.h"
#include "graphics_context.h"
#define MAX_BINDLESS_RESOURCES 16536
/**
* @brief Create descriptors
*
* @param context Graphics context handle
* @param vulkan_state
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
rse_err_t create_descriptors(struct graphics_context_t* context);
/**
* @brief Destroy all descriptors
*
* @param context Graphics context handle
*/
void destroy_descriptors(struct graphics_context_t* context);
#endif /* VULKAN_DESCRIPTORS_H */
+65
View File
@@ -0,0 +1,65 @@
#ifndef VULKAN_ERRORS_H
#define VULKAN_ERRORS_H
#define RSE_VULKAN_MODULE_ID 0x0300U
enum vulkan_error_t
{
VULKAN_ERROR_DEBUG_MESSENGER_CREATION_FAILED = RSE_VULKAN_MODULE_ID,
VULKAN_ERROR_LAYER_NOT_SUPPORTED,
VULKAN_ERROR_EXTENSION_NOT_SUPPORTED,
VULKAN_ERROR_INSTANCE_INIT_FAILED,
VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND,
VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND,
VULKAN_ERROR_MISSING_FEATURES_FOR_BINDLESS_DESIGN,
VULKAN_ERROR_QUEUE_NOT_SUPPORTED,
VULKAN_ERROR_DEVICE_CREATION_FAILED,
VULKAN_ERROR_ALLOCATOR_CREATION_FAILED,
VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED,
VULKAN_ERROR_BUFFER_CREATION_FAILED,
VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED,
VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED,
VULKAN_ERROR_SURFACE_CREATION_FAILED,
VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED,
VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED,
VULKAN_ERROR_SHADER_FILE_OPEN_FAILED,
VULKAN_ERROR_SHADER_CREATION_FAILED,
VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED,
VULKAN_ERROR_RENDERPASS_CREATION_FAILED,
VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED,
VULKAN_ERROR_FORMAT_NOT_FOUND,
VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED,
VULKAN_ERROR_DESCRIPTORSETLAYOUT_CREATION_FAILED,
VULKAN_ERROR_DESCRIPTORSETLAYOUT_NO_BINDINGS,
VULKAN_ERROR_DESCRIPTORSETLAYOUT_TOO_MANY,
VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED,
VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED,
VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED,
VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED,
VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED,
VULKAN_ERROR_ACQUIRE_SWAPCHAIN_FAILED,
VULKAN_ERROR_MAX_INSTANCE_COUNT_REACHED,
VULKAN_ERROR_IMAGE_NOT_CREATED,
VULKAN_ERROR_IMAGE_VIEW_NOT_CREATED,
VULKAN_ERROR_MAX_TEXTURE_COUNT_REACHED,
VULKAN_ERROR_TRANSITION_IMAGE_LAYOUT_FAILED,
VULKAN_ERROR_PIPELINE_TOO_MANY_SHADER_STAGES,
VULKAN_ERROR_PIPELINE_INVALID_SHADER_STAGE,
VULKAN_ERROR_PIPELINE_INVALID_VERTEX_INPUT_RATE,
VULKAN_ERROR_PIPELINE_INVALID_BINDING,
VULKAN_ERROR_PIPELINE_INVALID_FORMAT,
VULKAN_ERROR_PIPELINE_INVALID_DYNAMIC_STATE,
VULKAN_ERROR_PIPELINE_MISSING_DYNAMIC_STATE,
VULKAN_ERROR_PIPELINE_MISSING_SHADER_STAGES,
VULKAN_ERROR_PIPELINE_MISSING_VERTEX_BINDINGS,
VULKAN_ERROR_PIPELINE_MISSING_SHADER_ATTRIBUTES,
VULKAN_ERROR_PIPELINE_MISSING_SHADER_MODULES,
VULKAN_ERROR_DRAW_FAILED,
VULKAN_ERROR_PIPELINE_ALREADY_INITIALIZED,
VULKAN_ERROR_UNKNOWN_DESCRIPTOR_TYPE,
VULKAN_ERROR_DESCRIPTOR_SET_LAYOUT_CREATION_FAILED,
VULKAN_ERROR_DESCRIPTOR_SET_CREATION_FAILED,
VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED,
};
#endif
+135 -263
View File
@@ -1,28 +1,34 @@
#include "vulkan_image.h" #include "vulkan_image.h"
#include <SDL3/SDL_log.h>
#include <stdint.h> #include <stdint.h>
#include "SDL3/SDL_video.h"
#include "src/graphics_context.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/file_utils.h" #include "utilities/file_utils.h"
#include "utilities/localization.h"
#include "utilities/logger.h"
#include "vulkan/vulkan_core.h" #include "vulkan/vulkan_core.h"
#include "vulkan_buffers.h" #include "vulkan_buffers.h"
#include "vulkan_commands.h" #include "vulkan_commands.h"
#include "vulkan_errors.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define STB_IMAGE_IMPLEMENTATION #include "stb/stb_image.h"
#include "stb_image.h"
#define IMAGE_TAKEN 1U #define IMAGE_TAKEN 1U
#define IMAGE_FREE 0U #define IMAGE_FREE 0U
rse_err_t sampler_create(struct graphics_context_t* context, VkSampler* sampler) /**
* @brief Create a sampler object for texture sampling
*
* @param[in/out] context Graphics context to create sampler in
* @return RSE_ERROR_NO_ERROR on success, error code otherwise
*/
rse_err_t create_sampler(struct graphics_context_t* context, VkSampler sampler)
{ {
VkSamplerCreateInfo create_info = {0}; VkSamplerCreateInfo create_info = {0};
@@ -45,26 +51,23 @@ rse_err_t sampler_create(struct graphics_context_t* context, VkSampler* sampler)
create_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; create_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
create_info.unnormalizedCoordinates = VK_FALSE; create_info.unnormalizedCoordinates = VK_FALSE;
if (VK_SUCCESS != vkCreateSampler(context->vulkan_handles.device, &create_info, NULL, sampler)) { if (VK_SUCCESS != vkCreateSampler(context->device, &create_info, NULL, &sampler)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create image view"); LOGF("Failed to create image view");
return RSE_ERROR_INTERNAL_ERROR; return 1;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
static rse_err_t find_supported_format(struct graphics_context_t* context, static rse_err_t find_supported_format(struct graphics_context_t* context, const VkFormat* candidates,
const VkFormat* candidates, size_t candidates_count, VkImageTiling tiling, VkFormatFeatureFlags features,
size_t candidates_count,
VkImageTiling tiling,
VkFormatFeatureFlags features,
VkFormat* found_format) VkFormat* found_format)
{ {
size_t i = 0U; size_t i = 0U;
for (i = 0U; i < candidates_count; ++i) { for (i = 0U; i < candidates_count; ++i) {
VkFormatProperties props; VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(context->vulkan_handles.physical_device, candidates[i], &props); vkGetPhysicalDeviceFormatProperties(context->physical_device, candidates[i], &props);
if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
*found_format = candidates[i]; *found_format = candidates[i];
@@ -75,20 +78,14 @@ static rse_err_t find_supported_format(struct graphics_context_t* context,
} }
} }
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to find correct format"); LOGE(_("Failed to find correct format\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_FORMAT_NOT_FOUND;
} }
static rse_err_t create_image(struct graphics_context_t* context, static rse_err_t create_image(struct graphics_context_t* context, struct vulkan_image_t* image, uint32_t width,
struct vulkan_image_t* image, uint32_t height, VkFormat format, VkSampleCountFlagBits maa_samples,
uint32_t width, VkImageUsageFlags usage, VmaAllocationCreateFlags allocationFlags)
uint32_t height,
VkFormat format,
VkSampleCountFlagBits maa_samples,
VkImageUsageFlags usage,
VmaAllocationCreateFlags allocationFlags)
{ {
VkResult result;
VkExtent3D image_extent = {0}; VkExtent3D image_extent = {0};
VkImageCreateInfo image_info = {0}; VkImageCreateInfo image_info = {0};
VmaAllocationCreateInfo alloc_create_info = {0}; VmaAllocationCreateInfo alloc_create_info = {0};
@@ -123,23 +120,16 @@ static rse_err_t create_image(struct graphics_context_t* context,
alloc_create_info.pUserData = NULL; alloc_create_info.pUserData = NULL;
alloc_create_info.priority = 1.0f; alloc_create_info.priority = 1.0f;
result = vmaCreateImage(context->vulkan_handles.allocator, if (VK_SUCCESS != vmaCreateImage(context->allocator, &image_info, &alloc_create_info, &image->image,
&image_info, &image->allocation, &image->allocation_info)) {
&alloc_create_info, LOGF(_("Failed to create image\n"));
&image->image, return VULKAN_ERROR_IMAGE_NOT_CREATED;
&image->allocation,
&image->allocation_info);
if (VK_SUCCESS != result) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create image");
return RSE_ERROR_INTERNAL_ERROR;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
static rse_err_t create_image_view(struct graphics_context_t* context, static rse_err_t create_image_view(struct graphics_context_t* context, struct vulkan_image_t* image, VkFormat format,
struct vulkan_image_t* image,
VkFormat format,
VkImageAspectFlags aspectFlags) VkImageAspectFlags aspectFlags)
{ {
VkImageViewCreateInfo image_view_create_info = {0}; VkImageViewCreateInfo image_view_create_info = {0};
@@ -160,10 +150,9 @@ static rse_err_t create_image_view(struct graphics_context_t* context,
image_view_create_info.subresourceRange.baseArrayLayer = 0U; image_view_create_info.subresourceRange.baseArrayLayer = 0U;
image_view_create_info.subresourceRange.layerCount = 1U; image_view_create_info.subresourceRange.layerCount = 1U;
if (VK_SUCCESS != if (VK_SUCCESS != vkCreateImageView(context->device, &image_view_create_info, NULL, &image->image_view)) {
vkCreateImageView(context->vulkan_handles.device, &image_view_create_info, NULL, &image->image_view)) { LOGF(_("Failed to create swapchain image view\n"));
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create swapchain image view"); return VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED;
return RSE_ERROR_INTERNAL_ERROR;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
@@ -172,18 +161,15 @@ rse_err_t create_color_resource(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
VkFormat color_format = IMAGE_FORMAT; VkFormat color_format = IMAGE_FORMAT;
VkExtent2D extent = {0}; VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
STATUS_CHECK(image_get_extent(context, &extent)); vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->physical_device, context->surface,
&physical_device_surface_capabilities);
STATUS_CHECK(create_image(context, STATUS_CHECK(create_image(context, &context->color_image, physical_device_surface_capabilities.currentExtent.width,
&context->color_image, physical_device_surface_capabilities.currentExtent.height, color_format,
extent.width,
extent.height,
color_format,
VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_8_BIT,
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0U));
0U));
STATUS_CHECK(create_image_view(context, &context->color_image, color_format, VK_IMAGE_ASPECT_COLOR_BIT)); STATUS_CHECK(create_image_view(context, &context->color_image, color_format, VK_IMAGE_ASPECT_COLOR_BIT));
return status; return status;
@@ -193,20 +179,15 @@ rse_err_t create_depth_resources(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
VkFormat depth_format = {0}; VkFormat depth_format = {0};
VkExtent2D extent = {0}; VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
STATUS_CHECK(image_get_extent(context, &extent));
STATUS_CHECK(find_depth_format(context, &depth_format)); STATUS_CHECK(find_depth_format(context, &depth_format));
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->physical_device, context->surface,
&physical_device_surface_capabilities);
STATUS_CHECK(create_image(context, STATUS_CHECK(create_image(context, &context->depth_image, physical_device_surface_capabilities.currentExtent.width,
&context->depth_image, physical_device_surface_capabilities.currentExtent.height, depth_format,
extent.width, VK_SAMPLE_COUNT_8_BIT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0U));
extent.height,
depth_format,
VK_SAMPLE_COUNT_8_BIT,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
0U));
STATUS_CHECK(create_image_view(context, &context->depth_image, depth_format, VK_IMAGE_ASPECT_DEPTH_BIT)); STATUS_CHECK(create_image_view(context, &context->depth_image, depth_format, VK_IMAGE_ASPECT_DEPTH_BIT));
return status; return status;
@@ -230,29 +211,21 @@ static rse_err_t format_to_pixel_size(VkFormat format, VkDeviceSize* format_size
case VK_FORMAT_R8_UINT: case VK_FORMAT_R8_UINT:
*format_size = 1; *format_size = 1;
break; break;
case VK_FORMAT_R8G8B8_UINT:
case VK_FORMAT_R8G8B8_UNORM:
*format_size = 3;
break;
case VK_FORMAT_R8G8B8A8_SRGB: case VK_FORMAT_R8G8B8A8_SRGB:
case VK_FORMAT_R8G8B8A8_UNORM:
*format_size = 4; *format_size = 4;
break; break;
default: default:
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed translate vulkan format to pixel width"); LOGE("Failed translate vulkan format to pixel width");
break; break;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
static rse_err_t transition_image_layout(struct graphics_context_t* context, static rse_err_t transition_image_layout(struct graphics_context_t* context, struct vulkan_image_t* image,
struct vulkan_image_t* image, VkImageLayout old_layout, VkImageLayout new_layout)
VkImageLayout old_layout,
VkImageLayout new_layout)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR;
VkImageSubresourceRange range = {0}; VkImageSubresourceRange range = {0};
VkImageMemoryBarrier image_barrier = {0}; VkImageMemoryBarrier image_barrier = {0};
VkCommandBuffer command_buffer = {0}; VkCommandBuffer command_buffer = {0};
@@ -290,22 +263,18 @@ static rse_err_t transition_image_layout(struct graphics_context_t* context,
source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else { } else {
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_TRANSITION_IMAGE_LAYOUT_FAILED;
} }
STATUS_CHECK(begin_single_time_command(context, &command_buffer)); command_buffer = begin_single_time_command(context);
vkCmdPipelineBarrier(command_buffer, source_stage, destination_stage, 0, 0, NULL, 0, NULL, 1, &image_barrier); vkCmdPipelineBarrier(command_buffer, source_stage, destination_stage, 0, 0, NULL, 0, NULL, 1, &image_barrier);
STATUS_CHECK(end_single_time_comands(context, command_buffer)); end_single_time_comands(context, command_buffer);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
static rse_err_t create_textured_image(struct graphics_context_t* context, static rse_err_t create_textured_image(struct graphics_context_t* context, uint32_t width, uint32_t height,
uint32_t width, VkFormat format, const unsigned char* pixels, uint16_t* texture_id)
uint32_t height,
VkFormat format,
const unsigned char* pixels,
rse_id_t* texture_id)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
struct vulkan_buffer_t staging_buffer = {0}; struct vulkan_buffer_t staging_buffer = {0};
@@ -331,38 +300,56 @@ static rse_err_t create_textured_image(struct graphics_context_t* context,
/* Check if we found free image handle */ /* Check if we found free image handle */
if (free_texture_image == NULL) { if (free_texture_image == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to find free texture image handle"); LOGE(_("Failed to find free texture image handle\n"));
*texture_id = -1; *texture_id = -1;
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_MAX_TEXTURE_COUNT_REACHED;
} }
STATUS_CHECK(format_to_pixel_size(format, &staging_buffer_size)); STATUS_CHECK(format_to_pixel_size(format, &staging_buffer_size));
staging_buffer_size = staging_buffer_size * width * height; staging_buffer_size = staging_buffer_size * width * height;
STATUS_CHECK( STATUS_CHECK(create_image(context, free_texture_image, width, height, format, VK_SAMPLE_COUNT_8_BIT,
create_image(context, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
free_texture_image,
width,
height,
format,
VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)); VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT));
transition_image_layout(context, // /* Put image into correct layout to copy pixels from buffer to image
free_texture_image, // memory */ range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VK_IMAGE_LAYOUT_UNDEFINED, // range.baseMipLevel = 0;
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // range.levelCount = 1;
// range.baseArrayLayer = 0;
// range.layerCount = 1;
//
// image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
// image_barrier.pNext = NULL;
// image_barrier.srcAccessMask = 0U;
// image_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
// image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
// image_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// image_barrier.srcQueueFamilyIndex = 0U;
// image_barrier.dstQueueFamilyIndex = 0U;
// image_barrier.image = free_texture_image->image;
// image_barrier.subresourceRange = range;
//
// command_buffer = begin_single_time_command();
// vkCmdPipelineBarrier(command_buffer,
// VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
// VK_PIPELINE_STAGE_TRANSFER_BIT,
// 0,
// 0,
// NULL,
// 0,
// NULL,
// 1,
// &image_barrier);
// end_single_time_comands(command_buffer);
transition_image_layout(context, free_texture_image, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
/* Copy pixel data to GPU memory */ /* Copy pixel data to GPU memory */
STATUS_CHECK( STATUS_CHECK(create_buffer(
create_buffer(context, context, staging_buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
staging_buffer_size, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, &staging_buffer));
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
&staging_buffer));
memcpy(staging_buffer.allocation_info.pMappedData, pixels, staging_buffer_size); memcpy(staging_buffer.allocation_info.pMappedData, pixels, staging_buffer_size);
@@ -378,20 +365,34 @@ static rse_err_t create_textured_image(struct graphics_context_t* context,
copy_region.imageOffset.z = 0; copy_region.imageOffset.z = 0;
copy_region.imageExtent = image_extent; copy_region.imageExtent = image_extent;
STATUS_CHECK(begin_single_time_command(context, &command_buffer)); command_buffer = begin_single_time_command(context);
vkCmdCopyBufferToImage(command_buffer, vkCmdCopyBufferToImage(command_buffer, staging_buffer.buffer, free_texture_image->image,
staging_buffer.buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
free_texture_image->image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copy_region);
end_single_time_comands(context, command_buffer); end_single_time_comands(context, command_buffer);
destroy_buffer(context, &staging_buffer); destroy_buffer(context, &staging_buffer);
transition_image_layout(context, transition_image_layout(context, free_texture_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
free_texture_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// /* Change image layout for shaders to be able to read it */
// image_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// image_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// image_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
// image_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
//
// command_buffer = begin_single_time_command();
// vkCmdPipelineBarrier(command_buffer,
// VK_PIPELINE_STAGE_TRANSFER_BIT,
// VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
// 0,
// 0,
// NULL,
// 0,
// NULL,
// 1,
// &image_barrier);
// end_single_time_comands(command_buffer);
STATUS_CHECK(create_image_view(context, free_texture_image, format, VK_IMAGE_ASPECT_COLOR_BIT)); STATUS_CHECK(create_image_view(context, free_texture_image, format, VK_IMAGE_ASPECT_COLOR_BIT));
free_texture_image->id_taken = IMAGE_TAKEN; free_texture_image->id_taken = IMAGE_TAKEN;
@@ -408,107 +409,8 @@ rse_err_t init_vulkan_images(struct graphics_context_t* context)
return status; return status;
} }
rse_err_t image_get_extent(struct graphics_context_t* context, VkExtent2D* extent) rse_err_t load_texture_from_bitmat(struct graphics_context_t* context, const unsigned char* pixel_buffer, int width,
{ int height, uint16_t* texture_id, VkFormat color_format)
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
int w = 0;
int h = 0;
if (extent == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided extent pointer is NULL");
return RSE_ERROR_NULL_POINTER;
}
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->vulkan_handles.physical_device,
context->vulkan_handles.surface,
&physical_device_surface_capabilities);
if (physical_device_surface_capabilities.currentExtent.width != UINT32_MAX) {
*extent = physical_device_surface_capabilities.currentExtent;
} else {
SDL_GetWindowSize(context->window_handle, &w, &h);
extent->width = w;
extent->height = h;
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t texture_update_image(struct graphics_context_t* context,
const unsigned char* pixels,
int width,
int height,
rse_id_t texture_id,
VkFormat format)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkDeviceSize staging_buffer_size = 0U;
VkBufferImageCopy copy_region = {0};
struct vulkan_buffer_t staging_buffer = {0};
VkExtent3D image_extent = {0};
VkCommandBuffer command_buffer = {0};
image_extent.width = width;
image_extent.height = height;
image_extent.depth = 1U;
STATUS_CHECK(format_to_pixel_size(format, &staging_buffer_size));
staging_buffer_size = staging_buffer_size * width * height;
transition_image_layout(context,
&context->texture_images[texture_id],
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
/* Copy pixel data to GPU memory */
STATUS_CHECK(
create_buffer(context,
staging_buffer_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
&staging_buffer));
memcpy(staging_buffer.allocation_info.pMappedData, pixels, staging_buffer_size);
copy_region.bufferOffset = 0U;
copy_region.bufferRowLength = 0U;
copy_region.bufferImageHeight = 0U;
copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_region.imageSubresource.mipLevel = 0U;
copy_region.imageSubresource.baseArrayLayer = 0U;
copy_region.imageSubresource.layerCount = 1U;
copy_region.imageOffset.x = 0;
copy_region.imageOffset.y = 0;
copy_region.imageOffset.z = 0;
copy_region.imageExtent = image_extent;
STATUS_CHECK(begin_single_time_command(context, &command_buffer));
vkCmdCopyBufferToImage(command_buffer,
staging_buffer.buffer,
context->texture_images[texture_id].image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copy_region);
end_single_time_comands(context, command_buffer);
destroy_buffer(context, &staging_buffer);
transition_image_layout(context,
&context->texture_images[texture_id],
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
return RSE_ERROR_NO_ERROR;
}
rse_err_t load_texture_from_bitmat(struct graphics_context_t* context,
const unsigned char* pixel_buffer,
int width,
int height,
rse_id_t* texture_id,
VkFormat color_format)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
@@ -518,82 +420,56 @@ rse_err_t load_texture_from_bitmat(struct graphics_context_t* context,
return status; return status;
} }
rse_err_t texture_load_from_buffer(struct graphics_context_t* context, const unsigned char* data_buffer, size_t buffer_size, rse_id_t* texture_id) rse_err_t load_texture_from_file(struct graphics_context_t* context, const char* file_path, uint16_t* texture_id)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR;
int width = 0; int width = 0;
int height = 0; int height = 0;
int channels = 0; int channels = 0;
unsigned char* pixel_buffer = NULL; stbi_uc* pixel_buffer = NULL;
rse_err_t status = RSE_ERROR_NO_ERROR;
pixel_buffer = stbi_load(file_path, &width, &height, &channels, STBI_rgb_alpha);
pixel_buffer = stbi_load_from_memory(data_buffer, (int)buffer_size, &width, &height, &channels, STBI_rgb_alpha);
if (pixel_buffer == NULL) { if (pixel_buffer == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to load image"); LOGE("Failed to load texture from file: %s", file_path);
return -1;
return RSE_ERROR_INTERNAL_ERROR;
} }
if (load_texture_from_bitmat(context, pixel_buffer, width, height, texture_id, VK_FORMAT_R8G8B8A8_SRGB) != RSE_ERROR_NO_ERROR) { STATUS_CHECK(load_texture_from_bitmat(context, pixel_buffer, width, height, texture_id, VK_FORMAT_R8G8B8A8_SRGB));
status = RSE_ERROR_INTERNAL_ERROR;
goto mem_free;
}
mem_free:
stbi_image_free(pixel_buffer); stbi_image_free(pixel_buffer);
return status; return status;
} }
rse_err_t texture_load_from_file(struct graphics_context_t* context, const char* file_path, rse_id_t* texture_id)
{
size_t buffer_size = 0U;
unsigned char* data_buffer = NULL;
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(file_read_bytes(file_path, &buffer_size, NULL));
rse_malloc(data_buffer, buffer_size);
STATUS_CHECK(file_read_bytes(file_path, &buffer_size, data_buffer));
if (texture_load_from_buffer(context, data_buffer, buffer_size, texture_id) != RSE_ERROR_NO_ERROR) {
status = RSE_ERROR_INTERNAL_ERROR;
}
rse_free(data_buffer);
return status;
}
void destroy_textures(struct graphics_context_t* context) void destroy_textures(struct graphics_context_t* context)
{ {
size_t i = 0U; size_t i = 0U;
vkDestroySampler(context->device, context->sampler, NULL);
for (i = 0U; i < RSE_MAX_IMAGE_COUNT; ++i) { for (i = 0U; i < RSE_MAX_IMAGE_COUNT; ++i) {
if (context->texture_images[i].id_taken == IMAGE_TAKEN) { if (context->texture_images[i].id_taken == IMAGE_TAKEN) {
vkDestroyImageView(context->vulkan_handles.device, context->texture_images[i].image_view, NULL); vkDestroyImageView(context->device, context->texture_images[i].image_view, NULL);
vmaDestroyImage(context->vulkan_handles.allocator, vmaDestroyImage(context->allocator, context->texture_images[i].image,
context->texture_images[i].image,
context->texture_images[i].allocation); context->texture_images[i].allocation);
} }
} }
vkDestroySampler(context->vulkan_handles.device, context->vulkan_handles.sampler, NULL);
vkDestroySampler(context->vulkan_handles.device, context->debug_overlay.sampler, NULL);
} }
void destroy_depth_resource(struct graphics_context_t* context) void destroy_depth_resource(struct graphics_context_t* context)
{ {
vkDestroyImageView(context->vulkan_handles.device, context->depth_image.image_view, NULL); vkDestroyImageView(context->device, context->depth_image.image_view, NULL);
vmaDestroyImage(context->vulkan_handles.allocator, context->depth_image.image, context->depth_image.allocation); vmaDestroyImage(context->allocator, context->depth_image.image, context->depth_image.allocation);
} }
void destroy_color_resource(struct graphics_context_t* context) void destroy_color_resource(struct graphics_context_t* context)
{ {
vkDestroyImageView(context->vulkan_handles.device, context->color_image.image_view, NULL); vkDestroyImageView(context->device, context->color_image.image_view, NULL);
vmaDestroyImage(context->vulkan_handles.allocator, context->color_image.image, context->color_image.allocation); vmaDestroyImage(context->allocator, context->color_image.image, context->color_image.allocation);
} }
uint8_t image_exists(struct graphics_context_t* context, rse_id_t image_id) uint8_t image_exists(struct graphics_context_t* context, uint8_t image_id)
{ {
return context->texture_images[image_id].id_taken == IMAGE_TAKEN; return context->texture_images[image_id].id_taken == IMAGE_TAKEN;
} }
@@ -618,12 +494,8 @@ rse_err_t find_depth_format(struct graphics_context_t* context, VkFormat* found_
rse_err_t status; rse_err_t status;
VkFormat formats[formats_count] = {VK_FORMAT_D32_SFLOAT}; VkFormat formats[formats_count] = {VK_FORMAT_D32_SFLOAT};
STATUS_CHECK(find_supported_format(context, STATUS_CHECK(find_supported_format(context, formats, formats_count, VK_IMAGE_TILING_OPTIMAL,
formats, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, found_format));
formats_count,
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT,
found_format));
return status; return status;
#undef formats_count #undef formats_count
+4 -20
View File
@@ -29,8 +29,6 @@
*/ */
rse_err_t init_vulkan_images(struct graphics_context_t* context); rse_err_t init_vulkan_images(struct graphics_context_t* context);
rse_err_t image_get_extent(struct graphics_context_t* context, VkExtent2D* extent);
/** /**
* @brief Loads image from file, for later to be used as a texture * @brief Loads image from file, for later to be used as a texture
* *
@@ -40,9 +38,8 @@ rse_err_t image_get_extent(struct graphics_context_t* context, VkExtent2D* exten
* @return rse_err_t RSE_ERROR_NO_ERROR on success * @return rse_err_t RSE_ERROR_NO_ERROR on success
*/ */
rse_err_t load_texture_from_bitmat(struct graphics_context_t* context, const unsigned char* pixel_buffer, int width, rse_err_t load_texture_from_bitmat(struct graphics_context_t* context, const unsigned char* pixel_buffer, int width,
int height, rse_id_t* texture_id, VkFormat color_format); int height, uint16_t* texture_id, VkFormat color_format);
rse_err_t texture_load_from_buffer(struct graphics_context_t* context, const unsigned char* data_buffer, size_t buffer_size, rse_id_t* texture_id);
/** /**
* @brief Loads image from file, for later to be used as a texture * @brief Loads image from file, for later to be used as a texture
* *
@@ -51,14 +48,7 @@ rse_err_t texture_load_from_buffer(struct graphics_context_t* context, const uns
* @param texture_id Texture ID will be set up here * @param texture_id Texture ID will be set up here
* @return rse_err_t RSE_ERROR_NO_ERROR on success * @return rse_err_t RSE_ERROR_NO_ERROR on success
*/ */
rse_err_t texture_load_from_file(struct graphics_context_t* context, const char* file_path, rse_id_t* texture_id); rse_err_t load_texture_from_file(struct graphics_context_t* context, const char* file_path, uint16_t* texture_id);
rse_err_t texture_update_image(struct graphics_context_t* context,
const unsigned char* pixels,
int width,
int height,
rse_id_t texture_id,
VkFormat format);
/** /**
* @brief Destroy all loaded textures. Usefull for closing application or just clearing. * @brief Destroy all loaded textures. Usefull for closing application or just clearing.
@@ -75,7 +65,7 @@ void destroy_textures(struct graphics_context_t* context);
* @param image_id Image ID * @param image_id Image ID
* @return uint8_t 1 on success, 0 on failure * @return uint8_t 1 on success, 0 on failure
*/ */
uint8_t image_exists(struct graphics_context_t* context, rse_id_t image_id); uint8_t image_exists(struct graphics_context_t* context, uint8_t image_id);
/** /**
* @brief Returns number of loaded texture * @brief Returns number of loaded texture
@@ -93,12 +83,6 @@ void destroy_color_resource(struct graphics_context_t* context);
rse_err_t create_depth_resources(struct graphics_context_t* context); rse_err_t create_depth_resources(struct graphics_context_t* context);
void destroy_depth_resource(struct graphics_context_t* context); void destroy_depth_resource(struct graphics_context_t* context);
/** rse_err_t create_sampler(struct graphics_context_t* context, VkSampler sampler);
* @brief Create a sampler object for texture sampling
*
* @param[in/out] context Graphics context to create sampler in
* @return RSE_ERROR_NO_ERROR on success, error code otherwise
*/
rse_err_t sampler_create(struct graphics_context_t* context, VkSampler* sampler);
#endif /* TEXTURE_H */ #endif /* TEXTURE_H */
+18 -15
View File
@@ -12,21 +12,24 @@
#include "vulkan_render_pass.h" #include "vulkan_render_pass.h"
#include "vulkan_image.h" #include "vulkan_image.h"
#include "vulkan_errors.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/logger.h"
#include "utilities/localization.h"
rse_err_t create_render_pass(struct graphics_context_t* context) rse_err_t create_render_pass(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
VkAttachmentDescription attachments[3] = {0}; VkAttachmentDescription attachments[3] = {};
VkAttachmentDescription color_attachment = {0}; VkAttachmentDescription color_attachment = {};
VkAttachmentReference color_attachment_ref = {0}; VkAttachmentReference color_attachment_ref = {};
VkAttachmentDescription color_attachment_resolve = {0}; VkAttachmentDescription color_attachment_resolve = {};
VkAttachmentReference color_attachment_resolve_ref = {0}; VkAttachmentReference color_attachment_resolve_ref = {};
VkSubpassDescription subpass = {0}; VkSubpassDescription subpass = {};
VkRenderPassCreateInfo renderpass_info = {0}; VkRenderPassCreateInfo renderpass_info = {};
VkAttachmentDescription depth_attachment = {0}; VkAttachmentDescription depth_attachment = {};
VkAttachmentReference depth_attachment_ref = {0}; VkAttachmentReference depth_attachment_ref = {};
VkSubpassDependency dependency = {0}; VkSubpassDependency dependency = {};
STATUS_CHECK(find_depth_format(context, &depth_attachment.format)); STATUS_CHECK(find_depth_format(context, &depth_attachment.format));
@@ -97,10 +100,10 @@ rse_err_t create_render_pass(struct graphics_context_t* context)
renderpass_info.dependencyCount = 1; renderpass_info.dependencyCount = 1;
renderpass_info.pDependencies = &dependency; renderpass_info.pDependencies = &dependency;
if (VK_SUCCESS != vkCreateRenderPass(context->vulkan_handles.device, &renderpass_info, if (VK_SUCCESS != vkCreateRenderPass(context->device, &renderpass_info,
NULL, &context->vulkan_handles.render_pass)) { NULL, &context->render_pass)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create render pass"); LOGF(_("Failed to create render pass\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_RENDERPASS_CREATION_FAILED;
} }
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
@@ -108,5 +111,5 @@ rse_err_t create_render_pass(struct graphics_context_t* context)
void destroy_render_pass(struct graphics_context_t* context) void destroy_render_pass(struct graphics_context_t* context)
{ {
vkDestroyRenderPass(context->vulkan_handles.device, context->vulkan_handles.render_pass, NULL); vkDestroyRenderPass(context->device, context->render_pass, NULL);
} }
+63 -60
View File
@@ -1,15 +1,16 @@
#include "vulkan_swapchain.h"
#include <SDL3/SDL_events.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "src/graphics_context.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/logger.h"
#include "utilities/localization.h"
#include "vulkan/vulkan_core.h" #include "vulkan/vulkan_core.h"
#include "vulkan_commons.h" #include "vulkan_commons.h"
#include "vulkan_descriptors.h"
#include "vulkan_errors.h"
#include "vulkan_image.h" #include "vulkan_image.h"
#include "vulkan_swapchain.h"
/** /**
* @brief Create a Swapchain * @brief Create a Swapchain
@@ -21,60 +22,56 @@ static rse_err_t create_swapchain(struct graphics_context_t* context)
{ {
VkSwapchainCreateInfoKHR create_info; VkSwapchainCreateInfoKHR create_info;
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities; VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
rse_err_t status = RSE_ERROR_NO_ERROR;
VkBool32 surfaceSupported; VkBool32 surfaceSupported;
VkExtent2D extent = {0};
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->vulkan_handles.physical_device, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
context->vulkan_handles.surface, context->physical_device, context->surface,
&physical_device_surface_capabilities); &physical_device_surface_capabilities);
STATUS_CHECK(image_get_extent(context, &extent));
/* Store extent in global variable, for later use */ /* Store extent in global variable, for later use */
context->swapchain_data.swapchain_extent = extent; context->swapchain_extent =
physical_device_surface_capabilities.currentExtent;
/* Check if device supports surface for presentation */ /* Check if device supports surface for presentation */
vkGetPhysicalDeviceSurfaceSupportKHR(context->vulkan_handles.physical_device, vkGetPhysicalDeviceSurfaceSupportKHR(context->physical_device,
context->queue_family_indices[0], context->queue_family_indices[0],
context->vulkan_handles.surface, context->surface, &surfaceSupported);
&surfaceSupported);
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
create_info.pNext = NULL; create_info.pNext = NULL;
create_info.flags = 0U; create_info.flags = 0U;
create_info.surface = context->vulkan_handles.surface; create_info.surface = context->surface;
create_info.minImageCount = SWAP_BUFFER_COUNT; create_info.minImageCount = SWAP_BUFFER_COUNT + 1;
create_info.imageFormat = IMAGE_FORMAT; create_info.imageFormat = IMAGE_FORMAT;
create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
create_info.imageExtent = context->swapchain_data.swapchain_extent; create_info.imageExtent = context->swapchain_extent;
create_info.imageArrayLayers = 1U; /* For non-stereoscopic-3D applications, this value is 1. */ create_info.imageArrayLayers =
1U; /* For non-stereoscopic-3D applications, this value is 1. */
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; /* Using only one queue family, so this is ok create_info.imageSharingMode =
VK_SHARING_MODE_EXCLUSIVE; /* Using only one queue family, so this is ok
*/ */
create_info.queueFamilyIndexCount = 1U; create_info.queueFamilyIndexCount = 1U;
create_info.pQueueFamilyIndices = context->queue_family_indices; create_info.pQueueFamilyIndices = context->queue_family_indices;
create_info.preTransform = physical_device_surface_capabilities.currentTransform; create_info.preTransform =
physical_device_surface_capabilities.currentTransform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
create_info.clipped = VK_TRUE; create_info.clipped = VK_TRUE;
create_info.oldSwapchain = VK_NULL_HANDLE; create_info.oldSwapchain = VK_NULL_HANDLE;
if (VK_SUCCESS != if (VK_SUCCESS != vkCreateSwapchainKHR(context->device, &create_info, NULL,
vkCreateSwapchainKHR(context->vulkan_handles.device, &create_info, NULL, &context->swapchain_data.swapchain)) { &context->swapchain)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create swapchain"); LOGF(_("Failed to create swapchain\n"));
return RSE_ERROR_INTERNAL_ERROR; return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED;
} }
/* Obtain swapchain images */ /* Obtain swapchain images */
vkGetSwapchainImagesKHR(context->vulkan_handles.device, vkGetSwapchainImagesKHR(context->device, context->swapchain,
context->swapchain_data.swapchain, &context->swapchain_images_count, NULL);
&context->swapchain_images_count, rse_malloc(context->swapchain_images, sizeof(VkImage) * context->swapchain_images_count);
NULL); vkGetSwapchainImagesKHR(context->device, context->swapchain,
vkGetSwapchainImagesKHR(context->vulkan_handles.device, &context->swapchain_images_count, context->swapchain_images);
context->swapchain_data.swapchain,
&context->swapchain_images_count,
context->swapchain_data.swapchain_images);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
@@ -86,13 +83,15 @@ static rse_err_t create_swapchain(struct graphics_context_t* context)
*/ */
static rse_err_t create_swapchain_image_views(struct graphics_context_t* context) static rse_err_t create_swapchain_image_views(struct graphics_context_t* context)
{ {
for (size_t i = 0; i < SWAPCHAIN_IMAGE_COUNT; i++) { rse_malloc(context->swapchain_image_views,
sizeof(VkImageView) * context->swapchain_images_count);
for (size_t i = 0; i < context->swapchain_images_count; i++) {
VkImageViewCreateInfo create_info; VkImageViewCreateInfo create_info;
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.pNext = NULL; create_info.pNext = NULL;
create_info.flags = 0U; create_info.flags = 0U;
create_info.image = context->swapchain_data.swapchain_images[i]; create_info.image = context->swapchain_images[i];
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */ create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */
create_info.format = VK_FORMAT_B8G8R8A8_SRGB; create_info.format = VK_FORMAT_B8G8R8A8_SRGB;
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
@@ -105,12 +104,11 @@ static rse_err_t create_swapchain_image_views(struct graphics_context_t* context
create_info.subresourceRange.baseArrayLayer = 0U; create_info.subresourceRange.baseArrayLayer = 0U;
create_info.subresourceRange.layerCount = 1U; create_info.subresourceRange.layerCount = 1U;
if (VK_SUCCESS != vkCreateImageView(context->vulkan_handles.device, if (VK_SUCCESS !=
&create_info, vkCreateImageView(context->device, &create_info, NULL,
NULL, &context->swapchain_image_views[i])) {
&context->swapchain_data.swapchain_image_views[i])) { LOGF(_("Failed to create swapchain image view\n"));
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create swapchain image view"); return VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED;
return RSE_ERROR_INTERNAL_ERROR;
} }
} }
@@ -124,26 +122,30 @@ static rse_err_t create_swapchain_image_views(struct graphics_context_t* context
*/ */
static rse_err_t create_framebuffers(struct graphics_context_t* context) static rse_err_t create_framebuffers(struct graphics_context_t* context)
{ {
context->swapchain_framebuffers = NULL;
rse_malloc(context->swapchain_framebuffers,
sizeof(VkFramebuffer) * context->swapchain_images_count);
for (size_t i = 0; i < context->swapchain_images_count; i++) { for (size_t i = 0; i < context->swapchain_images_count; i++) {
VkImageView attachments[] = {context->color_image.image_view, VkImageView attachments[] = {context->color_image.image_view,
context->depth_image.image_view, context->depth_image.image_view,
context->swapchain_data.swapchain_image_views[i]}; context->swapchain_image_views[i]};
VkFramebufferCreateInfo framebufferInfo = {0}; VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = context->vulkan_handles.render_pass; framebufferInfo.renderPass = context->render_pass;
framebufferInfo.attachmentCount = 3; framebufferInfo.attachmentCount = 3;
framebufferInfo.pAttachments = attachments; framebufferInfo.pAttachments = attachments;
framebufferInfo.width = context->swapchain_data.swapchain_extent.width; framebufferInfo.width = context->swapchain_extent.width;
framebufferInfo.height = context->swapchain_data.swapchain_extent.height; framebufferInfo.height = context->swapchain_extent.height;
framebufferInfo.layers = 1; framebufferInfo.layers = 1;
if (vkCreateFramebuffer(context->vulkan_handles.device, if (vkCreateFramebuffer(context->device, &framebufferInfo, NULL,
&framebufferInfo, &context->swapchain_framebuffers[i]) !=
NULL, VK_SUCCESS) {
&context->swapchain_data.swapchain_framebuffers[i]) != VK_SUCCESS) { LOGF(_("Failed to create framebuffer\n"));
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create framebuffer"); return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED;
return RSE_ERROR_INTERNAL_ERROR;
} }
} }
@@ -156,17 +158,16 @@ static rse_err_t create_framebuffers(struct graphics_context_t* context)
*/ */
void recreate_swapchain(struct graphics_context_t* context) void recreate_swapchain(struct graphics_context_t* context)
{ {
SDL_Event event;
int width = 0; int width = 0;
int height = 0; int height = 0;
SDL_GetWindowSizeInPixels(context->window_handle, &width, &height); glfwGetFramebufferSize(context->window_handle, &width, &height);
while (width == 0 || height == 0) { while (width == 0 || height == 0) {
SDL_GetWindowSizeInPixels(context->window_handle, &width, &height); glfwGetFramebufferSize(context->window_handle, &width, &height);
SDL_WaitEvent(&event); glfwWaitEvents();
} }
vkDeviceWaitIdle(context->vulkan_handles.device); vkDeviceWaitIdle(context->device);
cleanup_swapchain(context); cleanup_swapchain(context);
@@ -188,14 +189,16 @@ void cleanup_swapchain(struct graphics_context_t* context)
destroy_color_resource(context); destroy_color_resource(context);
destroy_depth_resource(context); destroy_depth_resource(context);
for (i = 0; i < context->swapchain_images_count; i++) { for (i = 0; i < context->swapchain_images_count; i++) {
vkDestroyFramebuffer(context->vulkan_handles.device, context->swapchain_data.swapchain_framebuffers[i], NULL); vkDestroyFramebuffer(context->device,
context->swapchain_framebuffers[i], NULL);
} }
for (i = 0; i < context->swapchain_images_count; i++) { for (i = 0; i < context->swapchain_images_count; i++) {
vkDestroyImageView(context->vulkan_handles.device, context->swapchain_data.swapchain_image_views[i], NULL); vkDestroyImageView(context->device, context->swapchain_image_views[i],
NULL);
} }
vkDestroySwapchainKHR(context->vulkan_handles.device, context->swapchain_data.swapchain, NULL); vkDestroySwapchainKHR(context->device, context->swapchain, NULL);
} }
rse_err_t create_swapchain_and_framebuffers(struct graphics_context_t* context) rse_err_t create_swapchain_and_framebuffers(struct graphics_context_t* context)
+89 -18
View File
@@ -11,34 +11,105 @@
#include "window.h" #include "window.h"
#include <SDL3/SDL_events.h> #include "GLFW/glfw3.h"
#include <SDL3/SDL_init.h> #include "utilities/localization.h"
#include <SDL3/SDL_timer.h> #include "window_errors.h"
#include <SDL3/SDL_video.h> #include "utilities/logger.h"
#include <stdint.h>
#include <stdio.h>
#include "SDL3/SDL_log.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_base.h"
rse_err_t window_init(SDL_Window** window_handle, bool* is_framebuffer_resized) /**
* @brief GLFW error callback
*
* @param error Error code
* @param description Error description
*/
static void error_callback(int error, const char* description)
{ {
(void)is_framebuffer_resized; (void)error;
// TODO: Actually use this parameter? LOGE("Error: %s", description);
}
*window_handle = SDL_CreateWindow("RedScarfEngine", 1024, 768, SDL_WINDOW_VULKAN); /**
* @brief Pressed key callback
*
* @param window Window in which contex the key has been pressed
* @param key The keyboard key that was pressed or released
* @param scancode The system-specific scancode of the key.
* @param action GLFW_PRESS, GLFW_RELEASE or GLFW_REPEAT
* @param mods Bit field describing which modifier keys were held down
*/
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
(void)scancode;
(void)action;
(void)mods;
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
}
static void framebuffer_resized_callback(GLFWwindow* window, int width, int height)
{
bool* is_frambuffer_resized = (bool*)glfwGetWindowUserPointer(window);
(void)window;
(void)width;
(void)height;
*is_frambuffer_resized = 1;
}
rse_err_t window_init(GLFWwindow** window_handle, bool* is_framebuffer_resized)
{
/* Initiliaze GLFW AP */
if (GLFW_TRUE != glfwInit())
{
LOGF(_("GLFW init failed!\n"));
return WINDOW_ERROR_GLFW_INIT_FAILED;
}
/* Check for Vulkan support */
if (GLFW_FALSE == glfwVulkanSupported()) {
LOGF(_("Unable to load Vulkan\n"));
return WINDOW_ERROR_VULKAN_NOT_LOADED;
}
/* Set error callback */
glfwSetErrorCallback(error_callback);
/* Create window */
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
*window_handle = glfwCreateWindow(640, 480, "RedScarfEngine", NULL, NULL);
if (NULL == *window_handle) { if (NULL == *window_handle) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Window context creation failed. Reason: %s", SDL_GetError()); LOGF(_("Window context creation failed\n"));
return RSE_ERROR_INTERNAL_ERROR; return WINDOW_ERROR_WINDOW_NOT_CREATED;
} }
glfwSetKeyCallback(*window_handle, key_callback);
glfwSetFramebufferSizeCallback(*window_handle, framebuffer_resized_callback);
glfwSetWindowUserPointer(*window_handle, is_framebuffer_resized);
return RSE_ERROR_NO_ERROR; return RSE_ERROR_NO_ERROR;
} }
void window_terminate(SDL_Window* window_handle) rse_err_t window_loop(struct graphics_context_t* context)
{ {
SDL_DestroyWindow(window_handle); while (!glfwWindowShouldClose(context->window_handle)) {
SDL_QuitSubSystem(SDL_INIT_VIDEO); glfwPollEvents();
draw_frame(context);
}
vkDeviceWaitIdle(context->device);
return RSE_ERROR_NO_ERROR;
}
void window_terminate(GLFWwindow* window_handle)
{
glfwDestroyWindow(window_handle);
glfwTerminate();
} }
+11 -3
View File
@@ -12,10 +12,10 @@
#ifndef WINDOW_H #ifndef WINDOW_H
#define WINDOW_H #define WINDOW_H
#include <SDL3/SDL_main.h>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
/* Vulkan header MUST be included before glfw */ /* Vulkan header MUST be included before glfw */
#include <GLFW/glfw3.h>
#include "utilities/commons.h" #include "utilities/commons.h"
#include "graphics_context.h" #include "graphics_context.h"
@@ -30,13 +30,21 @@
* WINDOW_WINDOW_NOT_CREATED * WINDOW_WINDOW_NOT_CREATED
* *
*/ */
rse_err_t window_init(SDL_Window **window_handle, bool *is_framebuffer_resized); rse_err_t window_init(GLFWwindow** window_handle, bool* is_framebuffer_resized);
/**
* @brief Main window loop
*
* @param context Graphics context
* @return rse_err_t WINDOW_SUCCESS or error code
*/
rse_err_t window_loop(struct graphics_context_t* context);
/** /**
* @brief Closes window and terminates GLFW * @brief Closes window and terminates GLFW
* *
* @param window_handle Pointer to window window_handle * @param window_handle Pointer to window window_handle
*/ */
void window_terminate(SDL_Window* window_handle); void window_terminate(GLFWwindow* window_handle);
#endif /* WINDOW_H */ #endif /* WINDOW_H */
+24
View File
@@ -0,0 +1,24 @@
/**
* @file window_errors.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief
* @version 0.1
* @date 2023-10-06
*
* @copyright Copyright (c) 2023
*
*/
#ifndef WINDOW_ERRORS_H
#define WINDOW_ERRORS_H
#define RSE_WINDOW_MODULE_ID 0x0200U
/* Error codes */
enum window_error_t {
WINDOW_ERROR_GLFW_INIT_FAILED = RSE_WINDOW_MODULE_ID,
WINDOW_ERROR_VULKAN_NOT_LOADED,
WINDOW_ERROR_WINDOW_NOT_CREATED
};
#endif /* WINDOW_ERRORS_H */
-13
View File
@@ -1,13 +0,0 @@
project('RedScarfEngine',
['c', 'cpp'],
version: '0.0.1',
)
# add_project_arguments('-DWAYLAND=1', language : 'c')
subdir('utilities')
subdir('graphics')
subdir('events')
subdir('red_scarf_engine')
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/python
# !WARNING! When run, this script will create prepare.lock file, which will indicate that the script has been run before.
# This is used for automation with CMake, which will run this script if and only if this file does not exists.
# If you want to run this script again, remove this file manually first.
# Script used for preparing build envirionment. I cannot use submodules, because I cannot specify commit/tag
# in submodule config. What's more, preparing vulkan SDK is complicated, so a custom scrip is needed for that.
import io
import re
import git
import os
import requests
import subprocess
REPO_DIR = os.path.join(os.getcwd(), "third_party")
class Dependency:
def __init__(self, name, file: io.TextIOWrapper):
self.name = name
while 1:
line = file.readline().split("=", 1)
if len(line) > 1:
match line[0].strip():
case "git_url":
self.git_url = line[1].strip()
case "dl_url":
self.dl_url = line[1].strip()
case "tag":
self.tag = line[1].strip()
case "custom_commands":
self.custom_commands = line[1].strip()
else:
break
def download(self):
dl_counter = 0
# Git repository
if hasattr(self, "git_url") and self.git_url:
repo = git.Repo.clone_from(self.git_url, os.path.join(REPO_DIR, self.name))
repo.git.checkout(self.tag)
elif hasattr(self, "dl_url") and self.dl_url:
while dl_counter < 10:
response = requests.get(self.dl_url)
if response.status_code == 200: # everything went fine
open(os.path.join(REPO_DIR, self.name), "wb").write(response.content)
break
dl_counter += 1
print("Failed to download {}. Retrying {}th time...", self.name, dl_counter)
time.sleep(5.0)
if hasattr(self, "custom_commands") and self.custom_commands:
p = subprocess.run(self.custom_commands, shell=True)
p.chek_returncode()
def main():
if not os.path.isdir(REPO_DIR):
os.mkdir(REPO_DIR)
f = open("dependencies.dep", "r")
os.chdir(REPO_DIR)
while True:
line = f.readline()
if not line:
break
stripped = line.strip()
# Remove comments
if stripped.startswith("#"):
return
uncommented = stripped.split("#", 1)[0]
if len(uncommented) == 0:
return
if uncommented.startswith("["):
dep_name = re.search('\[(.*)\]', uncommented).group(1)
dep = Dependency(dep_name, f)
print("Downloading dependency " + dep_name + "...")
dep.download()
f.close()
if __name__ == "__main__":
main()
+10
View File
@@ -0,0 +1,10 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror")
add_executable(${PROJECT_NAME} src/main.c)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE
graphics
utilities)
-23
View File
@@ -1,23 +0,0 @@
red_scarf_engine_srcs = [
'src/red_scarf_engine.c',
'src/main.c'
]
sdl3_dep = dependency('sdl3',
version : '>=3.4.0')
executable(
'RedScarfEngine',
red_scarf_engine_srcs,
include_directories : [
'../'
],
dependencies : [
sdl3_dep,
rse_graphics_lib_dep,
],
link_with: [
rse_events_lib,
rse_utilities_lib,
],
)
-22
View File
@@ -1,22 +0,0 @@
/**
* @file red_scarf_engine.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Red Scarf Engine main entry point
* @version 0.1
* @date 2026-03-03
*
* @copyright Copyright (c) 2026
*
*/
#ifndef RED_SCARF_ENGINE_H_
#define RED_SCARF_ENGINE_H_
#include "utilities/commons.h"
typedef struct rse_context_t* rse_context;
rse_err_t rse_init(rse_context* context);
rse_err_t rse_run(rse_context context);
#endif /* RED_SCARF_ENGINE_H_ */
+11 -5
View File
@@ -1,16 +1,22 @@
#include "red_scarf_engine.h" #include "graphics/rse_graphics.h"
#include "utilities/task.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
rse_context context; thread_t graphics_task;
struct rse_graphics_context_t* context = NULL;
(void)argc; (void)argc;
(void)argv; (void)argv;
STATUS_CHECK(rse_init(&context)); STATUS_CHECK(rse_graphics_init(&context));
STATUS_CHECK(rse_run(context)); rse_graphics_test_function(context);
create_task(&graphics_task, (thread_worker_t)rse_graphics_run, context);
task_join(&graphics_task);
task_exit();
return 0; return 0;
} }
-71
View File
@@ -1,71 +0,0 @@
#include "red_scarf_engine.h"
#include "graphics/rse_graphics.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/time_utils.h"
#include "events/rse_events_manager.h"
struct rse_context_t {
struct graphics_context_t graphics_context;
struct events_context_t events_context;
};
static void fps_print(double* start_time)
{
static uint32_t frame_counts = 0U;
double now = time_get_sec_from_start();
frame_counts++;
if ((now - *start_time) > 1.0) {
printf("\rFPS: %u", frame_counts);
fflush(stdout);
frame_counts = 0;
*start_time += 1.0;
}
}
rse_err_t rse_init(rse_context* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct rse_context_t* rse_context = NULL;
rse_malloc(*context, sizeof(struct rse_context_t));
rse_context = *context;
SDL_Init(SDL_INIT_VIDEO);
SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
STATUS_CHECK(time_init());
STATUS_CHECK(rse_events_init(&rse_context->events_context));
STATUS_CHECK(rse_graphics_init(&rse_context->graphics_context));
return RSE_ERROR_NO_ERROR;
}
rse_err_t rse_run(rse_context context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
double start_time;
start_time = time_get_sec_from_start();
STATUS_CHECK(rse_graphics_test_function(&context->graphics_context));
STATUS_CHECK(rse_graphics_run(&context->graphics_context));
while(true) {
time_update();
rse_events_main_loop(&context->events_context);
if (context->events_context.should_stop == EVENT_SHOULD_STOP_ENGINE_TRUE) {
break;
}
rse_graphics_main_loop(&context->graphics_context);
fps_print(&start_time);
}
rse_graphics_deinit(&context->graphics_context);
rse_free(context);
return status;
}
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
pushd build/red_scarf_engine/
VULKAN_SDK=${PWD}/third_party/vulkan/x86_64/ VK_LAYER_PATH=${PWD}/third_party/vulkan/x86_64/etc/vulkan/explicit_layer.d PATH=${PATH}:${PWD}/third_party/vulkan/x86_64/bin LD_LIBRARY_PATH=:${PWD}/third_party/vulkan/x86_64/lib ./RedScarfEngine
popd
-10
View File
@@ -1,10 +0,0 @@
[wrap-file]
directory = cglm-0.9.6
source_url = https://github.com/recp/cglm/archive/refs/tags/v0.9.6.tar.gz
source_filename = cglm-0.9.6.tar.gz
source_hash = be5e7d384561eb0fca59724a92b7fb44bf03e588a7eae5123a7d796002928184
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/cglm_0.9.6-1/cglm-0.9.6.tar.gz
wrapdb_version = 0.9.6-1
[provide]
cglm = cglm_dep
-7
View File
@@ -1,7 +0,0 @@
project('stb', 'c')
stb_inc = include_directories('.')
stb_dep = declare_dependency(
include_directories: stb_inc,
)
-14
View File
@@ -1,14 +0,0 @@
[wrap-file]
directory = SDL3-3.4.12
source_url = https://github.com/libsdl-org/SDL/releases/download/release-3.4.12/SDL3-3.4.12.tar.gz
source_filename = SDL3-3.4.12.tar.gz
source_hash = f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7
source_fallback_url = https://wrapdb.mesonbuild.com/v2/sdl3_3.4.12-1/get_source/SDL3-3.4.12.tar.gz
patch_filename = sdl3_3.4.12-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/sdl3_3.4.12-1/get_patch
patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sdl3_3.4.12-1/sdl3_3.4.12-1_patch.zip
patch_hash = 67e859d280c9cc2de422efebc4f1edf954984b59e79eddf6c92ab61d89cd874d
wrapdb_version = 3.4.12-1
[provide]
dependency_names = sdl3
-5
View File
@@ -1,5 +0,0 @@
[wrap-git]
url = https://github.com/nothings/stb.git
revision = HEAD
depth = 1
patch_directory = stb/
-14
View File
@@ -1,14 +0,0 @@
[wrap-file]
directory = Vulkan-Headers-1.4.346
source_url = https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.4.346.tar.gz
source_filename = vulkan-headers-1.4.346.tar.gz
source_hash = 5bb77f5d7b460e255a9e51affc00d64354986b55cf577d8eab28529cad01fc80
source_fallback_url = https://wrapdb.mesonbuild.com/v2/vulkan-headers_1.4.346-1/get_source/vulkan-headers-1.4.346.tar.gz
patch_filename = vulkan-headers_1.4.346-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/vulkan-headers_1.4.346-1/get_patch
patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/vulkan-headers_1.4.346-1/vulkan-headers_1.4.346-1_patch.zip
patch_hash = b85cbf6793193b28c5bd80858308b214816e66041b10ec00f0115b19f3a7a60e
wrapdb_version = 1.4.346-1
[provide]
dependency_names = VulkanHeaders
-14
View File
@@ -1,14 +0,0 @@
[wrap-file]
directory = VulkanMemoryAllocator-3.3.0
source_url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/archive/refs/tags/v3.3.0.tar.gz
source_filename = VulkanMemoryAllocator-3.3.0.tar.gz
source_hash = c4f6bbe6b5a45c2eb610ca9d231158e313086d5b1a40c9922cb42b597419b14e
source_fallback_url = https://wrapdb.mesonbuild.com/v2/vulkan-memory-allocator_3.3.0-1/get_source/VulkanMemoryAllocator-3.3.0.tar.gz
patch_filename = vulkan-memory-allocator_3.3.0-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/vulkan-memory-allocator_3.3.0-1/get_patch
patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/vulkan-memory-allocator_3.3.0-1/vulkan-memory-allocator_3.3.0-1_patch.zip
patch_hash = 85f3c1c82f3d1938fa295c80b06fc1c5c001f939f009b566c336a941d5d2d8b5
wrapdb_version = 3.3.0-1
[provide]
dependency_names = VulkanMemoryAllocator
-9
View File
@@ -1,9 +0,0 @@
[wrap-file]
directory = Vulkan-ValidationLayers-1.2.158
source_url = https://github.com/KhronosGroup/Vulkan-ValidationLayers/archive/v1.2.158.tar.gz
source_filename = vulkan-validationlayers-1.2.158.tar.gz
source_hash = 9d7a6eba2db88b8af6457acce0b638324ca03cde2e9589ae96749114091c6e9e
patch_url = https://wrapdb.mesonbuild.com/v2/vulkan-validationlayers_1.2.158-2/get_patch
patch_filename = vulkan-validationlayers-1.2.158-2-wrap.zip
patch_hash = d38dae45ca6a7124ee8df045a8d5288390ab836b4d28c53234f90baee5c649fc
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 KiB

+51
View File
@@ -0,0 +1,51 @@
find_package(Vulkan REQUIRED)
find_package(vma REQUIRED)
find_package(glfw3 REQUIRED)
find_package(Cmock REQUIRED)
find_package(stb REQUIRED)
add_definitions( -DRSE_TEST=1 )
add_executable(tests
src/main.c
src/mocks/vulkan/Mockvulkan_core.c
src/mocks/GLFW/Mockglfw3.c
src/mocks/vk_mem_alloc.c
src/math_tests.c
src/graphics_window_tests.c
src/graphics_vulkan_tests.c
${PROJECT_SOURCE_DIR}/graphics/src/window.c
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_base.c
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_pipeline.c
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_commands.c
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_buffers.c
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_descriptors.c
${PROJECT_SOURCE_DIR}/graphics/src/graphics.c
${PROJECT_SOURCE_DIR}/graphics/src/math.c
${PROJECT_SOURCE_DIR}/graphics/src/mesh_controller.c
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_commons.c
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_image.c
${PROJECT_SOURCE_DIR}/graphics/src/text_renderer.c
${PROJECT_SOURCE_DIR}/utilities/src/logger.c
${PROJECT_SOURCE_DIR}/utilities/src/file_utils.c
${PROJECT_SOURCE_DIR}/utilities/src/task_linux.c
)
target_include_directories(tests PRIVATE
${CMAKE_SOURCE_DIR}
${glfw3_INCLUDE_DIR}
${Vulkan_INCLUDE_DIR}
${unity_INCLUDE_DIR}
${cmock_INCLUDE_DIR}
src/mocks/
${PROJECT_SOURCE_DIR}/graphics/
${PROJECT_SOURCE_DIR}/graphics/src/
${PROJECT_SOURCE_DIR}/utilities/
)
target_link_libraries(tests PRIVATE
${cmock}
${unity}
m
)
+27
View File
@@ -0,0 +1,27 @@
/**
* @file main.c
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Main file for tests
* @version 0.1
* @date 2024-02-16
*
* @copyright Copyright (c) 2024
*
*/
#define RSE_TEST 1
#include "rse_commons.h"
#define VMA_IMPLEMENTATION
#include "unity_internals.h"
#include "rse_graphics_window_tests.h"
#include "rse_math_tests.h"
#include "rse_graphics_vulkan_tests.h"
int main(int argc, char *argv[])
{
UNITY_BEGIN();
rse_graphics_window_tests();
rse_math_tests();
rse_graphics_vulkan_tests();
return UNITY_END();
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+44
View File
@@ -0,0 +1,44 @@
#ifndef FTTYPES_H
#define FTTYPES_H
#define FT_FREETYPE_H <stdio.h>
#define FT_LOAD_RENDER 1
typedef void* FT_Library;
typedef int FT_Error;
typedef long FT_Long;
typedef unsigned long FT_ULong;
typedef unsigned int FT_UInt;
typedef FT_Long FT_Int32;
typedef struct {
int x;
int y;
} FT_Vector;
typedef struct {
int width;
int rows;
void *buffer;
} FT_Bitmap;
typedef struct {
int bitmap_left;
int bitmap_top;
FT_Vector advance;
FT_Bitmap bitmap;
} FT_GlyphSlotRec_;
typedef struct {
int num_glyphs;
FT_GlyphSlotRec_ *glyph;
} FT_FaceRec_;
typedef FT_FaceRec_* FT_Face;
FT_Error FT_Init_FreeType( FT_Library *alibrary ) { return 0; }
FT_Error FT_New_Face( FT_Library library, const char* filepathname, FT_Long face_index, FT_Face *aface ) { return 0; }
FT_Error FT_Set_Pixel_Sizes( FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height ) { return 0; }
FT_Error FT_Load_Char( FT_Face face, FT_ULong char_code, FT_Int32 load_flags ) { return 0; }
#endif // FTTYPES_H
+15
View File
@@ -0,0 +1,15 @@
#ifndef STBI_IMAGE_H
#define STBI_IMAGE_H
#define STBI_rgb_alpha 4
typedef unsigned char stbi_uc;
stbi_uc g_mocked_uc;
void stbi_image_free (void *retval_from_stbi_load) {}
stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) { return &g_mocked_uc; }
#endif // STBI_IMAGE_H
+12
View File
@@ -0,0 +1,12 @@
#include "vk_mem_alloc.h"
VkResult vmaCreateAllocator(VmaAllocatorCreateInfo* pCreateInfo, VmaAllocator* pAllocator) { return VK_SUCCESS; }
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo* pBufferCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, VkBuffer* pBuffer, VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { return VK_SUCCESS; }
VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo* pImageCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, VkImage* pImage, VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { return VK_SUCCESS; }
void vmaDestroyAllocator(VmaAllocator allocator) {}
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation) {}
void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation) {}
VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void** ppData) { return VK_SUCCESS; }
+71
View File
@@ -0,0 +1,71 @@
#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
#define AMD_VULKAN_MEMORY_ALLOCATOR_H
#include <stdint.h>
#include <vulkan/vulkan.h>
#define VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE 1
#define VMA_ALLOCATION_CREATE_MAPPED_BIT 2
#define VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT 3
#define VMA_MEMORY_USAGE_AUTO_PREFER_HOST 4
#define VMA_MEMORY_USAGE_AUTO 5;
#define VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT 6
typedef int VmaAllocation;
typedef int VmaAllocator;
typedef int VmaMemoryUsage;
typedef int VmaAllocationCreateFlags;
typedef struct {
VkBuffer buffer;
VmaAllocation allocation;
} VmaBuffer;
typedef struct {
VkImage image;
VmaAllocation allocation;
} VmaImage;
typedef struct {
float priority;
uint32_t flags;
uint32_t usage;
uint32_t requiredFlags;
uint32_t preferredFlags;
uint32_t memoryTypeBits;
void* pool;
void* pUserData;
} VmaAllocationCreateInfo;
typedef struct {
VkDeviceMemory memory;
VkDeviceSize offset;
VkDeviceSize size;
void* pMappedData;
} VmaAllocationInfo;
typedef struct {
void* vkGetInstanceProcAddr;
void* vkGetDeviceProcAddr;
} VmaVulkanFunctions;
typedef struct {
uint32_t vulkanApiVersion;
VkPhysicalDevice physicalDevice;
VkDevice device;
VkInstance instance;
VmaVulkanFunctions* pVulkanFunctions;
} VmaAllocatorCreateInfo;
VkResult vmaCreateAllocator(VmaAllocatorCreateInfo* pCreateInfo, VmaAllocator* pAllocator);
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo* pBufferCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, VkBuffer* pBuffer, VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo);
VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo* pImageCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, VkImage* pImage, VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo);
void vmaDestroyAllocator(VmaAllocator allocator);
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation);
void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation);
VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void** ppData);
#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+61
View File
@@ -0,0 +1,61 @@
/**
* @file rse_graphics_vulkan_tests.c
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief
* @version 0.1
* @date 2024-02-16
*
* @copyright Copyright (c) 2024
*
*/
#include "mocks/vulkan/Mockvulkan_core.h"
#include "mocks/GLFW/Mockglfw3.h"
#include "rse_graphics_vulkan_tests.h"
#include <stdio.h>
#include "graphics/rse_graphics.h"
#include "utilities/rse_errors_common.h"
#include "unity.h"
#include "vulkan/vulkan_core.h"
static void test_rse_rse_graphics_init(void)
{
VkExtensionProperties extenstion_properties[1];
VkLayerProperties layer_properties[1];
uint32_t count = 1;
sprintf(extenstion_properties[0].extensionName, "VK_KHR_surface");
sprintf(layer_properties[0].layerName, "VK_LAYER_KHRONOS_validation");
/* initialize GLFW */
glfwInit_ExpectAndReturn(GLFW_TRUE);
glfwVulkanSupported_ExpectAndReturn(GLFW_TRUE);
glfwSetErrorCallback_ExpectAndReturn(NULL, NULL);
glfwSetErrorCallback_IgnoreArg_callback();
glfwWindowHint_Expect(GLFW_CLIENT_API, GLFW_NO_API);
glfwCreateWindow_ExpectAndReturn(640, 480, "RedScarfEngine", NULL, NULL, (GLFWwindow*)1);
glfwSetKeyCallback_ExpectAndReturn((GLFWwindow*)1, NULL, NULL);
glfwSetKeyCallback_IgnoreArg_callback();
glfwSetFramebufferSizeCallback_ExpectAndReturn((GLFWwindow*)1, NULL, NULL);
glfwSetFramebufferSizeCallback_IgnoreArg_callback();
/* create instance */
vkEnumerateInstanceLayerProperties_ExpectAnyArgsAndReturn(VK_SUCCESS);
vkEnumerateInstanceLayerProperties_ExpectAnyArgsAndReturn(VK_SUCCESS);
vkEnumerateInstanceLayerProperties_ReturnThruPtr_pPropertyCount(&count);
/* TODO: Currently, due to the implementation, layer_properties that we get here is NULL.
* This is because mocked rse_rse_malloc does nothing. Fix this somehow */
vkEnumerateInstanceLayerProperties_ReturnThruPtr_pProperties(layer_properties);
vkEnumerateInstanceExtensionProperties_ExpectAnyArgsAndReturn(VK_SUCCESS);
vkEnumerateInstanceExtensionProperties_ExpectAnyArgsAndReturn(VK_SUCCESS);
vkEnumerateInstanceExtensionProperties_ReturnThruPtr_pPropertyCount(&count);
vkEnumerateInstanceExtensionProperties_ReturnThruPtr_pProperties(extenstion_properties);
TEST_ASSERT_EQUAL(RSE_ERROR_NO_ERROR, rse_rse_graphics_init());
}
void rse_graphics_vulkan_tests(void)
{
RUN_TEST(test_rse_rse_graphics_init);
}
+17
View File
@@ -0,0 +1,17 @@
/**
* @file rse_graphics_vulkan_tests.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Tests for rse_graphics_vulkan module
* @version 0.1
* @date 2024-02-16
*
* @copyright Copyright (c) 2024
*
*/
#ifndef GRAPHICS_VULKAN_TESTS_H
#define GRAPHICS_VULKAN_TESTS_H
void rse_graphics_vulkan_tests(void);
#endif // GRAPHICS_VULKAN_TESTS_H
+60
View File
@@ -0,0 +1,60 @@
#include "rse_graphics_window_tests.h"
/*-----------------RSE WINDOW TESTS------------------------*/
#include "mocks/GLFW/Mockglfw3.h"
#include "graphics/src/window.h"
#include "utilities/rse_errors_common.h"
#include "graphics/src/rse_window_errors.h"
void setUp (void) {}
void tearDown (void) {}
static void test_rse_window_init_window_init_success()
{
uint8_t window = 0;
glfwInit_ExpectAndReturn(GLFW_TRUE);
glfwVulkanSupported_ExpectAndReturn(GLFW_TRUE);
glfwSetErrorCallback_ExpectAndReturn(NULL, NULL);
glfwSetErrorCallback_IgnoreArg_callback();
glfwWindowHint_Expect(GLFW_CLIENT_API, GLFW_NO_API);
glfwCreateWindow_ExpectAndReturn(640, 480, "RedScarfEngine", NULL, NULL, (GLFWwindow*)&window);
glfwSetKeyCallback_ExpectAndReturn((GLFWwindow*)&window, NULL, NULL);
glfwSetKeyCallback_IgnoreArg_callback();
glfwSetFramebufferSizeCallback_ExpectAndReturn((GLFWwindow*)&window, NULL, NULL);
glfwSetFramebufferSizeCallback_IgnoreArg_callback();
TEST_ASSERT_EQUAL(RSE_ERROR_NO_ERROR, window_init());
}
static void test_rse_window_init_glfw_init_failed()
{
glfwInit_ExpectAndReturn(GLFW_FALSE);
TEST_ASSERT_EQUAL(WINDOW_ERROR_GLFW_INIT_FAILED, window_init());
}
static void test_rse_window_init_glfw_vulkan_not_supported()
{
glfwInit_ExpectAndReturn(GLFW_TRUE);
glfwVulkanSupported_ExpectAndReturn(GLFW_FALSE);
TEST_ASSERT_EQUAL(WINDOW_ERROR_VULKAN_NOT_LOADED, window_init());
}
static void test_rse_window_init_window_not_created()
{
glfwInit_ExpectAndReturn(GLFW_TRUE);
glfwVulkanSupported_ExpectAndReturn(GLFW_TRUE);
glfwSetErrorCallback_ExpectAndReturn(NULL, NULL);
glfwSetErrorCallback_IgnoreArg_callback();
glfwWindowHint_Expect(GLFW_CLIENT_API, GLFW_NO_API);
glfwCreateWindow_ExpectAndReturn(640, 480, "RedScarfEngine", NULL, NULL, NULL);
TEST_ASSERT_EQUAL(WINDOW_ERROR_WINDOW_NOT_CREATED, window_init());
}
void rse_graphics_window_tests(void)
{
RUN_TEST(test_rse_window_init_window_init_success);
RUN_TEST(test_rse_window_init_glfw_init_failed);
RUN_TEST(test_rse_window_init_glfw_vulkan_not_supported);
RUN_TEST(test_rse_window_init_window_not_created);
}
+17
View File
@@ -0,0 +1,17 @@
/**
* @file rse_graphics_window_tests.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Tests for rse_graphics module
* @version 0.1
* @date 2024-02-16
*
* @copyright Copyright (c) 2024
*
*/
#ifndef GRAPHICS_TESTS_H
#define GRAPHICS_TESTS_H
void rse_graphics_window_tests(void);
#endif // GRAPHICS_TESTS_H
+231
View File
@@ -0,0 +1,231 @@
/**
* @file rse_math_tests.c
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Tests for rse_math module
* @version 0.1
* @date 2024-02-16
*
* @copyright Copyright (c) 2024
*
*/
#include "rse_math_tests.h"
#include "graphics/src/rse_math.h"
#include "unity.h"
#include <stdio.h>
#include <math.h>
static void test_rse_math_test_vector3_normalize(void)
{
float res_x = 0.8017837f;
float res_y = 0.2672612f;
float res_z = 0.5345224f;
struct vec3_t test_vec = {3.0f, 1.0f, 2.0f};
struct vec3_t result = rse_math_normalize_vec3(&test_vec);
TEST_ASSERT_EQUAL_FLOAT(res_x, result.x);
TEST_ASSERT_EQUAL_FLOAT(res_y, result.y);
TEST_ASSERT_EQUAL_FLOAT(res_z, result.z);
}
static void test_rse_math_test_vector3_cross(void)
{
float res_x = -3.0f;
float res_y = 6.0f;
float res_z = -3.0f;
struct vec3_t test_vec1 = {2.0f, 3.0f, 4.0f};
struct vec3_t test_vec2 = {5.0f, 6.0f, 7.0f};
struct vec3_t result = rse_math_cross_vec3(&test_vec1, &test_vec2);
TEST_ASSERT_EQUAL_FLOAT(res_x, result.x);
TEST_ASSERT_EQUAL_FLOAT(res_y, result.y);
TEST_ASSERT_EQUAL_FLOAT(res_z, result.z);
}
static void test_rse_math_test_look_at(void)
{
struct vec3_t eye;
struct vec3_t center;
struct vec3_t up;
struct mat4_t result;
struct mat4_t expected;
eye.x = 0.0f;
eye.y = 0.0f;
eye.z = 5.0f;
center.x = 0.0f;
center.y = 0.0f;
center.z = 0.0f;
up.x = 0.0f;
up.y = 0.1f;
up.z = 0.0f;
expected.x_1 = 1.0f;
expected.y_1 = 0.0f;
expected.z_1 = 0.0f;
expected.w_1 = 0.0f;
expected.x_2 = 0.0f;
expected.y_2 = 1.0f;
expected.z_2 = 0.0f;
expected.w_2 = 0.0f;
expected.x_3 = 0.0f;
expected.y_3 = 0.0f;
expected.z_3 = 1.0f;
expected.w_3 = 0.0f;
expected.x_4 = 0.0f;
expected.y_4 = 0.0f;
expected.z_4 = -5.0f;
expected.w_4 = 1.0f;
result = rse_math_look_at(&eye, &center, &up);
TEST_ASSERT_EQUAL_FLOAT(expected.x_1, result.x_1);
TEST_ASSERT_EQUAL_FLOAT(expected.y_1, result.y_1);
TEST_ASSERT_EQUAL_FLOAT(expected.z_1, result.z_1);
TEST_ASSERT_EQUAL_FLOAT(expected.w_1, result.w_1);
TEST_ASSERT_EQUAL_FLOAT(expected.x_2, result.x_2);
TEST_ASSERT_EQUAL_FLOAT(expected.y_2, result.y_2);
TEST_ASSERT_EQUAL_FLOAT(expected.z_2, result.z_2);
TEST_ASSERT_EQUAL_FLOAT(expected.w_2, result.w_2);
TEST_ASSERT_EQUAL_FLOAT(expected.x_3, result.x_3);
TEST_ASSERT_EQUAL_FLOAT(expected.y_3, result.y_3);
TEST_ASSERT_EQUAL_FLOAT(expected.z_3, result.z_3);
TEST_ASSERT_EQUAL_FLOAT(expected.w_3, result.w_3);
TEST_ASSERT_EQUAL_FLOAT(expected.x_4, result.x_4);
TEST_ASSERT_EQUAL_FLOAT(expected.y_4, result.y_4);
TEST_ASSERT_EQUAL_FLOAT(expected.z_4, result.z_4);
TEST_ASSERT_EQUAL_FLOAT(expected.w_4, result.w_4);
}
static void rse_math_test_radians(void)
{
float degrees;
float radians;
float expected_radians;
degrees = 45.0f;
expected_radians = 0.7853982;
radians = rse_math_deg_to_radians(degrees);
TEST_ASSERT_EQUAL_FLOAT(expected_radians, radians);
}
static void rse_math_test_perspective(void)
{
float fovy;
float width;
float height;
float z_near;
float z_far;
struct mat4_t result;
struct mat4_t expected;
fovy = rse_math_deg_to_radians(45.0f);
width = 640.0f;
height = 480.0f;
z_near = 0.1f;
z_far = 20.0f;
expected.x_1 = -1.81066012;
expected.y_1 = 0;
expected.z_1 = 0;
expected.w_1 = 0;
expected.x_2 = 0;
expected.y_2 = 2.41421342;
expected.z_2 = 0;
expected.w_2 = 0;
expected.x_3 = 0;
expected.y_3 = 0;
expected.z_3 = -1.0100503;
expected.w_3 = -1;
expected.x_4 = 0;
expected.y_4 = 0;
expected.z_4 = -0.201005027;
expected.w_4 = 0;
result = rse_math_perspective(fovy, width / height, z_near, z_far);
TEST_ASSERT_EQUAL_FLOAT(expected.x_1, result.x_1);
TEST_ASSERT_EQUAL_FLOAT(expected.y_1, result.y_1);
TEST_ASSERT_EQUAL_FLOAT(expected.z_1, result.z_1);
TEST_ASSERT_EQUAL_FLOAT(expected.w_1, result.w_1);
TEST_ASSERT_EQUAL_FLOAT(expected.x_2, result.x_2);
TEST_ASSERT_EQUAL_FLOAT(expected.y_2, result.y_2);
TEST_ASSERT_EQUAL_FLOAT(expected.z_2, result.z_2);
TEST_ASSERT_EQUAL_FLOAT(expected.w_2, result.w_2);
TEST_ASSERT_EQUAL_FLOAT(expected.x_3, result.x_3);
TEST_ASSERT_EQUAL_FLOAT(expected.y_3, result.y_3);
TEST_ASSERT_EQUAL_FLOAT(expected.z_3, result.z_3);
TEST_ASSERT_EQUAL_FLOAT(expected.w_3, result.w_3);
TEST_ASSERT_EQUAL_FLOAT(expected.x_4, result.x_4);
TEST_ASSERT_EQUAL_FLOAT(expected.y_4, result.y_4);
TEST_ASSERT_EQUAL_FLOAT(expected.z_4, result.z_4);
TEST_ASSERT_EQUAL_FLOAT(expected.w_4, result.w_4);
}
static void rse_math_test_rotation(void)
{
struct mat4_t matrix_to_rotate = rse_math_uniform_mat4();
struct vec3_t rotation_vector = {1.0f, 0.0f, 0.0f};
float rotation = 45.f;
struct mat4_t result = {0.0f};
struct mat4_t expected = rse_math_uniform_mat4();
expected.y_2 = 0.5253220;
expected.y_3 = -0.8509035;
expected.z_2 = 0.8509035;
expected.z_3 = 0.5253220;
result = rse_math_rotate(matrix_to_rotate, rotation, rotation_vector);
TEST_ASSERT_EQUAL_FLOAT(expected.x_1, result.x_1);
TEST_ASSERT_EQUAL_FLOAT(expected.y_1, result.y_1);
TEST_ASSERT_EQUAL_FLOAT(expected.z_1, result.z_1);
TEST_ASSERT_EQUAL_FLOAT(expected.w_1, result.w_1);
TEST_ASSERT_EQUAL_FLOAT(expected.x_2, result.x_2);
TEST_ASSERT_EQUAL_FLOAT(expected.y_2, result.y_2);
TEST_ASSERT_EQUAL_FLOAT(expected.z_2, result.z_2);
TEST_ASSERT_EQUAL_FLOAT(expected.w_2, result.w_2);
TEST_ASSERT_EQUAL_FLOAT(expected.x_3, result.x_3);
TEST_ASSERT_EQUAL_FLOAT(expected.y_3, result.y_3);
TEST_ASSERT_EQUAL_FLOAT(expected.z_3, result.z_3);
TEST_ASSERT_EQUAL_FLOAT(expected.w_3, result.w_3);
TEST_ASSERT_EQUAL_FLOAT(expected.x_4, result.x_4);
TEST_ASSERT_EQUAL_FLOAT(expected.y_4, result.y_4);
TEST_ASSERT_EQUAL_FLOAT(expected.z_4, result.z_4);
TEST_ASSERT_EQUAL_FLOAT(expected.w_4, result.w_4);
}
void rse_math_tests(void)
{
RUN_TEST(test_rse_math_test_vector3_normalize);
RUN_TEST(test_rse_math_test_vector3_cross);
RUN_TEST(test_rse_math_test_look_at);
RUN_TEST(rse_math_test_radians);
RUN_TEST(rse_math_test_perspective);
RUN_TEST(rse_math_test_rotation);
}
+12
View File
@@ -0,0 +1,12 @@
/**
* @file rse_math_tests.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Tests for rse_math module
* @version 0.1
* @date 2024-02-16
*
* @copyright Copyright (c) 2024
*
*/
void rse_math_tests(void);
+16
View File
@@ -0,0 +1,16 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror")
add_library(utilities SHARED)
target_include_directories(utilities PRIVATE
${PROJECT_SOURCE_DIR}
)
target_sources(utilities PRIVATE
src/file_utils.c
src/logger.c
src/task_linux.c
)
target_link_libraries(utilities PRIVATE
pthread)
-7240
View File
File diff suppressed because it is too large. Load diff
+7 -12
View File
@@ -12,7 +12,8 @@
#ifndef COMMONS_H #ifndef COMMONS_H
#define COMMONS_H #define COMMONS_H
#include <SDL3/SDL_log.h> #include "utilities/logger.h"
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@@ -22,13 +23,13 @@
*/ */
#ifndef RSE_TEST #ifndef RSE_TEST
#define rse_malloc(ptr, size) \ #define rse_malloc(ptr, size) \
ptr = calloc(1, size); \ ptr = malloc(size); \
if (ptr == NULL) { \ if (ptr == NULL) { \
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Memory allocation failure"); \ LOGF(_("Memory allocation failed\n")); \
exit(1); \ exit(1); \
} }
#define rse_free(ptr) if (ptr != NULL) free(ptr); #define rse_free(ptr) free(ptr);
#define rse_memcpy(dst, src, size) memcpy(dst, src, size); #define rse_memcpy(dst, src, size) memcpy(dst, src, size);
#define rse_memset(ptr, value, size) memset(ptr, value, size); #define rse_memset(ptr, value, size) memset(ptr, value, size);
@@ -45,10 +46,4 @@
*/ */
typedef uint32_t rse_err_t; typedef uint32_t rse_err_t;
/**
* @brief General type for ID use
*
*/
typedef uint32_t rse_id_t;
#endif /* COMMONS_H */ #endif /* COMMONS_H */
-26
View File
@@ -1,26 +0,0 @@
/**
* @file entity.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Entity from ECS
* @version 0.1
* @date 2025-09-25
*
* @copyright Copyright (c) 2025
*
*/
#ifndef ENTITY_H
#define ENTITY_H
#include <stdint.h>
#define ENTITY_MAX_COUNT 65535
#define COMPONENT_MAX_COUNT 65535
typedef uint32_t entity_t;
entity_t entity_create(void);
void entity_free(entity_t entity);
#endif // !ENTITY_H
-2
View File
@@ -26,8 +26,6 @@ enum common_error_t
RSE_ERROR_NULL_POINTER, RSE_ERROR_NULL_POINTER,
RSE_ERROR_ALREADY_INITIALIZED, RSE_ERROR_ALREADY_INITIALIZED,
RSE_COMMON_ERROR_MALLLOC_FAILED, RSE_COMMON_ERROR_MALLLOC_FAILED,
RSE_ERROR_INVALID_PARAM,
RSE_ERROR_INTERNAL_ERROR,
}; };
#define ERRORS_COMMON_H #define ERRORS_COMMON_H
+2 -10
View File
@@ -2,10 +2,6 @@
#define FILE_UTILS_H #define FILE_UTILS_H
#include <stddef.h> #include <stddef.h>
#include "commons.h"
#include "cgltf.h"
#define MAX_PATH_LENGTH (4096U)
/** /**
* @brief Reads file and puts its content into char buffer. If NULL is provided as buffer, than just bytes_count * @brief Reads file and puts its content into char buffer. If NULL is provided as buffer, than just bytes_count
@@ -15,13 +11,9 @@
* @param bytes_count Returned bytes count * @param bytes_count Returned bytes count
* @param buffer Returned filled buffer * @param buffer Returned filled buffer
*/ */
rse_err_t file_read_bytes(const char* file_path, size_t* bytes_count, unsigned char* buffer); void read_file(const char* file_path, size_t* bytes_count, char* buffer);
rse_err_t file_write(const char* file_name, size_t bytes_count, char* buffer); void write_to_file(const char* file_name, size_t bytes_count, char* buffer);
rse_err_t file_append_full_path(char* file_path, size_t max_buffer_size);
rse_err_t file_load_gltf(const char* file_name, cgltf_data** data);
#endif /* FILE_UTILS_H */ #endif /* FILE_UTILS_H */
+128
View File
@@ -0,0 +1,128 @@
/**
* @file linked_list.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief
* @version 0.1
* @date 2024-05-01
*
* @copyright Copyright (c) 2024
*
*/
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include "utilities/commons.h"
/**
* @brief Define a linked list structure
*
* @param name Name of the linked list structure
* @param type Type of the data stored in the linked list
* @return struct name { type data; struct name *next; }
* @note This macro defines a linked list structure with a given name and data type
*/
#define RSE_LINKED_LIST_DEFINE(name, type) \
struct name { \
type data; \
struct name *next; \
}
#define RSE_LINKED_LIST_PUSH_FRONT(list, value) \
typeof(list) new_node = NULL; \
rse_malloc(new_node, sizeof(*(list))); \
new_node->data = value; \
new_node->next = list; \
list = new_node
#define RSE_LINKED_LIST_PUSH_BACK(list, value) \
typeof(list) new_node = NULL; \
rse_malloc(new_node, sizeof(*(list))); \
new_node->data = value; \
new_node->next = NULL; \
if (list == NULL) { \
list = new_node; \
} else { \
typeof(list) current = list; \
while (current->next != NULL) { \
current = current->next; \
} \
current->next = new_node; \
}
#define RSE_LINKED_LIST_POP_FRONT(list) \
if (list != NULL) { \
typeof(list) temp = list; \
list = list->next; \
rse_free(temp); \
}
#define RSE_LINKED_LIST_POP_BACK(list) \
if (list != NULL) { \
if (list->next == NULL) { \
rse_free(list); \
list = NULL; \
} else { \
typeof(list) *current = list; \
while (current->next->next != NULL) { \
current = current->next; \
} \
rse_free(current->next); \
current->next = NULL; \
} \
}
#define RSE_LINKED_LIST_FRONT(list) \
list->data
#define RSE_LINKED_LIST_BACK(list) \
if (list != NULL) { \
typeof(list) *current = list; \
while (current->next != NULL) { \
current = current->next; \
} \
current->data; \
}
#define RSE_LINKED_LIST_IS_EMPTY(list) \
(list == NULL)
#define RSE_LINKED_LIST_SIZE(list) \
({ \
int size = 0; \
typeof(list) current = list; \
while (current != NULL) { \
size++; \
current = current->next; \
} \
size; \
})
#define RSE_LINKED_LIST_CLEAR(list) \
while (list != NULL) { \
typeof(list) temp = list; \
list = list->next; \
rse_free(temp); \
}
#define RSE_LINKED_LIST_FOREACH(list, i) \
for (typeof(list) i = list; i != NULL; i = i->next)
#define RSE_LINKED_LIST_FOREACH_REVERSE(list, i) \
for (typeof(list) i = list; i != NULL; i = i->next)
#define RSE_LINKED_LIST_TO_ARRAY(list, array) \
({ \
int size = RSE_LINKED_LIST_SIZE(list); \
rse_malloc(array, size * sizeof(*(list))); \
int i = 0; \
RSE_LINKED_LIST_FOREACH(list, current) { \
array[i++] = current->data; \
} \
size; \
})
#endif // LINKED_LIST_H
+31
View File
@@ -0,0 +1,31 @@
/**
* @file locale_common.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Error messages for Vulkan initializers
* @version 0.1
* @date 2022-05-13
*
* @copyright Copyright (c) 2022
*
*/
#ifndef LOCALE_COMMON_H
#define LOCALE_COMMON_H
#include "localization.h"
#define common_messages common_messages_nolocale[SELECTED_LANGUAGE]
enum common_errors_t {
COMMON_ERROR_MEMORY_ALLOCATION_FAILED,
COMMON_ERROR_LAST_MESSAGE
};
static const char common_messages_nolocale[LAST_LANGUAGE][COMMON_ERROR_LAST_MESSAGE][MAX_MESSAGE_LENGHT] = {
{/* EN_US */
/* COMMON_MEMORY_ALLOCATION_FAILED */
"Unable to allocate memory"
}
};
#endif /* LOCALE_COMMON_H */
+20
View File
@@ -0,0 +1,20 @@
/**
* @file localization.h
* @author Piotr Krygier (everyonencancode@gmail.com)
* @brief Simple localization header. Gettext is used for localization
* @version 0.1
* @date 2022-02-15
*
* @copyright Copyright (c) 2022
*
*/
#ifndef LOCALIZATION_H
#define LOCALIZATION_H
#include <libintl.h>
/* Enable localization */
#define _(STRING) gettext(STRING)
#endif /* LOCALIZATION_H */
+45
View File
@@ -0,0 +1,45 @@
/**
* @file logger.h
* @author Piotr Krygier (everyonencancode@gmail.com)
* @brief Logging interfaces
* @version 0.1
* @date 2022-02-11
*
* @copyright Copyright (c) 2022
*
*/
#ifndef LOGGER_H
#define LOGGER_H
#include <stdio.h>
#include <string.h>
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define LOGGER(log_level, ...) _log_log(log_level, __FILENAME__, __LINE__, __VA_ARGS__)
#define LOGT(...) LOGGER(LOGLEVEL_TRACE, __VA_ARGS__)
#define LOGD(...) LOGGER(LOGLEVEL_DEBUG, __VA_ARGS__)
#define LOGI(...) LOGGER(LOGLEVEL_INFO, __VA_ARGS__)
#define LOGW(...) LOGGER(LOGLEVEL_WARNING, __VA_ARGS__)
#define LOGE(...) LOGGER(LOGLEVEL_ERROR, __VA_ARGS__)
#define LOGF(...) LOGGER(LOGLEVEL_FATAL, __VA_ARGS__)
/**
* @brief Log level. Is not an enum class, because level is being compared to enable prints
*
*/
enum log_level_t
{
LOGLEVEL_TRACE,
LOGLEVEL_DEBUG,
LOGLEVEL_INFO,
LOGLEVEL_WARNING,
LOGLEVEL_ERROR,
LOGLEVEL_FATAL,
LOGLEVEL_NONE
};
void _log_log(unsigned int level, const char* file, int line, const char* fmt, ...);
#endif /* LOGGER_H */
-25
View File
@@ -1,25 +0,0 @@
rse_utilities_srcs = [
'src/file_utils.c',
'src/time_utils.c'
]
cc = meson.get_compiler('c')
sdl3_dep = dependency('sdl3',
version : '>=3.4.0')
stb = dependency('stb',
fallback: ['stb', 'stb_dep'])
m_dep = cc.find_library('m')
rse_utilities_lib = shared_library(
'rse_utilities',
rse_utilities_srcs,
include_directories : ['.'],
dependencies : [
sdl3_dep,
stb,
m_dep,
],
install : true
)
-34
View File
@@ -1,34 +0,0 @@
/**
* @file entity.c
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Entity from ECS
* @version 0.1
* @date 2025-09-25
*
* @copyright Copyright (c) 2025
*
*/
#include "utilities/entity.h"
#include <stdint.h>
static entity_t g_free_list[ENTITY_MAX_COUNT];
static uint32_t g_free_count = 0U;
static uint32_t g_living_count = 0U;
entity_t entity_create(void)
{
entity_t e = 0U;
if (g_free_count > 0) {
e = g_free_list[g_free_count--];
} else {
e = g_living_count++;
}
return e;
}
void entity_free(entity_t entity)
{
g_free_list[g_free_count++] = entity;
}
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more