Compare commits

..
22 Commits
Author SHA1 Message Date
everyonecancode 48e3feb7fa Replace fences with timeline semaphores
Replaced fences with timeline semaphores. I have no idea, if I did this
correctly, but it seems to be working fine. This might come back later
to bite me in my posterior.

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode.xyz>
2026-09-30 09:41:58 +02:00
everyonecancode eff8c43224 Fix time and subprojects
Moved SDL3 to subprojects.
Fixed time calculation.

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode.xyz>
2026-09-29 11:59:42 +02:00
Piotr Krygier ca1477b500 Add subprojects
Instead of relying on host system, some dependencies had been added using meson wrap DB and custom meson wrappers.

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-06-08 20:26:37 +02:00
Piotr Krygier ceb9e365de Load texture from glb
Add loading image from glb file.
2026-03-20 14:22:17 +01:00
Piotr Krygier 4c706982f1 Fix memory and style issues 2026-03-20 11:40:09 +01:00
Piotr Krygier c531f5fb55 Fixed issue with avocado's seed
Turn's out there rendering should be done clockwise, as it was originally.
2026-03-19 22:00:12 +01:00
Piotr Krygier 99fb36d9fd Add basic loader for gltf
Added loading of avocado. Something is still wrong though,
becaus I cannot see the seed. But basic functionality is there.
2026-03-19 21:00:39 +01:00
Piotr Krygier 984a56a8e8 Fix image size and semaphore issues
There was an issue with image size for depth
and color attachment, which has been fixed.
Also, finally found and issue with semaphore indexing

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-03-14 10:01:24 +01:00
Piotr Krygier dd70f5e5b0 Add gltf loader
Added MIT licensed external library for loading gltf
files

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-03-13 11:49:26 +01:00
Piotr Krygier c6825caa69 Implement event handler
Added event handler implementation.
Moved enginge specific implementation outside graphics module

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-03-04 11:04:53 +01:00
Piotr Krygier bbbe69d73c Prepare for event system
Moved stuff around.
Create an entry point for RSE.
Created base for events module

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-03-03 11:26:43 +01:00
Piotr Krygier 7d6156be01 Add movement relative to time
Previously movement was based on frames rather than
time. I added time_get_dt() function for getting time
since last frame and changed rotation speed to use this
function.

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-24 13:38:04 +01:00
Piotr Krygier 415d4394c2 Add FPS counter
Added console FPS counter. Thise required me to create
some time functions and remove FPS cap from Vulkan

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-11 18:53:48 +01:00
Piotr Krygier de597ebc5d Replace uniform with storage buffer for instances
Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 19:07:40 +01:00
Piotr Krygier ff527767b9 Add scene time based rotation
Added time based scene rotation as a proof of concept.
Based on this implementation, I can not only move
scene using external data (like mouse, in the future)
but also I can use provided functionality to get high resolution
timer for future use

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 15:50:36 +01:00
Piotr Krygier b8fc36910f Replace MVP uniform buffer with push constants
UBO should be used for large data. MVP should be
push constants, since it's a small amount of data
updated every frame

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 14:34:11 +01:00
Piotr Krygier b8509a9a68 Add push constants
Added functions for adding push constants to pipeline

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 13:09:18 +01:00
Piotr Krygier 954876549d Create typedef for IDs
There was a code, where id for something was u32, and
u16 for other things. I have created a typedef to fix
that

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 10:25:01 +01:00
Piotr Krygier 55e0ee261e Remove stack implementation
There is no need for stack implementation to be used here.
Simple C operations are enough for now

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 10:25:00 +01:00
Piotr Krygier 3dcf7fec56 Fix naming conventions
Fixed some functions names that were not following my convention

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 10:24:59 +01:00
Piotr Krygier b7a472fe45 Descriptor update
Updated descriptor set build to use function parameters instead of
global vars

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 10:24:50 +01:00
Piotr Krygier 493afb05e6 Initial commit
This is working repository now. I had to clean this up due to
my f_ups, that made this simple repo around 200MB large.

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 10:24:45 +01:00
104 changed files with 10422 additions and 162413 deletions

No files matched your search

+8
View File
@@ -30,3 +30,11 @@ ColumnLimit: 120
# Pointer star next to type, not variable name
DerivePointerAlignment: false
PointerAlignment: Left
# Function parameters Alignment
AlignAfterOpenBracket: Align
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
BinPackArguments: false
BinPackParameters: OnePerLine
-1
View File
@@ -8,6 +8,5 @@ CompileFlags:
"-I./",
"-I./graphics",
"-I./utilites",
"-I./third_party/include",
]
+5
View File
@@ -50,4 +50,9 @@ 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.
-15
View File
@@ -1,15 +0,0 @@
cmake_minimum_required(VERSION 3.31)
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_C_FLAGS "${CMAKE_C_FLAGS} -g")
add_subdirectory(red_scarf_engine)
add_subdirectory(utilities)
add_subdirectory(graphics)
# Enable later
# add_subdirectory(tests)
-9
View File
@@ -1,9 +0,0 @@
{
"version": 4,
"vendor": {
"conan": {}
},
"include": [
"build/CMakePresets.json"
]
}
Binary file not shown.
-10
View File
@@ -2,13 +2,3 @@
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.
-14
View File
@@ -1,14 +0,0 @@
[requires]
vulkan-headers/1.4.309.0
vulkan-loader/1.4.309.0
libgettext/0.22
freetype/2.13.3
stb/cci.20240531
sdl/3.2.14
[generators]
CMakeDeps
CMakeToolchain
[options]
sdl/*:wayland=False
+18
View File
@@ -0,0 +1,18 @@
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
@@ -0,0 +1,26 @@
/**
* @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
@@ -0,0 +1,44 @@
/**
* @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
@@ -0,0 +1,99 @@
/**
* @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;
}
-47
View File
@@ -1,47 +0,0 @@
find_package(VulkanHeaders REQUIRED)
find_package(VulkanLoader REQUIRED)
# find_package(vulkan-validationlayers)
find_package(freetype REQUIRED)
find_package(stb REQUIRED)
find_package(SDL3 REQUIRED)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -g")
add_library(graphics SHARED)
target_include_directories(graphics PRIVATE
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/third_party/include/
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_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/rse_graphics.c
src/math.c
src/mesh_controller.c
src/vulkan_buffers.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-headers::vulkan-headers
Vulkan::Loader
# vulkan-validationlayers::vulkan-validationlayers
Freetype::Freetype
stb::stb
sdl::sdl
)
+58
View File
@@ -0,0 +1,58 @@
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('.')
)
+8 -5
View File
@@ -1,10 +1,9 @@
#ifndef RSE_GRAPHICS_H
#define RSE_GRAPHICS_H
#include "src/graphics_context.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
* added
@@ -12,20 +11,24 @@ struct rse_graphics_context_t;
* @param context Graphics context handle. Must be NULL!
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
rse_err_t rse_graphics_init(struct rse_graphics_context_t** context);
rse_err_t rse_graphics_init(struct graphics_context_t* context);
/**
* @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
*/
int rse_graphics_run(void* arg);
rse_err_t rse_graphics_run(struct 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
*
*/
void rse_graphics_test_function(struct rse_graphics_context_t* context);
rse_err_t rse_graphics_test_function(struct graphics_context_t* context);
void rse_graphics_deinit(struct graphics_context_t* context);
#endif /* RSE_GRAPHICS_H */
+4 -17
View File
@@ -1,21 +1,8 @@
#version 450
/* Each time texture count is changed this value also need to change. I have to fix it somehow. */
#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;
layout(set = 0, binding = 0) uniform sampler2D overlay_tex;
layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 out_color;
void main() {
/* 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);
out_color = texture(overlay_tex, uv);
}
+13
View File
@@ -0,0 +1,13 @@
#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);
}
+24 -16
View File
@@ -1,20 +1,27 @@
#version 450
layout(binding = 0) uniform UniformBufferObject {
layout(push_constant) uniform pc {
mat4 model;
mat4 view;
mat4 proj;
} 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 = 1) in vec3 inColor;
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 = 1) out vec2 fragTexCoord;
layout(location = 2) out uint outInstanceTextureId;
@@ -22,28 +29,29 @@ layout(location = 2) out uint outInstanceTextureId;
void main()
{
InstanceData instance = instances[gl_InstanceIndex];
mat4 gRotMat;
mat3 mx, my, mz;
// rotate around x
float s = sin(instanceRotation.x + 0);
float c = cos(instanceRotation.x + 0);
float s = sin(instance.rot.x + 0);
float c = cos(instance.rot.x + 0);
mx[0] = vec3(1, 0.0, 0.0);
mx[1] = vec3(0, c, -s);
mx[2] = vec3(0.0, s, c);
// rotate around y
s = sin(instanceRotation.y + 0);
c = cos(instanceRotation.y + 0);
s = sin(instance.rot.y + 0);
c = cos(instance.rot.y + 0);
my[0] = vec3(c, 0.0, s);
my[1] = vec3(0.0, 1.0, 0.0);
my[2] = vec3(-s, 0.0, c);
// rot around z
s = sin(instanceRotation.z + 0);
c = cos(instanceRotation.z + 0);
s = sin(instance.rot.z + 0);
c = cos(instance.rot.z + 0);
mz[0] = vec3(c, -s, 0.0);
mz[1] = vec3(s, c, 0.0);
@@ -51,19 +59,19 @@ void main()
mat3 rotMat = mz * my * mx;
s = sin(instanceRotation.y + 0);
c = cos(instanceRotation.y + 0);
s = sin(instance.rot.y + 0);
c = cos(instance.rot.y + 0);
gRotMat[0] = vec4(c, 0.0, s, 0.0);
gRotMat[1] = vec4(0.0, 1.0, 0.0, 0.0);
gRotMat[2] = vec4(-s, 0.0, c, 0.0);
gRotMat[3] = vec4(0.0, 0.0, 0.0, 1.0);
vec4 locPos = vec4(inPosition.xyz * rotMat, 1.0);
vec4 pos = vec4((locPos.xyz * instanceScale) + instancePosition, 1.0);
vec4 pos = vec4((locPos.xyz * instance.scale) + instance.pos, 1.0);
gl_Position = ubo.proj * ubo.view * ubo.model * pos;
fragColor = inColor;
fragTexCoord = inTexCoord;
outInstanceTextureId = instanceTextureId;
outInstanceTextureId = instance.texture_id;
}
+105 -351
View File
@@ -11,6 +11,7 @@
#include "descriptor_builder.h"
#include <SDL3/SDL_log.h>
#include <stddef.h>
#include <stdint.h>
@@ -18,411 +19,160 @@
#include "src/vulkan_commons.h"
#include "utilities/commons.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_errors.h"
RSE_LINKED_LIST_DEFINE(descriptor_set_layout_list_t, VkDescriptorSetLayoutBinding);
#define POOL_MAX_SETS (32U)
struct descriptor_t
rse_err_t descriptor_pool_add_type(struct graphics_context_t* context, VkDescriptorType type, uint32_t count)
{
uint32_t binding;
VkDescriptorType type;
uint32_t descriptor_count;
VkShaderStageFlags shader_stages;
VkSampler* immutable_sampler;
};
context->descriptor_data.pool_sizes[context->descriptor_data.pool_sizes_count].type = type;
context->descriptor_data.pool_sizes[context->descriptor_data.pool_sizes_count].descriptorCount = count;
RSE_LINKED_LIST_DEFINE(descriptor_set_list_t, struct descriptor_t);
context->descriptor_data.pool_sizes_count++;
return RSE_ERROR_NO_ERROR;
}
struct descriptor_set_handle_t
rse_err_t descriptor_pool_initialize(struct graphics_context_t* context)
{
VkDescriptorSet descriptor_sets;
VkDescriptorPool descriptor_pool;
VkDescriptorSetLayout descriptor_sets_layout;
struct descriptor_set_list_t* descriptor_set_list;
};
VkDescriptorPoolCreateInfo create_info = {0};
/* FIXME: When new descriptors are introduced to Vulkan, this enum has to be
* updated */
enum descriptor_type_id_t
{
DESCRIPTOR_TYPE_SAMPLER_ID,
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,
};
create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
create_info.pNext = NULL;
create_info.flags = 0U;
create_info.maxSets = POOL_MAX_SETS;
create_info.poolSizeCount = context->descriptor_data.pool_sizes_count;
create_info.pPoolSizes = context->descriptor_data.pool_sizes;
rse_err_t descriptor_initialize_handle(struct descriptor_set_handle_t **descriptor_set_handle)
{
if (NULL != *descriptor_set_handle) {
LOGE(_("Desciptor already initialized\n"));
return RSE_ERROR_INVALID_PARAM;
if (vkCreateDescriptorPool(context->vulkan_handles.device,
&create_info,
NULL,
&context->descriptor_data.descriptor_pool) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create descriptor pool");
return RSE_ERROR_INTERNAL_ERROR;
}
rse_malloc(*descriptor_set_handle, sizeof(struct descriptor_set_handle_t));
(*descriptor_set_handle)->descriptor_set_list = NULL;
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_free_handle(struct descriptor_set_handle_t **descriptor_set_handle)
rse_err_t descriptor_create_new_set(struct graphics_context_t* context, rse_id_t* set_id)
{
if (NULL == *descriptor_set_handle) {
LOGE(_("Provided NULL handle\n"));
return RSE_ERROR_INVALID_PARAM;
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;
}
RSE_LINKED_LIST_CLEAR((*descriptor_set_handle)->descriptor_set_list);
rse_free(*descriptor_set_handle);
*set_id = context->descriptor_data.descriptor_sets_count++;
return RSE_ERROR_NO_ERROR;
}
*descriptor_set_handle = NULL;
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_add_set(struct descriptor_set_handle_t* descriptor_set_handle, uint32_t binding, VkDescriptorType type,
uint32_t descriptor_count, VkShaderStageFlags shader_stages, VkSampler* sampler)
rse_err_t descriptor_set_finish(struct graphics_context_t* context,
struct descriptor_set_layout_bindings_t* layout_bindings,
rse_id_t set_id)
{
struct descriptor_t descriptor_sets = {.binding = binding,
.type = type,
.descriptor_count = descriptor_count,
.shader_stages = shader_stages,
.immutable_sampler = sampler};
VkDescriptorSetLayoutCreateInfo create_info = {0};
RSE_LINKED_LIST_PUSH_BACK(descriptor_set_handle->descriptor_set_list, descriptor_sets);
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 descriptors_build(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle)
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};
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) {
LOGF(_("Provided NULL context\n"));
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided NULL context");
return RSE_ERROR_NULL_POINTER;
}
if (NULL == descriptor_set_handle) {
LOGF(_("Descriptor set handle is null.\n"));
return RSE_ERROR_NULL_POINTER;
}
if (RSE_LINKED_LIST_IS_EMPTY(descriptor_set_handle->descriptor_set_list)) {
LOGF(_("Provided empty descriptors list\n"));
return RSE_ERROR_NULL_POINTER;
}
iterator = 0U;
RSE_LINKED_LIST_FOREACH(descriptor_set_handle->descriptor_set_list, descriptor)
{
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(descriptor_set_handle->descriptor_set_list, 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;
if (context->descriptor_data.descriptor_pool == VK_NULL_HANDLE) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU,
"Requested descriptor build, but descriptor pool has not been initialized");
}
/* Allocate descriptors from the pool */
alloc_info.descriptorPool = descriptor_pool;
alloc_info.descriptorSetCount =
pool_max_sets; /* TODO: Always allocating maximum number of sets? This might get weird */
alloc_info.pSetLayouts = &set_layout;
alloc_info.descriptorPool = context->descriptor_data.descriptor_pool;
alloc_info.descriptorSetCount = context->descriptor_data.descriptor_sets_count;
alloc_info.pSetLayouts = context->descriptor_data.set_layouts;
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
if (VK_SUCCESS != vkAllocateDescriptorSets(context->device, &alloc_info, &descriptor_sets)) {
LOGF(_("Failed to create descriptor sets\n"));
return VULKAN_ERROR_DESCRIPTOR_SET_CREATION_FAILED;
if (VK_SUCCESS != vkAllocateDescriptorSets(context->vulkan_handles.device,
&alloc_info,
context->descriptor_data.descriptor_sets)) {
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;
context->descriptor_sets = descriptor_set_handle;
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_get_layout(struct descriptor_set_handle_t* descriptor_set_handle,
VkDescriptorSetLayout* descriptor_sets_layout)
rse_err_t descriptor_attach_images(struct graphics_context_t* context,
rse_id_t descriptor_set_id,
rse_id_t* texture_ids,
size_t images_count,
VkSampler sampler,
uint32_t binding)
{
if (NULL == descriptor_set_handle->descriptor_sets_layout) {
LOGF(_("Requested descriptor set layout is NULL\n"));
return RSE_ERROR_NULL_POINTER;
}
*descriptor_sets_layout = descriptor_set_handle->descriptor_sets_layout;
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_get_vkhandle(struct descriptor_set_handle_t* descriptor_set_handle, VkDescriptorSet* descriptor_set)
{
if (NULL == descriptor_set_handle) {
LOGF(_("Provided handle is NULL\n"));
return RSE_ERROR_NULL_POINTER;
}
*descriptor_set = descriptor_set_handle->descriptor_sets;
return RSE_ERROR_NO_ERROR;
}
rse_err_t descriptor_attach_image(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle, uint16_t texture_id, VkSampler sampler, uint32_t binding)
{
VkDescriptorImageInfo image_info = {0};
size_t iter = 0U;
VkDescriptorImageInfo* image_infos = NULL;
VkWriteDescriptorSet write_set = {0};
image_info.sampler = sampler;
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_info.imageView = context->texture_images[texture_id].image_view;
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_info;
write_set.dstSet = descriptor_set_handle->descriptor_sets;
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 = 1;
write_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; // TODO: Maybe not only samplers?
write_set.descriptorCount = images_count;
vkUpdateDescriptorSets(context->device, 1U, &write_set, 0, NULL);
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, struct descriptor_set_handle_t* descriptor_set_handle, struct vulkan_buffer_t* buffer, uint32_t binding, VkDescriptorType type)
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};
@@ -433,19 +183,23 @@ rse_err_t descriptor_attach_buffer(struct graphics_context_t* context, struct de
write_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_set.pBufferInfo = &buffer_info;
write_set.dstSet = descriptor_set_handle->descriptor_sets;
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->device, 1U, &write_set, 0, NULL);
vkUpdateDescriptorSets(context->vulkan_handles.device, 1U, &write_set, 0, NULL);
return RSE_ERROR_NO_ERROR;
}
void destroy_descriptors(struct graphics_context_t* context)
{
vkDestroyDescriptorSetLayout(context->device, context->descriptor_sets->descriptor_sets_layout, NULL);
vkDestroyDescriptorPool(context->device, context->descriptor_sets->descriptor_pool, NULL);
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);
}
+33 -45
View File
@@ -20,21 +20,23 @@
struct descriptor_set_handle_t;
struct descriptor_set_list_t;
/**
* @brief Initialize new descriptor set handle
*
* @param descriptor_set_handle Handle to initialize
* #return RSE_ERROR_NO_ERROR on success
*/
rse_err_t descriptor_initialize_handle(struct descriptor_set_handle_t **descriptor_set_handle);
rse_err_t descriptor_pool_add_type(struct graphics_context_t* context, VkDescriptorType type, uint32_t count);
/**
* @brief Destroy descritptor set handle
*
* @param descriptor_set_handle Handle to destroy
* @return RSE_ERROR_NO_ERROR on succes
*/
rse_err_t descriptor_free_handle(struct descriptor_set_handle_t **descriptor_set_handle);
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
@@ -44,41 +46,27 @@ rse_err_t descriptor_free_handle(struct descriptor_set_handle_t **descriptor_set
* @param type Type of descriptors
* @param descriptor_count Number of descriptors in this set
* @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
*/
rse_err_t descriptor_add_set(struct descriptor_set_handle_t* descriptor_set_handle, uint32_t binding, VkDescriptorType type,
uint32_t descriptor_count, VkShaderStageFlags shader_stages, VkSampler* sampler);
rse_err_t descriptor_add_set(struct descriptor_set_handle_t* descriptor_set_handle,
uint32_t binding,
VkDescriptorType type,
uint32_t descriptor_count,
VkShaderStageFlags shader_stages);
/**
* @brief Allocate descriptor sets from provided list. The data will be stored in opaque descriptor handle
*
* @param context Graphics context
* @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 descriptors_build(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle);
rse_err_t descriptor_attach_images(struct graphics_context_t* context,
rse_id_t descriptor_set_id,
rse_id_t* texture_ids,
size_t images_count,
VkSampler sampler,
uint32_t binding);
/**
* @brief Return descriptor set layouts from descritptor handle
*
* @param descriptor_set_handle Handle for descriptor set
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
rse_err_t descriptor_get_layout(struct descriptor_set_handle_t* descriptor_set_handle,
VkDescriptorSetLayout* descritptor_sets_layout);
/**
* @brief Get VkDescriptorSet from descriptor handle
*
* @param descriptor_set_handle_t Handle to get VkDescriptorSet from
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
rse_err_t descriptor_get_vkhandle(struct descriptor_set_handle_t* descriptor_set_handle, VkDescriptorSet* descriptor_set);
rse_err_t descriptor_attach_buffer(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle, struct vulkan_buffer_t* buffer, uint32_t binding, VkDescriptorType type);
rse_err_t descriptor_attach_image(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle, uint16_t texture_id, VkSampler sampler, uint32_t binding);
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);
void destroy_descriptors(struct graphics_context_t* context);
#endif // !DESCRIPTOR_BUILDER_H
-26
View File
@@ -1,26 +0,0 @@
/**
* @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
@@ -0,0 +1,229 @@
/**
* @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
@@ -0,0 +1,23 @@
/**
* @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 */
+225 -62
View File
@@ -1,6 +1,6 @@
/**
* @file graphics_context.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Graphics context for Red Scarf Engine
* @version 0.1
* @date 2024-05-22
@@ -12,18 +12,24 @@
#ifndef GRAPHICS_CONTEXT_H
#define GRAPHICS_CONTEXT_H
#include "SDL3/SDL.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_render.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "vma/vk_mem_alloc.h"
#include <vulkan/vulkan.h>
#include "utilities/linked_list.h"
#include "utilities/entity.h"
#include "utilities/vector.h"
#include "vk_mem_alloc.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_commons.h"
#define MAX_MESH_NUMBER 2
#define MAX_INSTANCE_NUMBER 256
#define MAX_MESH_NUMBER 256U
/* FIXME: This is the total number of instances * meshes. It shouldn't be so low */
#define MAX_INSTANCE_NUMBER (MAX_MESH_NUMBER * 256U)
#define SWAPCHAIN_IMAGE_COUNT (3U)
/**
* @brief Maximum number of textures that can be loaded
@@ -31,11 +37,37 @@
*/
#define RSE_MAX_IMAGE_COUNT 256
#define MAX_PIPELINE_COUNT 16
#define MAX_VULKAN_BUFFERS_COUNT (32U)
RSE_LINKED_LIST_DEFINE(shader_module_list_t, VkShaderModule);
RSE_LINKED_LIST_DEFINE(vulkan_buffer_list_t, struct vulkan_buffer_t);
RSE_LINKED_LIST_DEFINE(descriptor_sets_list_t, VkDescriptorSet);
#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)
#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.
@@ -47,6 +79,11 @@ struct buffer_data_t
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;
*
@@ -61,16 +98,7 @@ struct vulkan_image_t
VmaAllocationInfo allocation_info;
};
/**
* @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;
};
RSE_VECTOR_DEFINE(instance_vector_t, struct instance_data_t);
/**
* @brief Mesh data, with unique id and dynamic arrays holding vertices and indices.
* One mesh can have multiple instances.
@@ -78,62 +106,197 @@ struct internal_instance_data_t
*/
struct mesh_t
{
uint8_t is_mesh_id_taken;
uint32_t unique_id;
uint8_t instance_count;
struct buffer_data_t vertex_data;
struct buffer_data_t index_data;
struct internal_instance_data_t instance_data[MAX_INSTANCE_NUMBER];
size_t vertex_count;
bool is_dirty;
size_t entities_count;
entity_t* entities; /* Which entities are connected to this mesh */
size_t index_count;
size_t instances_count;
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
{
rse_id_t descriptor_set_ids[MAX_PIPELINE_COUNT];
size_t pipelines_count;
size_t pipeline_layouts_count;
VkPipeline pipelines[MAX_PIPELINE_COUNT];
VkPipelineLayout pipeline_layout;
VkPipelineLayout pipeline_layouts[MAX_PIPELINE_COUNT];
};
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
{
SDL_Window* window_handle;
VkSampler sampler;
VkDevice device;
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;
VkImage* swapchain_images;
VkQueue present_queue;
VkSemaphore image_available_semaphores[SWAP_BUFFER_COUNT];
VkSemaphore render_finished_semaphores[SWAP_BUFFER_COUNT];
VkFence in_flight_fences[SWAP_BUFFER_COUNT];
struct pipeline_internal_t pipelines_data;
struct descriptor_set_handle_t* descriptor_sets;
VkDebugUtilsMessengerEXT debug_messenger;
bool is_framebuffer_resized; // TODO: Consider moving this to heap
bool is_framebuffer_resized;
uint32_t queue_family_indices[QUEUE_FAMILY_INDEX_LAST_INDEX];
uint32_t current_frame;
uint32_t queue_family_indices[1];
/* 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_buffer_list_t* uniform_buffers;
uint32_t swapchain_images_count;
uint64_t frame_finished_idx;
SDL_Window* window_handle;
struct mvp_data_t mvp_data;
struct renderer_data_t renderer_data;
struct debug_overlay_t debug_overlay;
struct vulkan_handles vulkan_handles;
struct swapchain_data_t swapchain_data;
struct descriptor_data_t descriptor_data;
struct pipeline_internal_t pipelines_data;
struct render_target_t render_targets[RENDER_TARGET_COUNT];
struct vulkan_buffers_t uniform_buffers;
struct vulkan_image_t texture_images[RSE_MAX_IMAGE_COUNT];
struct vulkan_image_t depth_image;
struct vulkan_image_t color_image;
struct shader_module_list_t* shader_modules;
struct mesh_t meshes[MAX_MESH_NUMBER];
struct shader_modules_t shader_modules;
struct mesh_data_t mesh_data;
struct fonts_data_t fonts_data;
};
#endif /* GRAPHICS_CONTEXT_H */
-148
View File
@@ -1,148 +0,0 @@
#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
@@ -1,122 +0,0 @@
/**
* @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 */
+195 -77
View File
@@ -1,91 +1,209 @@
#include "mesh_controller.h"
#include "vulkan_buffers.h"
#include "vulkan_errors.h"
#include "utilities/errors_common.h"
#include "utilities/logger.h"
#include "utilities/localization.h"
/**
* @file mesh_controller.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Graphics context for Red Scarf Engine
* @version 0.1
* @date 2025-09-23
*
* @copyright Copyright (c) 2025
*
*/
#include <SDL3/SDL_log.h>
#include <stdbool.h>
#include <stddef.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"
#define MESH_ID_FREE 0
#define MESH_ID_TAKEN 1
uint16_t create_mesh(struct graphics_context_t* context, struct vertex_t* vertices,
uint16_t* indices,
size_t vertices_count,
size_t indices_count)
rse_err_t mesh_create(struct graphics_context_t* context,
struct vertex_t* vertices,
uint16_t* indices,
size_t vertices_count,
size_t indices_count,
rse_id_t* mesh_id)
{
size_t iter = 0U;
rse_err_t status = RSE_ERROR_NO_ERROR;
for(iter = 0U; iter < MAX_MESH_NUMBER; ++iter) {
if(context->meshes[iter].is_mesh_id_taken == MESH_ID_FREE) {
break;
if (mesh_id == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided mesh id handle is NULL");
return RSE_ERROR_NULL_POINTER;
}
*mesh_id = context->mesh_data.mesh_count;
if (context->mesh_data.mesh_count > 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->mesh_data.mesh_count++;
return status;
}
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)
{
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(iter >= MAX_MESH_NUMBER) {
LOGE(_("Could not allocate new mesh, reached maximum.\n"));
return MAX_MESH_NUMBER;
}
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;
context->meshes[iter].is_mesh_id_taken = MESH_ID_TAKEN;
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);
add_vertices(context, iter, vertices, indices, &context->meshes[iter].vertex_data, &context->meshes[iter].index_data);
return iter;
}
rse_err_t create_mesh_instance(struct graphics_context_t* context, uint16_t mesh_id,
struct instance_data_t instance_data)
{
size_t iter = 0U;
for(iter = 0U; iter < MAX_INSTANCE_NUMBER; ++iter) {
if(context->meshes[mesh_id].instance_data[iter].instance_taken == MESH_ID_FREE) {
break;
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));
}
if(iter >= MAX_INSTANCE_NUMBER) {
LOGE(_("Could not allocate new mesh, reached maximum.\n"));
return VULKAN_ERROR_MAX_INSTANCE_COUNT_REACHED;
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)
{
if (context->mesh_data.mesh_bucket[entity].instances_count >= MAX_INSTANCE_NUMBER) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Could not allocate new mesh, reached maximum.");
return RSE_ERROR_INTERNAL_ERROR;
}
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;
return mesh_add_instance(context, entity, &instance_data);
}
+14 -18
View File
@@ -12,6 +12,7 @@
#ifndef MESH_CONTROLLER_H
#define MESH_CONTROLLER_H
#include "utilities/entity.h"
#include "vulkan_commons.h"
#include "graphics_context.h"
#include "utilities/commons.h"
@@ -25,42 +26,37 @@
* @param indices_num indices count
* @return uint16_t Mesh identifier. Can be useful for getting vertices, indices and instances
*/
uint16_t create_mesh(struct graphics_context_t* context, struct vertex_t* vertices,
rse_err_t mesh_create(struct graphics_context_t* context, struct vertex_t* vertices,
uint16_t* indices,
size_t vertices_num,
size_t indices_num);
size_t vertices_count,
size_t indices_count, rse_id_t* mesh_id);
/**
* @brief Create a instance for the selected mesh. Instance can have its own location, rotation and scale
*
* @param mesh_id Mesh id
* @param instance_data Transformation info
* @param context Graphics context
* @param entity Entity we wat to update
* @param instance_data New instance information
*/
rse_err_t create_mesh_instance(struct graphics_context_t* context, uint16_t mesh_id,
rse_err_t mesh_create_instance(struct graphics_context_t* context, entity_t entity,
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
*
* @param mesh_id Mesh identifier
* @return size_t Number of vertices
*/
size_t get_vertices_count(struct graphics_context_t* context, uint16_t mesh_id);
// size_t get_vertices_count(struct graphics_context_t* context, rse_id_t mesh_id);
/**
* @brief Get the index count for mesh with mesh_id ID
*
* @param mesh_id Mesh identifier
w @param mesh_id Mesh identifier
* @return size_t Number of indices
*/
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);
// size_t get_indices_count(struct graphics_context_t* context, rse_id_t mesh_id);
/**
* @brief Get vertex offset in vertex buffer for selected mesh
@@ -68,6 +64,6 @@ size_t get_instances_count(struct graphics_context_t* context, uint16_t mesh_id)
* @param mesh_id Mesh identifies
* @return size_t Offset
*/
size_t get_vertex_offset(struct graphics_context_t* context, uint16_t mesh_id);
// size_t get_vertex_offset(struct graphics_context_t* context, rse_id_t mesh_id);
#endif /* MESH_CONTROLLER_H */
+6
View File
@@ -0,0 +1,6 @@
#include "utilities/commons.h"
rse_err_t object_load_gltf(const char* file_path)
{
}
+159 -234
View File
@@ -12,79 +12,69 @@
#include "pipeline_builder.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "src/descriptor_builder.h"
#include "SDL3/SDL_log.h"
#include "src/graphics_context.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/file_utils.h"
#include "utilities/linked_list.h"
#include "utilities/localization.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_errors.h"
#define MAX_BINDLESS_RESOURCES 1000
RSE_LINKED_LIST_DEFINE(vertex_binding_list_t, VkVertexInputBindingDescription);
RSE_LINKED_LIST_DEFINE(shader_attribute_list_t, VkVertexInputAttributeDescription);
RSE_LINKED_LIST_DEFINE(dynamic_state_list_t, VkDynamicState);
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;
struct descriptor_set_handle_t* descriptor_sets;
};
/**
* @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)
rse_err_t shader_create_module(struct graphics_context_t* context,
rse_id_t* shader_module_id,
const char* shader_path,
const VkShaderStageFlagBits shader_stage)
{
unsigned char* buffer;
size_t buffer_size;
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.pNext = NULL;
create_info.flags = 0U;
create_info.codeSize = data_size;
create_info.pCode = (const uint32_t*)(binary_data);
create_info.codeSize = buffer_size;
create_info.pCode = (const uint32_t*)(buffer);
RSE_LINKED_LIST_PUSH_FRONT(context->shader_modules, (VkShaderModule){0});
if (context->shader_modules.shader_modules_count > MAX_SHADER_MODULES_COUNT) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Reached maximum number of available shader modules");
return RSE_ERROR_INTERNAL_ERROR;
}
if (VK_SUCCESS !=
vkCreateShaderModule(context->device, &create_info, NULL, &RSE_LINKED_LIST_FRONT(context->shader_modules))) {
LOGF(_("Failed to create a shader\n"));
return VULKAN_ERROR_SHADER_CREATION_FAILED;
vkCreateShaderModule(context->vulkan_handles.device,
&create_info,
NULL,
&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;
}
@@ -112,11 +102,13 @@ static rse_err_t pipeline_add_viewport_state(struct pipeline_t* pipeline)
pipeline->viewport_state.pScissors = NULL; /* This is set during rendering */
if (pipeline->viewport_state.pViewports == NULL || pipeline->viewport_state.viewportCount == 0U) {
RSE_STACK_PUSH(pipeline->required_dynamic_states, VK_DYNAMIC_STATE_VIEWPORT);
pipeline->required_dynamic_states.dynamic_states[pipeline->required_dynamic_states.dynamic_states_count++] =
VK_DYNAMIC_STATE_VIEWPORT;
}
if (pipeline->viewport_state.pScissors == NULL || pipeline->viewport_state.scissorCount == 0U) {
RSE_STACK_PUSH(pipeline->required_dynamic_states, VK_DYNAMIC_STATE_SCISSOR);
pipeline->required_dynamic_states.dynamic_states[pipeline->required_dynamic_states.dynamic_states_count++] =
VK_DYNAMIC_STATE_SCISSOR;
}
return RSE_ERROR_NO_ERROR;
@@ -203,35 +195,29 @@ static rse_err_t pipeline_add_depth_stencil(struct pipeline_t* pipeline)
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create a Pipeline Layout
*
* @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 descriptor_set_handle_t* descriptor_set_handle)
rse_err_t pipeline_add_descriptor_sets(struct graphics_context_t* context,
struct pipeline_t* pipeline,
rse_id_t descriptor_set_id)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkDescriptorSetLayout descriptor_sets_layouts = VK_NULL_HANDLE;
STATUS_CHECK(descriptor_get_layout(descriptor_set_handle, &descriptor_sets_layouts));
VkPipelineLayoutCreateInfo create_info = {0};
create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
create_info.pNext = NULL;
create_info.flags = 0U;
create_info.setLayoutCount = 1; // FIXME: Make sure we can enable more than one layout
create_info.pSetLayouts = &descriptor_sets_layouts;
create_info.pushConstantRangeCount = 0U;
create_info.pPushConstantRanges = NULL;
create_info.setLayoutCount = 1; // FIXME: Make sure we can enable more than one layout
create_info.pSetLayouts = &context->descriptor_data.set_layouts[descriptor_set_id];
create_info.pushConstantRangeCount = pipeline->push_constant_ranges.push_constant_ranges_count;
create_info.pPushConstantRanges = pipeline->push_constant_ranges.push_constant_ranges;
if (VK_SUCCESS !=
vkCreatePipelineLayout(context->device, &create_info, NULL, &context->pipelines_data.pipeline_layout)) {
LOGF(_("Failed to create pipeline layout\n"));
return VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED;
vkCreatePipelineLayout(context->vulkan_handles.device, &create_info, NULL, &pipeline->pipeline_layout)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create pipeline layout");
return RSE_ERROR_INTERNAL_ERROR;
}
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;
}
@@ -242,139 +228,107 @@ static rse_err_t create_pipeline_layout(struct graphics_context_t* context,
*
* @param[in/out] pipelines List of pipelines to clean up
*/
static void pipeline_builder_cleanup(struct pipeline_infos_list_t* pipelines)
static void pipeline_infos_cleanup(struct pipeline_infos_t* pipelines)
{
size_t pipeline_count = RSE_LINKED_LIST_SIZE(pipelines);
VkVertexInputBindingDescription* vertex_bindings = NULL;
VkVertexInputAttributeDescription* shader_attributes = NULL;
VkDynamicState* dynamic_states = NULL;
size_t pipeline_count = pipelines->pipeline_create_infos_count;
for (size_t i = 0; i < pipeline_count; i++) {
VkGraphicsPipelineCreateInfo* pipeline_info = &RSE_LINKED_LIST_FRONT(pipelines);
if (pipeline_info != NULL) {
vertex_bindings =
(VkVertexInputBindingDescription*)pipeline_info->pVertexInputState->pVertexBindingDescriptions;
VkGraphicsPipelineCreateInfo* pipeline_info = &pipelines->create_infos[i];
shader_attributes =
(VkVertexInputAttributeDescription*)pipeline_info->pVertexInputState->pVertexAttributeDescriptions;
dynamic_states = (VkDynamicState*)pipeline_info->pDynamicState->pDynamicStates;
rse_free((VkPipelineVertexInputStateCreateInfo*)pipeline_info->pVertexInputState);
rse_free((VkPipelineDynamicStateCreateInfo*)pipeline_info->pDynamicState);
rse_free(vertex_bindings);
rse_free(shader_attributes);
rse_free(dynamic_states);
}
RSE_LINKED_LIST_POP_FRONT(pipelines);
rse_free((VkPipelineVertexInputStateCreateInfo*)pipeline_info->pVertexInputState);
rse_free((VkPipelineDynamicStateCreateInfo*)pipeline_info->pDynamicState);
}
if (RSE_LINKED_LIST_IS_EMPTY(pipelines) == 0) {
LOGF(_("Failed to clean up pipelines\n"));
}
pipelines->pipeline_create_infos_count = 0;
}
rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, VkDynamicState dynamic_state)
{
if (dynamic_state >= VK_DYNAMIC_STATE_MAX_ENUM) {
LOGF(_("Invalid dynamic state in pipeline\n"));
return VULKAN_ERROR_PIPELINE_INVALID_DYNAMIC_STATE;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid dynamic state in pipeline");
return RSE_ERROR_INTERNAL_ERROR;
}
RSE_LINKED_LIST_PUSH_FRONT(pipeline->dynamic_states, dynamic_state);
// TODO: This was "Push Front" when this was still a list. This might be relevant
pipeline->dynamic_states[pipeline->dynamic_states_count++] = dynamic_state;
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;
struct pipeline_t* pipeline_ptr = NULL;
if (*pipeline != NULL) {
LOGF(
_("Pipeline is not NULL. This function will initialize everything "
"for you, no need for manual allocation\n"));
return VULKAN_ERROR_PIPELINE_ALREADY_INITIALIZED;
if (pipeline == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided NULL pointer");
return RSE_ERROR_INTERNAL_ERROR;
}
rse_malloc(pipeline_ptr, sizeof(struct pipeline_t));
rse_memset(pipeline_ptr, 0, sizeof(struct pipeline_t));
*pipeline = pipeline_ptr;
rse_memset(pipeline, 0, sizeof(struct pipeline_t));
return status;
}
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, struct pipeline_t* pipeline,
const char* shader_path, const VkShaderStageFlagBits shader_stage)
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context,
struct pipeline_t* pipeline,
const rse_id_t shader_id)
{
size_t i = 0;
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 */
if (pipeline->shader_stages_count + 1 > MAX_SHADER_STAGES) {
LOGF(_("Too many shader stages in pipeline\n"));
return VULKAN_ERROR_PIPELINE_TOO_MANY_SHADER_STAGES;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Too many shader stages in pipeline");
return RSE_ERROR_INTERNAL_ERROR;
}
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].pNext = NULL;
pipeline->shader_stages[i].flags = 0U;
pipeline->shader_stages[i].stage = shader_stage;
pipeline->shader_stages[i].module = RSE_LINKED_LIST_FRONT(context->shader_modules); /* This is automatically updated
during shader module creation */
pipeline->shader_stages[i].stage = context->shader_modules.shaders_stages[shader_id];
/* This is automatically updated during shader module creation */
pipeline->shader_stages[i].module = context->shader_modules.shader_modules[shader_id];
pipeline->shader_stages[i].pName = "main";
pipeline->shader_stages[i].pSpecializationInfo = NULL;
return status;
}
rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline, const uint8_t binding, const uint8_t stride,
rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline,
const uint8_t binding,
const uint8_t stride,
const VkVertexInputRate input_rate)
{
VkVertexInputBindingDescription binding_pipeline_description = {0};
if (input_rate >= VK_VERTEX_INPUT_RATE_MAX_ENUM) {
LOGF(_("Invalid vertex input rate\n"));
return VULKAN_ERROR_PIPELINE_INVALID_VERTEX_INPUT_RATE;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid vertex input rate");
return RSE_ERROR_INTERNAL_ERROR;
}
binding_pipeline_description.binding = binding;
binding_pipeline_description.stride = stride;
binding_pipeline_description.inputRate = input_rate;
RSE_LINKED_LIST_PUSH_FRONT(pipeline->vertex_bindings, binding_pipeline_description);
pipeline->vertex_bindings[pipeline->vertex_bindings_count++] = binding_pipeline_description;
return RSE_ERROR_NO_ERROR;
}
rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline, const uint8_t binding,
const uint8_t location, const VkFormat format, const uint8_t offset)
rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline,
const uint8_t binding,
const uint8_t location,
const VkFormat format,
const uint8_t offset)
{
VkVertexInputAttributeDescription attribute_description = {0};
if (binding >= 16U) {
LOGF(_("Invalid binding in pipeline attribute description\n"));
return VULKAN_ERROR_PIPELINE_INVALID_BINDING;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid binding in pipeline attribute description");
return RSE_ERROR_INTERNAL_ERROR;
}
if (format >= VK_FORMAT_MAX_ENUM) {
LOGF(_("Invalid format in pipeline attribute description\n"));
return VULKAN_ERROR_PIPELINE_INVALID_FORMAT;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid format in pipeline attribute description");
return RSE_ERROR_INTERNAL_ERROR;
}
attribute_description.binding = binding;
@@ -382,114 +336,87 @@ rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline, const
attribute_description.format = format;
attribute_description.offset = offset;
RSE_LINKED_LIST_PUSH_FRONT(pipeline->shader_attributes, attribute_description);
pipeline->shader_attributes[pipeline->shader_attributes_count++] = attribute_description;
return RSE_ERROR_NO_ERROR;
}
rse_err_t pipelines_build(struct graphics_context_t* context, struct pipeline_infos_list_t* pipelines_infos_list, struct descriptor_set_handle_t* descriptor_set_handles)
rse_err_t pipeline_add_push_constants(struct pipeline_t* pipeline,
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 != pipelines_infos_list);
size_t i = 0U;
rse_err_t status = RSE_ERROR_NO_ERROR;
size_t pipelines_count = RSE_LINKED_LIST_SIZE(pipelines_infos_list);
VkGraphicsPipelineCreateInfo* pipeline_infos = NULL;
context->pipelines_data.pipelines_count = pipelines_count;
STATUS_CHECK(create_pipeline_layout(context, descriptor_set_handles));
rse_malloc(pipeline_infos, sizeof(VkGraphicsPipelineCreateInfo) * pipelines_count);
RSE_LINKED_LIST_TO_ARRAY(pipelines_infos_list, pipeline_infos);
/* 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;
if (VK_SUCCESS !=
vkCreateGraphicsPipelines(context->vulkan_handles.device,
VK_NULL_HANDLE,
pipelines_infos_list->pipeline_create_infos_count,
pipelines_infos_list->create_infos,
NULL,
&context->pipelines_data.pipelines[context->pipelines_data.pipelines_count])) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create graphics pipelines");
status = RSE_ERROR_INTERNAL_ERROR;
}
if (VK_SUCCESS != vkCreateGraphicsPipelines(context->device, VK_NULL_HANDLE, pipelines_count, pipeline_infos, NULL,
context->pipelines_data.pipelines)) {
LOGF(_("Failed to create graphics pipelines\n"));
status = VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED;
}
rse_free(pipeline_infos);
pipeline_builder_cleanup(pipelines_infos_list);
context->pipelines_data.pipelines_count += pipelines_infos_list->pipeline_create_infos_count;
pipeline_infos_cleanup(pipelines_infos_list);
rse_memset(pipelines_infos_list, 0, sizeof(struct pipeline_infos_t));
return status;
}
rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline_t* pipeline,
struct pipeline_infos_list_t** pipelines_infos)
rse_err_t pipeline_add(const struct graphics_context_t* context,
struct pipeline_t* pipeline,
struct pipeline_infos_t* pipelines_infos)
{
/* 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;
VkGraphicsPipelineCreateInfo pipeline_info = {0};
size_t vertex_bindings_count = 0U;
VkVertexInputBindingDescription* vertex_bindings = 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;
uint8_t dynamic_state_found = 0;
while (RSE_STACK_IS_EMPTY(pipeline->required_dynamic_states) == 0) {
while (pipeline->required_dynamic_states.dynamic_states_count > 0) {
dynamic_state_found = 0;
VkDynamicState dynamic_state = RSE_STACK_POP(pipeline->required_dynamic_states);
RSE_LINKED_LIST_FOREACH(pipeline->dynamic_states, dynamic_state_ptr)
{
if (dynamic_state_ptr->data == dynamic_state) {
VkDynamicState dynamic_state =
pipeline->required_dynamic_states.dynamic_states[pipeline->required_dynamic_states.dynamic_states_count--];
for (iter = 0U; iter < pipeline->dynamic_states_count; ++iter) {
if (pipeline->dynamic_states[iter] == dynamic_state) {
dynamic_state_found = 1;
break;
}
}
if (dynamic_state_found == 0) {
LOGF(_("Missing dynamic state in pipeline\n"));
return VULKAN_ERROR_PIPELINE_MISSING_DYNAMIC_STATE;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Missing dynamic state in pipeline");
return RSE_ERROR_INTERNAL_ERROR;
}
}
/* 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 */
if (pipeline->shader_stages_count == 0) {
LOGF(
_("Missing shader stages in pipeline. There must be at least "
"one.\n"));
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_STAGES;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Missing shader stages in pipeline. There must be at least one.");
return RSE_ERROR_INTERNAL_ERROR;
}
/* 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 */
pipeline_add_input_assembly(pipeline);
pipeline_add_viewport_state(pipeline);
@@ -508,10 +435,10 @@ rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline
vertex_bindings_ci->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_bindings_ci->pNext = NULL;
vertex_bindings_ci->flags = 0U;
vertex_bindings_ci->vertexBindingDescriptionCount = vertex_bindings_count;
vertex_bindings_ci->pVertexBindingDescriptions = vertex_bindings;
vertex_bindings_ci->vertexAttributeDescriptionCount = shader_attributes_count;
vertex_bindings_ci->pVertexAttributeDescriptions = shader_attributes;
vertex_bindings_ci->vertexBindingDescriptionCount = pipeline->vertex_bindings_count;
vertex_bindings_ci->pVertexBindingDescriptions = pipeline->vertex_bindings;
vertex_bindings_ci->vertexAttributeDescriptionCount = pipeline->shader_attributes_count;
vertex_bindings_ci->pVertexAttributeDescriptions = pipeline->shader_attributes;
pipeline_info.pVertexInputState = vertex_bindings_ci;
pipeline_info.pInputAssemblyState = &pipeline->input_assembly;
@@ -526,16 +453,17 @@ rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline
dynamic_states_ci->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_states_ci->pNext = NULL;
dynamic_states_ci->flags = 0U;
dynamic_states_ci->dynamicStateCount = dynamic_states_count;
dynamic_states_ci->pDynamicStates = dynamic_states;
dynamic_states_ci->dynamicStateCount = pipeline->dynamic_states_count;
dynamic_states_ci->pDynamicStates = pipeline->dynamic_states;
pipeline_info.pDynamicState = dynamic_states_ci;
pipeline_info.layout = context->pipelines_data.pipeline_layout;
pipeline_info.renderPass = context->render_pass;
pipeline_info.layout = pipeline->pipeline_layout;
pipeline_info.renderPass = context->vulkan_handles.render_pass;
pipeline_info.subpass = 0;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // TODO: Add possiblity do derive from another pipeline
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;
}
@@ -543,20 +471,17 @@ rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline
void destroy_pipelines(struct graphics_context_t* context)
{
size_t i = 0;
size_t shader_modules_count = RSE_LINKED_LIST_SIZE(context->shader_modules);
vkDestroyPipelineLayout(context->device, context->pipelines_data.pipeline_layout, NULL);
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) {
vkDestroyPipeline(context->device, context->pipelines_data.pipelines[i], NULL);
vkDestroyPipeline(context->vulkan_handles.device, context->pipelines_data.pipelines[i], NULL);
}
}
for (i = 0; i < shader_modules_count; i++) {
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);
for (i = 0; i < context->shader_modules.shader_modules_count; i++) {
vkDestroyShaderModule(context->vulkan_handles.device, context->shader_modules.shader_modules[i], NULL);
}
}
+45 -20
View File
@@ -12,39 +12,37 @@
#ifndef PIPELINE_BUILDER_H
#define PIPELINE_BUILDER_H
#include <stdint.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_core.h>
#include "descriptor_builder.h"
#include "graphics_context.h"
#include "utilities/commons.h"
#include "utilities/linked_list.h"
#include "utilities/stack.h"
#define MAX_SHADER_STAGES 12U
struct pipeline_t;
struct pipeline_infos_list_t;
rse_err_t shader_create_module(struct graphics_context_t* context,
rse_id_t* shader_module_id,
const char* shader_path,
const VkShaderStageFlagBits shader_stage);
/**
* @brief Initialize pipeline builder
*
* @param[out] pipeline Pipeline to initialize. Must be NULL
* @param[out] pipeline Pipeline to initialize. Must not be NULL
* @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
*
* @param[in] context Graphics context handle
* @param[in] pipeline Pipeline to build
* @param[in] shader_path Path to the shader file
* @param[in] shader_stage Shader stage to add
* @param[in] shader_id ID of the shader we want to add to pipeline
* @return RSE_SUCCESS on success, error code otherwise
*/
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, struct pipeline_t* pipeline,
const char* shader_path, const VkShaderStageFlagBits shader_stage);
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context,
struct pipeline_t* pipeline,
const rse_id_t shader_id);
/**
* @brief Add vertex input binding to pipeline
@@ -55,7 +53,9 @@ rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, struct p
* @param[in] input_rate Input rate for vertex data
* @return RSE_SUCCESS on success, error code otherwise
*/
rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline, const uint8_t binding, const uint8_t stride,
rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline,
const uint8_t binding,
const uint8_t stride,
const VkVertexInputRate input_rate);
/**
@@ -68,8 +68,11 @@ rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline, const u
* @param[in] offset Offset of the attribute in bytes
* @return RSE_SUCCESS on success, error code otherwise
*/
rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline, const uint8_t binding,
const uint8_t location, const VkFormat format, const uint8_t offset);
rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline,
const uint8_t binding,
const uint8_t location,
const VkFormat format,
const uint8_t offset);
/**
* @brief Add dynamic state to pipeline
@@ -80,6 +83,27 @@ rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline, const
*/
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.
* After call, pipeline is ready to be used and all added components are cleared, ready for next pipeline to be built.
@@ -89,18 +113,19 @@ rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, const VkDynami
* @param[in] descriptor_set_handle Filled handle with descriptor sets
* @return RSE_SUCCESS on success, error code otherwise
*/
rse_err_t pipelines_build(struct graphics_context_t* context, struct pipeline_infos_list_t* pipelines, struct descriptor_set_handle_t* descriptor_set_handle);
rse_err_t pipelines_build(struct graphics_context_t* context, struct pipeline_infos_t* pipelines);
/**
* @brief Add pipeline to list of pipelines
*
* @param[in] context Graphics context handle
* @param[in] pipeline Pipeline to add
* @param[out] pipeline_info List of pipelines infos, where new info should be added
* @param[out] pipeline_infos Array of pipelines infos, where new info should be added
* @return RSE_SUCCESS on success, error code otherwise
*/
rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline_t* pipeline,
struct pipeline_infos_list_t** pipelines);
rse_err_t pipeline_add(const struct graphics_context_t* context,
struct pipeline_t* pipeline,
struct pipeline_infos_t* pipelines_infos);
/**
* @brief Destroy all pipelines
+41
View File
@@ -0,0 +1,41 @@
/**
* @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
@@ -0,0 +1,24 @@
/**
* @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 */
+245 -102
View File
@@ -1,17 +1,32 @@
#include "rse_graphics.h"
#include <string.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 "font_manager.h"
#include "graphics_context.h"
#include "math.h"
#include "mesh_controller.h"
#include "pipeline_builder.h"
#include "src/renderer.h"
#include "src/vulkan_buffers.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/file_utils.h"
#include "utilities/localization.h"
#include "utilities/logger.h"
#include "utilities/time_utils.h"
#include "vulkan_base.h"
#include "vulkan_commons.h"
#include "vulkan_image.h"
@@ -22,140 +37,268 @@ struct rse_graphics_context_t
struct graphics_context_t* context;
};
rse_err_t rse_graphics_init(struct rse_graphics_context_t** context)
static void model_view_projection_update(struct 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;
struct rse_graphics_context_t* ctx_ptr = NULL;
if (*context != NULL) {
LOGF(_("Graphics already initialized!\n"));
return RSE_ERROR_ALREADY_INITIALIZED;
}
SDL_Init(SDL_INIT_VIDEO);
rse_malloc(*context, sizeof(struct rse_graphics_context_t));
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));
STATUS_CHECK(window_init(&context->window_handle, &context->is_framebuffer_resized));
STATUS_CHECK(vulkan_init(context));
return status;
}
void rse_graphics_test_function(struct rse_graphics_context_t* rse_context)
#define TEXTURES_COUNT (3U)
rse_err_t rse_graphics_test_function(struct graphics_context_t* context)
{
char curr_path[4096] = {0};
struct graphics_context_t* context = rse_context->context;
uint16_t tex_id_2 = 0;
struct pipeline_t* pipeline = NULL;
struct descriptor_set_handle_t* descriptor_set_handle = NULL;
struct pipeline_infos_list_t* pipeline_infos = NULL;
struct vulkan_buffer_t ubo = {0};
rse_err_t status = RSE_ERROR_NO_ERROR;
rse_id_t textures[TEXTURES_COUNT] = {0};
struct pipeline_t pipeline;
rse_id_t descriptor_set_handle = 0;
struct descriptor_set_layout_bindings_t layout_bindings = {0};
struct pipeline_infos_t pipeline_infos = {0};
rse_id_t mesh_id_1 = 0;
rse_id_t mesh_id_2 = 0;
rse_id_t avocado_id = 0;
// rse_id_t font_id = 0;
rse_id_t static_mesh_renderer = 0U;
rse_id_t vertex_shader_id, fragment_shader_id;
get_curr_path(curr_path, 4096);
strncat(curr_path, "../../test_image.jpg", 4095);
load_texture_from_file(context, curr_path, &tex_id_2);
// if (status != RSE_ERROR_NO_ERROR) {
// LOGE("Failed to do something. Check error messages. Error: %d", status);
// return;
// }
// fonts_initialize();
STATUS_CHECK(texture_load_from_file(context, "../../test_image.jpg", &textures[0]));
STATUS_CHECK(texture_load_from_file(context, "../../test_image_2.jpg", &textures[1]));
// STATUS_CHECK(fonts_init());
// STATUS_CHECK(fonts_load_from_file(context, "../../NotoSansMono-Regular.ttf", &font_id));
// STATUS_CHECK(fonts_set_font_size(context, font_id, 16));
// FIXME: Temporary array of vertices, for testing purposes
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}, {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[] = {
{{-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}},
};
struct vertex_t vertices2[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, 0.0f}, {1.0f, 1.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[] = {
{{-0.7f, -0.7f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{-0.7f, 0.7f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.1f, 0.7f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.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
uint16_t indices[] = {0, 1, 2, 2, 3, 0};
uint16_t indices[] = {0, 1, 2, 2, 3, 0, 0, 3, 2, 2, 1, 0};
uint16_t mesh_id = create_mesh(context, vertices, indices, 6, 6);
create_mesh_instance(
context, mesh_id,
(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});
STATUS_CHECK(mesh_create(context, vertices, indices, 4, 12, &mesh_id_1));
if (RSE_ERROR_NO_ERROR !=
mesh_create_instance(context,
mesh_id_1,
(struct instance_data_t){{0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0}, 0.5f, textures[0]})) {
exit(1);
}
create_mesh_instance(
context, mesh_id,
(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});
if (RSE_ERROR_NO_ERROR !=
mesh_create_instance(
context,
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);
}
mesh_id = create_mesh(context, vertices2, indices, 6, 6);
create_mesh_instance(
context, mesh_id,
(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});
STATUS_CHECK(mesh_create(context, vertices_2, indices, 4, 6, &mesh_id_2));
if (RSE_ERROR_NO_ERROR !=
mesh_create_instance(
context,
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);
}
// fonts_load_from_file("/usr/share/fonts/JetBrainsMonoNerdFont/JetBrainsMonoNerdFont-Medium.ttf");
// print_text("Hello World!", (struct vec3_t){-1.0f, 0.0f, 0.9f}, 0.3f);
mesh_create_from_file(context, "../../Avocado.glb", &avocado_id, &textures[2]);
if (RSE_ERROR_NO_ERROR !=
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 */
pipeline_builder_init(&pipeline);
get_curr_path(curr_path, 4096);
strncat(curr_path, "shader.vert.num", 4095);
pipeline_add_shader_stage(context, pipeline, curr_path, VK_SHADER_STAGE_VERTEX_BIT);
get_curr_path(curr_path, 4096);
strncat(curr_path, "solid_objects.frag.num", 4095);
pipeline_add_shader_stage(context, pipeline, curr_path, VK_SHADER_STAGE_FRAGMENT_BIT);
shader_create_module(context, &vertex_shader_id, "shader.vert.num", VK_SHADER_STAGE_VERTEX_BIT);
pipeline_add_shader_stage(context, &pipeline, vertex_shader_id);
shader_create_module(context, &fragment_shader_id, "solid_objects.frag.num", 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, 1, sizeof(struct instance_data_t), VK_VERTEX_INPUT_RATE_INSTANCE);
pipeline_add_vertex_input_binding(&pipeline, 0, sizeof(struct vertex_t), VK_VERTEX_INPUT_RATE_VERTEX);
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, 2, VK_FORMAT_R32G32_SFLOAT, offsetof(struct vertex_t, tex_coords));
pipeline_add_vertex_input_attribute(pipeline, 1, 3, VK_FORMAT_R32G32B32_SFLOAT,
offsetof(struct instance_data_t, pos));
pipeline_add_vertex_input_attribute(pipeline, 1, 4, VK_FORMAT_R32G32B32_SFLOAT,
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_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,
2,
VK_FORMAT_R32G32_SFLOAT,
offsetof(struct vertex_t, tex_coords));
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_VIEWPORT);
pipeline_add_dynamic_state(&pipeline, VK_DYNAMIC_STATE_SCISSOR);
sampler_create(context, &context->sampler);
pipeline_add_push_constants(&pipeline, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(struct mvp_data_t));
pipeline_add_descriptor_sets(context, &pipeline, descriptor_set_handle);
descriptor_initialize_handle(&descriptor_set_handle);
pipeline_add(context, &pipeline, &pipeline_infos);
descriptor_add_set(descriptor_set_handle, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, NULL);
descriptor_add_set(descriptor_set_handle, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1,
VK_SHADER_STAGE_FRAGMENT_BIT, &context->sampler);
pipelines_build(context, &pipeline_infos);
descriptors_build(context, descriptor_set_handle);
renderer_get_new_id(context, &static_mesh_renderer);
add_pipeline(context, pipeline, &pipeline_infos);
renderer_set_render_function(context, static_mesh_renderer, render_static_mesh);
pipelines_build(context, pipeline_infos, descriptor_set_handle);
create_uniform_buffer(context, &ubo);
update_uniform_buffers(context, &ubo);
descriptor_attach_buffer(context, descriptor_set_handle, &ubo, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
descriptor_attach_image(context, descriptor_set_handle, tex_id_2, context->sampler, 1);
// TODO: Add a shader for text rendering
// pipeline_add_shader_stage("../graphics/shaders/on_screen_text.frag.num", VK_SHADER_STAGE_FRAGMENT_BIT);
return RSE_ERROR_NO_ERROR;
}
int rse_graphics_run(void* arg)
rse_err_t rse_graphics_run(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct rse_graphics_context_t* rse_context = (struct rse_graphics_context_t*)arg;
struct graphics_context_t* context = rse_context->context;
STATUS_CHECK(run_vulkan(context));
STATUS_CHECK(window_loop(context));
deinit_vulkan(context);
window_terminate(context->window_handle);
STATUS_CHECK(vulkan_run(context));
return status;
}
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);
rse_free(context->debug_overlay.pixels);
}
-154
View File
@@ -1,154 +0,0 @@
#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
@@ -1,25 +0,0 @@
/**
* @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,6 +14,7 @@
#ifdef __unix__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
@@ -22,8 +23,7 @@
#pragma GCC diagnostic ignored "-Wswitch"
#pragma GCC diagnostic ignored "-Wparentheses"
#endif
#include <cstdio>
#include "vma/vk_mem_alloc.h"
#include "vk_mem_alloc.h"
#ifdef __unix__
#pragma GCC diagnostic pop
#endif
+245 -151
View File
@@ -12,28 +12,27 @@
#include "vulkan_base.h"
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_vulkan.h>
#include <vma/vk_mem_alloc.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/errors_common.h"
#include "utilities/logger.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_errors.h"
#include "vulkan_commons.h"
#include "vulkan_commands.h"
#include "vulkan_buffers.h"
#include "vulkan_commands.h"
#include "vulkan_commons.h"
#include "vulkan_image.h"
#include "vulkan_render_pass.h"
#include "vulkan_swapchain.h"
#include "pipeline_builder.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define APPLICATION_NAME "RedScarfEngine PoC"
#define ENGINE_NAME "RedScarf Engine"
@@ -48,10 +47,10 @@
* @return VkBool32 Should always return VK_FALSE. The VK_TRUE value is reserved for use in layer development
*/
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT message_type,
const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data)
VkDebugUtilsMessageTypeFlagsEXT message_type,
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
void* user_data)
{
enum log_level_t log_level = LOGLEVEL_TRACE;
(void)message_severity;
(void)message_type;
(void)user_data;
@@ -60,17 +59,15 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverity
* here. We have to check flags and set severity to most important one */
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
log_level = LOGLEVEL_ERROR;
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage);
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
log_level = LOGLEVEL_WARNING;
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage);
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
log_level = LOGLEVEL_INFO;
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage);
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
log_level = LOGLEVEL_TRACE;
SDL_LogTrace(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage);
}
LOGGER(log_level, callback_data->pMessage);
return VK_FALSE;
}
@@ -83,9 +80,10 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverity
* @param debug_messenger Debug messanger handle
* @return VkResult VK_SUCCESS on success
*/
static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info,
const VkAllocationCallbacks* allocator,
VkDebugUtilsMessengerEXT* debug_messenger)
static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* create_info,
const VkAllocationCallbacks* allocator,
VkDebugUtilsMessengerEXT* debug_messenger)
{
PFN_vkCreateDebugUtilsMessengerEXT func = NULL;
@@ -111,20 +109,19 @@ static rse_err_t setup_debug_messenger(VkInstance instance, VkDebugUtilsMessenge
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
create_info.pNext = NULL;
create_info.messageSeverity = 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_ERROR_BIT_EXT;
create_info.messageSeverity =
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_ERROR_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_PERFORMANCE_BIT_EXT;
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
create_info.pfnUserCallback = debug_callback;
create_info.pUserData = NULL; // Optional
create_info.flags = 0U;
if (CreateDebugUtilsMessengerEXT(instance, &create_info, NULL, messenger) != VK_SUCCESS) {
LOGF(_("Failed to setup debug messenger\n"));
return VULKAN_ERROR_DEBUG_MESSENGER_CREATION_FAILED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to setup debug messenger");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
@@ -137,8 +134,9 @@ static rse_err_t setup_debug_messenger(VkInstance instance, VkDebugUtilsMessenge
* @param debug_messenger Debug messanger handle
* @param allocator Memory allocator
*/
static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debug_messenger,
const VkAllocationCallbacks* allocator)
static void DestroyDebugUtilsMessengerEXT(VkInstance instance,
VkDebugUtilsMessengerEXT debug_messenger,
const VkAllocationCallbacks* allocator)
{
PFN_vkDestroyDebugUtilsMessengerEXT func =
(PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
@@ -176,7 +174,7 @@ static rse_err_t create_instance(VkInstance* instance)
"VK_KHR_xlib_surface",
#endif /* WAYLAND */
#elif defined(_WIN32) || defined(WIN32)
"VK_KHR_win32_surface",
"VK_KHR_win32_surface",
#endif /* _WIN32 || WIN32 */
"VK_EXT_debug_utils",
};
@@ -201,43 +199,47 @@ static rse_err_t create_instance(VkInstance* instance)
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions_properties);
#ifndef NDEBUG
LOGD("Supported Layers: \n");
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "Supported Layers: ");
for (i = 0; i < layersCount; i++) {
LOGD("\t-%s\n", layer_properties[i].layerName);
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "\t-%s", layer_properties[i].layerName);
}
LOGD("Supported Extensions: \n");
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "Supported Extensions: ");
for (size_t i = 0; i < extensionCount; i++) {
LOGD("\t-%s\n", extensions_properties[i].extensionName);
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "\t-%s", extensions_properties[i].extensionName);
}
#endif
/* Check if selected layers are supported*/
for (i = 0; i < enabled_instance_layers_count; ++i) {
for(j = 0; i < layersCount; ++j) {
if(strcmp(layer_properties[j].layerName, enabled_instance_layers_names[i]) == 0) {
for (j = 0; i < layersCount; ++j) {
if (strcmp(layer_properties[j].layerName, enabled_instance_layers_names[i]) == 0) {
supported = 1;
break;
}
}
if(supported == 0) {
LOGF(_("Selected layer is not supported: %s\n"), layer_properties[j].layerName);
return VULKAN_ERROR_LAYER_NOT_SUPPORTED;
if (supported == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Selected layer is not supported: %s", layer_properties[j].layerName);
return RSE_ERROR_INTERNAL_ERROR;
}
supported = 0;
}
/* 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(j = 0; j < extensionCount; ++j) {
if(strcmp(extensions_properties[j].extensionName, enabled_instance_extensions_names[i]) == 0) {
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "\t-%s", enabled_instance_extensions_names[i]);
for (j = 0; j < extensionCount; ++j) {
if (strcmp(extensions_properties[j].extensionName, enabled_instance_extensions_names[i]) == 0) {
supported = 1;
break;
}
}
if(supported == 0) {
LOGF(_("Selected extension is not supported: %s\n"), extensions_properties[j].extensionName);
return VULKAN_ERROR_EXTENSION_NOT_SUPPORTED;
if (supported == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU,
"Selected extension is not supported: %s",
extensions_properties[j].extensionName);
return RSE_ERROR_INTERNAL_ERROR;
}
supported = 0;
}
@@ -264,8 +266,8 @@ static rse_err_t create_instance(VkInstance* instance)
result = vkCreateInstance(&create_info, NULL, instance);
if (VK_SUCCESS != result) {
LOGF(_("Vulkan instance initialization failed\n"));
return VULKAN_ERROR_INSTANCE_INIT_FAILED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Vulkan instance initialization failed");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
@@ -279,8 +281,8 @@ static rse_err_t create_instance(VkInstance* instance)
static rse_err_t create_surface(VkInstance instance, SDL_Window* window_handle, VkSurfaceKHR* surface)
{
if (true != SDL_Vulkan_CreateSurface(window_handle, instance, NULL, surface)) {
LOGF(_("Failed to create window surface. Error: %s\n"), SDL_GetError());
return VULKAN_ERROR_SURFACE_CREATION_FAILED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create window surface. Error: %s", SDL_GetError());
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
}
@@ -296,15 +298,16 @@ static rse_err_t pick_physical_device(VkInstance instance, VkPhysicalDevice* phy
{
uint32_t physical_device_count = 0U;
VkPhysicalDevice* physical_devices = NULL;
VkPhysicalDeviceProperties device_properties = {0};;
VkPhysicalDeviceProperties device_properties = {0};
;
VkPhysicalDeviceFeatures device_features = {0};
/* Get number of existing physical devices */
vkEnumeratePhysicalDevices(instance, &physical_device_count, NULL);
if (0 == physical_device_count) {
LOGF(_("No physical graphical physical devices found in system\n"));
return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "No physical graphical physical devices found in system");
return RSE_ERROR_INTERNAL_ERROR;
}
rse_malloc(physical_devices, sizeof(VkPhysicalDevice) * physical_device_count);
@@ -328,10 +331,12 @@ static rse_err_t pick_physical_device(VkInstance instance, VkPhysicalDevice* phy
if (VK_NULL_HANDLE == physical_device) {
/* No suitable device found */
LOGF(_("No suitable physical device found\n"));
return VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "No suitable physical device found");
return RSE_ERROR_INTERNAL_ERROR;
}
rse_free(physical_devices);
return RSE_ERROR_NO_ERROR;
}
@@ -349,39 +354,64 @@ static rse_err_t create_device(struct graphics_context_t* context)
float* graphics_familiy_queue_priorities = NULL;
float* presentation_familiy_queue_priorities = NULL;
uint32_t device_queues_count = 0;
uint32_t graphics_family_idx = 0;
int64_t graphics_family_idx = 0;
uint32_t presentation_familiy_idx = 0;
uint32_t queue_families_property_count = 0U;
uint32_t enabled_device_extensions_count = 2;
VkBool32 bindless_supported = VK_FALSE;
VkBool32 device_feature_supported = VK_FALSE;
VkDeviceCreateInfo device_create_info = {0};
VkQueueFamilyProperties* queue_family_properties = NULL;
VkDeviceQueueCreateInfo device_queue_createinfos[2] = {0};
VkPhysicalDeviceFeatures2 physical_features2 = {0};
VkPhysicalDeviceSynchronization2Features synchronization2_features = {0};
VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore_features = {0};
VkPhysicalDeviceDescriptorIndexingFeatures indexing_features = {0};
const char* enabled_device_extensions_names[] = {
"VK_KHR_swapchain",
"VK_EXT_descriptor_indexing",
};
/* Enable descriptori indexing for bindless textures */
/* Enable synchronization2 features */
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.pNext = &timeline_semaphore_features;
physical_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
physical_features2.pNext = &indexing_features;
vkGetPhysicalDeviceFeatures2(context->physical_device, &physical_features2);
vkGetPhysicalDeviceFeatures2(context->vulkan_handles.physical_device, &physical_features2);
bindless_supported = indexing_features.descriptorBindingPartiallyBound && indexing_features.runtimeDescriptorArray;
if (bindless_supported == VK_FALSE) {
LOGF(_("Required features for bindless design not supported\n"));
return VULKAN_ERROR_MISSING_FEATURES_FOR_BINDLESS_DESIGN;
device_feature_supported = indexing_features.descriptorBindingPartiallyBound && indexing_features.runtimeDescriptorArray;
if (device_feature_supported == VK_FALSE) {
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Required features for bindless design is not supported");
return RSE_ERROR_INTERNAL_ERROR;
}
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 */
vkGetPhysicalDeviceQueueFamilyProperties(context->physical_device, &queue_families_property_count, NULL);
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device,
&queue_families_property_count,
NULL);
rse_malloc(queue_family_properties, sizeof(VkQueueFamilyProperties) * queue_families_property_count);
vkGetPhysicalDeviceQueueFamilyProperties(context->physical_device, &queue_families_property_count,
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device,
&queue_families_property_count,
queue_family_properties);
/* Get queue families that support graphics operations AND have most
@@ -389,12 +419,12 @@ static rse_err_t create_device(struct graphics_context_t* context)
{
size_t numberOfQueues = 0;
for (uint32_t familyIdx = 0; familyIdx < queue_families_property_count; ++familyIdx) {
LOGI("Checking queue: %ld", familyIdx);
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Checking queue: %d", familyIdx);
if (queue_family_properties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
if (queue_family_properties[familyIdx].queueCount > numberOfQueues) {
graphics_family_idx = familyIdx;
graphics_family_found = true;
LOGI("Setting graphics family idx to %ld", familyIdx);
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Setting graphics family idx to %d", familyIdx);
numberOfQueues = queue_family_properties[familyIdx].queueCount;
device_queues_count++;
}
@@ -402,7 +432,10 @@ static rse_err_t create_device(struct graphics_context_t* context)
/* Check for family with surface support */
VkBool32 presentSupport = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(context->physical_device, familyIdx, context->surface, &presentSupport);
vkGetPhysicalDeviceSurfaceSupportKHR(context->vulkan_handles.physical_device,
familyIdx,
context->vulkan_handles.surface,
&presentSupport);
if (presentSupport) {
presentation_familiy_idx = familyIdx;
presentation_familiy_found = true;
@@ -419,23 +452,25 @@ 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 */
if (0 > graphics_family_idx) {
LOGF(_("Selected physical device does not support required queue capabilities\n"));
return VULKAN_ERROR_QUEUE_NOT_SUPPORTED;
if (graphics_family_idx < 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Selected physical device does not support required queue capabilities");
return RSE_ERROR_INTERNAL_ERROR;
}
context->queue_family_indices[0] = graphics_family_idx;
context->queue_family_indices[QUEUE_FAMILY_INDEX_GRAPHICS] = graphics_family_idx;
context->queue_family_indices[QUEUE_FAMILY_INDEX_PRESENTATION] = presentation_familiy_idx;
rse_malloc(graphics_familiy_queue_priorities, sizeof(float) * queue_family_properties[graphics_family_idx].queueCount);
rse_malloc(graphics_familiy_queue_priorities,
sizeof(float) * queue_family_properties[graphics_family_idx].queueCount);
for (size_t i = 0; i < queue_family_properties[graphics_family_idx].queueCount; i++) {
graphics_familiy_queue_priorities[i] = 1.0f;
}
rse_malloc(presentation_familiy_queue_priorities, sizeof(float) * queue_family_properties[presentation_familiy_idx].queueCount);
rse_malloc(presentation_familiy_queue_priorities,
sizeof(float) * queue_family_properties[presentation_familiy_idx].queueCount);
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;
@@ -463,13 +498,19 @@ static rse_err_t create_device(struct graphics_context_t* context)
device_create_info.ppEnabledExtensionNames = enabled_device_extensions_names;
device_create_info.pEnabledFeatures = NULL;
if (VK_SUCCESS != vkCreateDevice(context->physical_device, &device_create_info, NULL, &context->device)) {
LOGF(_("Failed to create logical device\n"));
return VULKAN_ERROR_DEVICE_CREATION_FAILED;
if (VK_SUCCESS != vkCreateDevice(context->vulkan_handles.physical_device,
&device_create_info,
NULL,
&context->vulkan_handles.device)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create logical device");
return RSE_ERROR_INTERNAL_ERROR;
}
vkGetDeviceQueue(context->device, graphics_family_idx, 0, &context->graphics_queue);
vkGetDeviceQueue(context->device, presentation_familiy_idx, 0, &context->present_queue);
vkGetDeviceQueue(context->vulkan_handles.device, graphics_family_idx, 0, &context->vulkan_handles.graphics_queue);
vkGetDeviceQueue(context->vulkan_handles.device,
presentation_familiy_idx,
0,
&context->vulkan_handles.present_queue);
rse_free(presentation_familiy_queue_priorities);
rse_free(graphics_familiy_queue_priorities);
@@ -492,20 +533,19 @@ static rse_err_t create_memory_allocator(struct graphics_context_t* context)
VmaAllocatorCreateInfo allocator_create_info = {0};
allocator_create_info.vulkanApiVersion = VK_API_VERSION_1_3;
allocator_create_info.physicalDevice = context->physical_device;
allocator_create_info.device = context->device;
allocator_create_info.instance = context->instance;
allocator_create_info.physicalDevice = context->vulkan_handles.physical_device;
allocator_create_info.device = context->vulkan_handles.device;
allocator_create_info.instance = context->vulkan_handles.instance;
allocator_create_info.pVulkanFunctions = &vulkan_functions;
if (VK_SUCCESS != vmaCreateAllocator(&allocator_create_info, &context->allocator)) {
LOGF(_("Failed to create Vulkan Memory Allocator\n"));
return VULKAN_ERROR_ALLOCATOR_CREATION_FAILED;
if (VK_SUCCESS != vmaCreateAllocator(&allocator_create_info, &context->vulkan_handles.allocator)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create Vulkan Memory Allocator");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create a Sync Objects
*
@@ -515,33 +555,57 @@ static rse_err_t create_memory_allocator(struct graphics_context_t* context)
static rse_err_t create_sync_objects(struct graphics_context_t* context)
{
VkSemaphoreCreateInfo semaphore_info = {0};
VkSemaphoreTypeCreateInfo semaphore_type = {0};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo = {0};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
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;
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
if (VK_SUCCESS != vkCreateSemaphore(context->device, &semaphore_info, NULL, &context->image_available_semaphores[i]) ||
VK_SUCCESS != vkCreateSemaphore(context->device, &semaphore_info, NULL, &context->render_finished_semaphores[i]) ||
VK_SUCCESS != vkCreateFence(context->device, &fenceInfo, NULL, &context->in_flight_fences[i])) {
LOGF(_("Failed to create sync objects.\n"));
return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED;
if (VK_SUCCESS != vkCreateSemaphore(context->vulkan_handles.device,
&semaphore_info,
NULL,
&context->vulkan_handles.image_available_semaphores[i]) ||
VK_SUCCESS != vkCreateSemaphore(context->vulkan_handles.device,
&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;
}
rse_err_t init_vulkan(struct graphics_context_t* context)
rse_err_t vulkan_init(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_instance(&context->instance));
STATUS_CHECK(setup_debug_messenger(context->instance, &context->debug_messenger));
STATUS_CHECK(create_surface(context->instance, context->window_handle, &context->surface));
STATUS_CHECK(pick_physical_device(context->instance, &context->physical_device));
STATUS_CHECK(create_instance(&context->vulkan_handles.instance));
STATUS_CHECK(setup_debug_messenger(context->vulkan_handles.instance, &context->vulkan_handles.debug_messenger));
STATUS_CHECK(
create_surface(context->vulkan_handles.instance, context->window_handle, &context->vulkan_handles.surface));
STATUS_CHECK(pick_physical_device(context->vulkan_handles.instance, &context->vulkan_handles.physical_device));
STATUS_CHECK(create_device(context));
STATUS_CHECK(create_memory_allocator(context));
STATUS_CHECK(create_render_pass(context));
@@ -553,105 +617,135 @@ rse_err_t init_vulkan(struct graphics_context_t* context)
return status;
}
rse_err_t run_vulkan(struct graphics_context_t* context)
rse_err_t vulkan_run(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_sync_objects(context));
init_time();
return status;
}
rse_err_t draw_frame(struct graphics_context_t* context)
rse_err_t vulkan_draw_frame(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
uint32_t image_index = 0U;
VkResult result = VK_FALSE;
VkSubmitInfo submit_info = {0};
VkSemaphore wait_semaphores[1];
VkPipelineStageFlags wait_stages[1];
VkSemaphore signal_semaphores[1];
VkSubmitInfo2 submit_info = {0};
VkPresentInfoKHR present_info = {0};
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};
vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
timeline_wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;
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;
update_time();
vkWaitSemaphores(context->vulkan_handles.device, &timeline_wait_info, UINT64_MAX);
result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphores[context->current_frame],
VK_NULL_HANDLE, &image_index);
STATUS_CHECK(buffers_update(context));
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) {
recreate_swapchain(context);
return RSE_ERROR_NO_ERROR;
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
LOGF(_("Failed to acquire swapchain image\n"));
return VULKAN_ERROR_ACQUIRE_SWAPCHAIN_FAILED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to acquire swapchain image");
return RSE_ERROR_INTERNAL_ERROR;
}
// 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);
record_command_buffer(context, image_index);
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
context->frame_finished_idx++;
binary_signal_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
binary_signal_info.pNext = NULL;
binary_signal_info.semaphore = context->vulkan_handles.render_finished_semaphores[context->current_frame];
binary_signal_info.value = 0;
binary_signal_info.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
wait_semaphores[0] = context->image_available_semaphores[context->current_frame];
wait_stages[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = wait_semaphores;
submit_info.pWaitDstStageMask = wait_stages;
timeline_signal_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
timeline_signal_info.pNext = NULL;
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;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &context->command_buffers[context->current_frame];
signal_infos[0] = binary_signal_info;
signal_infos[1] = timeline_signal_info;
signal_semaphores[0] = context->render_finished_semaphores[context->current_frame];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores;
binary_wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
binary_wait_info.pNext = NULL;
binary_wait_info.semaphore = context->vulkan_handles.image_available_semaphores[context->current_frame];
binary_wait_info.value = 0;
binary_wait_info.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
if (vkQueueSubmit(context->graphics_queue, 1, &submit_info, context->in_flight_fences[context->current_frame]) != VK_SUCCESS) {
LOGF(_("Failed to submit draw command buffer!\n"));
return VULKAN_ERROR_DRAW_FAILED;
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.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &context->render_finished_semaphores[context->current_frame];
present_info.pWaitSemaphores = &context->vulkan_handles.render_finished_semaphores[image_index];
swap_chains[0] = context->swapchain;
swap_chains[0] = context->swapchain_data.swapchain;
present_info.swapchainCount = 1;
present_info.pSwapchains = swap_chains;
present_info.pImageIndices = &image_index;
result = vkQueuePresentKHR(context->present_queue, &present_info);
result = vkQueuePresentKHR(context->vulkan_handles.present_queue, &present_info);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || context->is_framebuffer_resized) {
context->is_framebuffer_resized = false;
recreate_swapchain(context);
} else if (result != VK_SUCCESS) {
LOGF(_("Failed to submit draw command buffer!\n"));
return VULKAN_ERROR_DRAW_FAILED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to submit draw command buffer!");
return RSE_ERROR_INTERNAL_ERROR;
}
context->current_frame = (context->current_frame + 1) % SWAP_BUFFER_COUNT;
return RSE_ERROR_NO_ERROR;
return status;
}
void deinit_vulkan(struct graphics_context_t* context)
void vulkan_deinit(struct graphics_context_t* context)
{
vkDeviceWaitIdle(context->vulkan_handles.device);
destroy_buffers(context);
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
vkDestroySemaphore(context->device, context->image_available_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.image_available_semaphores[i], NULL);
vkDestroySemaphore(context->vulkan_handles.device, context->vulkan_handles.render_finished_semaphores[i], NULL);
}
vkDestroySemaphore(context->vulkan_handles.device, context->vulkan_handles.timeline_semaphore, NULL);
destroy_textures(context);
@@ -662,9 +756,9 @@ void deinit_vulkan(struct graphics_context_t* context)
destroy_pipelines(context);
destroy_descriptors(context);
vmaDestroyAllocator(context->allocator);
vkDestroyDevice(context->device, NULL);
DestroyDebugUtilsMessengerEXT(context->instance, context->debug_messenger, NULL);
vkDestroySurfaceKHR(context->instance, context->surface, NULL);
vkDestroyInstance(context->instance, NULL);
vmaDestroyAllocator(context->vulkan_handles.allocator);
vkDestroyDevice(context->vulkan_handles.device, NULL);
DestroyDebugUtilsMessengerEXT(context->vulkan_handles.instance, context->vulkan_handles.debug_messenger, NULL);
vkDestroySurfaceKHR(context->vulkan_handles.instance, context->vulkan_handles.surface, NULL);
vkDestroyInstance(context->vulkan_handles.instance, NULL);
}
+4 -4
View File
@@ -23,7 +23,7 @@
* @param context Graphics context handle
* @return rse_err_t 0 on success. Status code on failure
*/
rse_err_t init_vulkan(struct graphics_context_t* context);
rse_err_t vulkan_init(struct graphics_context_t* context);
/**
* @brief Initialize Vulkan backend with descriptors
@@ -31,14 +31,14 @@ rse_err_t init_vulkan(struct graphics_context_t* context);
* @param context Graphics context handle
* @return rse_err_t 0 on success. Status code on failure
*/
rse_err_t run_vulkan(struct graphics_context_t* context);
rse_err_t vulkan_run(struct graphics_context_t* context);
/**
* @brief Deinitialize Vulkan backend
*
* @param context Graphics context handle
* */
void deinit_vulkan(struct graphics_context_t* context);
void vulkan_deinit(struct graphics_context_t* context);
/**
* @brief Draw frame on the screen.
@@ -46,6 +46,6 @@ void deinit_vulkan(struct graphics_context_t* context);
* @param context Graphics context
* @return rse_err_t
*/
rse_err_t draw_frame(struct graphics_context_t* context);
rse_err_t vulkan_draw_frame(struct graphics_context_t* context);
#endif /* GRAPHICS_VULKANBASE_H */
+222 -188
View File
@@ -1,22 +1,24 @@
#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 "math.h"
#include "src/descriptor_builder.h"
#include "src/graphics_context.h"
#include "src/vulkan_commons.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/linked_list.h"
#include "utilities/localization.h"
#include "utilities/logger.h"
#include "utilities/vector.h"
#include "vulkan_commands.h"
#include "vulkan_errors.h"
#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
@@ -25,14 +27,18 @@
* @param dst Destination buffer
* @param size Size of buffer to copy
*/
static void copy_buffer(struct graphics_context_t* context, VkBuffer src, VkBuffer dst, VkDeviceSize size,
VkDeviceSize dest_offset)
static rse_err_t copy_buffer(struct graphics_context_t* context,
VkBuffer src,
VkBuffer dst,
VkDeviceSize size,
VkDeviceSize dest_offset)
{
/* Vulkan buffers can only be copied using command buffers */
rse_err_t status = RSE_ERROR_NO_ERROR;
VkBufferCopy copy_region = {0};
VkCommandBuffer command_buffer = {0};
VkCommandBuffer command_buffer = VK_NULL_HANDLE;
command_buffer = begin_single_time_command(context);
STATUS_CHECK(begin_single_time_command(context, &command_buffer));
copy_region.srcOffset = 0U;
copy_region.dstOffset = dest_offset;
@@ -40,7 +46,9 @@ static void copy_buffer(struct graphics_context_t* context, VkBuffer src, VkBuff
vkCmdCopyBuffer(command_buffer, src, dst, 1, &copy_region);
end_single_time_comands(context, command_buffer);
STATUS_CHECK(end_single_time_comands(context, command_buffer));
return status;
}
/**
@@ -59,13 +67,16 @@ static rse_err_t create_vertex_buffer(struct graphics_context_t* context)
buffer_size = MAX_VERTEX_BUFFER_SIZE;
/* Create Vertex Buffer*/
STATUS_CHECK(create_buffer(context, buffer_size,
STATUS_CHECK(create_buffer(context,
buffer_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
&context->vertex_buffer));
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
0, /* Will not be mapped with vmaMapMemory */
&context->render_targets[RENDER_TARGET_3D].vertex_buffer));
context->vertex_buffer.allocated_size = 0;
return RSE_ERROR_NO_ERROR;
context->render_targets[RENDER_TARGET_3D].vertex_buffer.allocated_size = 0;
return status;
}
/**
@@ -83,17 +94,19 @@ static rse_err_t create_index_buffer(struct graphics_context_t* context)
buffer_size = MAX_VERTEX_BUFFER_SIZE;
/* Create Index Buffer*/
STATUS_CHECK(create_buffer(context, buffer_size,
STATUS_CHECK(create_buffer(context,
buffer_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
&context->index_buffer));
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
0, /* Will not be mapped with vmaMapMemory */
&context->render_targets[RENDER_TARGET_3D].index_buffer));
context->index_buffer.allocated_size = 0;
context->render_targets[RENDER_TARGET_3D].index_buffer.allocated_size = 0;
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create instance buffers
* @brief Create instance buffer alongside draw indirect command buffer
*
* @param[in/out] context Graphics context handle
* @return RSE_ERROR_NO_ERROR on success.
@@ -101,48 +114,54 @@ static rse_err_t create_index_buffer(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;
size_t iter = 0U;
VkDeviceSize buffer_size;
buffer_size = sizeof(struct instance_data_t) * MAX_INSTANCE_NUMBER;
for (iter = 0; iter < MAX_MESH_NUMBER; ++iter) {
/* Create Instance Buffer */
STATUS_CHECK(create_buffer(context, buffer_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
&context->instance_buffers[iter]));
STATUS_CHECK(
create_buffer(context,
buffer_size,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
&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->instance_buffers[iter].allocated_size = 0;
}
context->render_targets[RENDER_TARGET_3D].instance_buffer.allocated_size = 0;
STATUS_CHECK(create_buffer(
context, sizeof(VkDrawIndexedIndirectCommand) * MAX_MESH_NUMBER,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
&context->draw_indirect_command_buffer));
return RSE_ERROR_NO_ERROR;
}
rse_err_t create_uniform_buffer(struct graphics_context_t* context, struct vulkan_buffer_t* buffer)
rse_err_t buffer_create_uniform(struct graphics_context_t* context,
struct vulkan_buffer_t* buffer,
VkDeviceSize buffer_size)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkDeviceSize buffer_size =
sizeof(struct uniform_buffer_object_t); // TODO: Change later. Uniform buffer is not only MVP
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,
buffer));
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, buffer));
RSE_LINKED_LIST_PUSH_BACK(context->uniform_buffers, *buffer);
context->uniform_buffers.vulkan_buffers[context->uniform_buffers.vulkan_buffers_count++] = *buffer;
return RSE_ERROR_NO_ERROR;
}
rse_err_t create_buffer(struct graphics_context_t* context, const VkDeviceSize size, VkBufferUsageFlags buffer_usage,
VmaMemoryUsage memory_usage, const VmaAllocationCreateFlags allocation_flags,
rse_err_t create_buffer(struct graphics_context_t* context,
const VkDeviceSize size,
VkBufferUsageFlags buffer_usage,
VmaMemoryUsage memory_usage,
const VmaAllocationCreateFlags allocation_flags,
struct vulkan_buffer_t* buffer)
{
VkBufferCreateInfo vertex_buffer_info = {0};
@@ -166,15 +185,16 @@ rse_err_t create_buffer(struct graphics_context_t* context, const VkDeviceSize s
create_info.pUserData = VK_NULL_HANDLE;
create_info.priority = 0.0f;
if (VK_SUCCESS != vmaCreateBuffer(context->allocator, &vertex_buffer_info, &create_info, &buffer->buffer,
&buffer->allocation, &buffer->allocation_info)) {
LOGF(_("Failed to create a vertex buffer\n"));
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
if (VK_SUCCESS != vmaCreateBuffer(context->vulkan_handles.allocator,
&vertex_buffer_info,
&create_info,
&buffer->buffer,
&buffer->allocation,
&buffer->allocation_info)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create a vertex buffer");
return RSE_ERROR_INTERNAL_ERROR;
}
static int count = 1;
LOGI("Created buffers: %d\n", count++);
buffer->allocated_size = size;
return RSE_ERROR_NO_ERROR;
@@ -182,119 +202,155 @@ rse_err_t create_buffer(struct graphics_context_t* context, const VkDeviceSize s
void destroy_buffer(struct graphics_context_t* context, struct vulkan_buffer_t* buffer)
{
vmaDestroyBuffer(context->allocator, buffer->buffer, buffer->allocation);
vmaDestroyBuffer(context->vulkan_handles.allocator, buffer->buffer, buffer->allocation);
}
void update_uniform_buffers(struct graphics_context_t* context, const struct vulkan_buffer_t* 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(buffer->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 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;
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;
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* index_buffer = &context->index_buffer;
struct vulkan_buffer_t* vertex_buffer = &context->render_targets[RENDER_TARGET_3D].vertex_buffer;
struct vulkan_buffer_t* index_buffer = &context->render_targets[RENDER_TARGET_3D].index_buffer;
/* Creating staging buffer*/
STATUS_CHECK(create_buffer(
context, MAX_VERTEX_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));
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));
/* 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);
copy_buffer(context, staging_buffer.buffer, context->vertex_buffer.buffer, vertices_size,
context->vertex_buffer.allocated_size);
copy_buffer(context, staging_buffer.buffer, vertex_buffer->buffer, vertices_size, vertex_buffer->allocated_size);
vertex_buffer->allocated_size += vertices_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 */
memset(staging_buffer.allocation_info.pMappedData, 0, 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);
index_data->buffer_offset = index_buffer->allocated_size;
index_buffer->allocated_size += indices_size;
destroy_buffer(context, &staging_buffer);
draw_indirect_command.indexCount = vertex_data->count;
draw_indirect_command.instanceCount = 0;
draw_indirect_command.firstIndex = 0;
draw_indirect_command.vertexOffset = vertex_data->buffer_offset;
draw_indirect_command.firstInstance = 0;
memcpy((char*)context->draw_indirect_command_buffer.allocation_info.pMappedData +
(mesh_id * sizeof(VkDrawIndexedIndirectCommand)),
&draw_indirect_command, sizeof(VkDrawIndexedIndirectCommand));
context->mesh_data.mesh_bucket[mesh_id].is_dirty = true;
context->mesh_data.mesh_bucket[mesh_id].vertex_count = vertices_count;
context->mesh_data.mesh_bucket[mesh_id].entities_count = 0;
context->mesh_data.mesh_bucket[mesh_id].instances_count = 0;
context->mesh_data.mesh_bucket[mesh_id].index_count = indices_count;
return RSE_ERROR_NO_ERROR;
}
rse_err_t update_mesh_instances(struct graphics_context_t* context, uint16_t mesh_id,
struct instance_data_t* instance_data)
rse_err_t mesh_add_instance(struct graphics_context_t* context, uint32_t mesh_id, struct instance_data_t* instance_data)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkDrawIndexedIndirectCommand draw_indirect_command;
struct vulkan_buffer_t staging_buffer;
struct vulkan_buffer_t* instance_buffer = &context->instance_buffers[mesh_id];
size_t instance_size = sizeof(struct instance_data_t);
/* Creating staging buffer*/
STATUS_CHECK(create_buffer(
context, instance_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));
/* Copy transformation information about instance */
memcpy(staging_buffer.allocation_info.pMappedData, instance_data, instance_size);
copy_buffer(context, staging_buffer.buffer, instance_buffer->buffer, instance_size,
instance_buffer->allocated_size);
destroy_buffer(context, &staging_buffer);
instance_buffer->allocated_size += instance_size;
memcpy(&draw_indirect_command,
(char*)context->draw_indirect_command_buffer.allocation_info.pMappedData +
(mesh_id * sizeof(VkDrawIndexedIndirectCommand)),
sizeof(VkDrawIndexedIndirectCommand));
draw_indirect_command.instanceCount++;
memcpy((char*)context->draw_indirect_command_buffer.allocation_info.pMappedData +
(mesh_id * sizeof(VkDrawIndexedIndirectCommand)),
&draw_indirect_command, sizeof(VkDrawIndexedIndirectCommand));
/* 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;
bool dirty_found = false;
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 mesh_t* mesh = NULL;
uint32_t indices_count = 0U;
rse_malloc(draw_indirect_commands_buffer, draw_indirect_commands_buffer_size);
for (iter = 0U; iter < context->mesh_data.mesh_count; ++iter) {
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,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
&staging_buffer));
/* Copy command buffer information about instances */
memcpy(staging_buffer.allocation_info.pMappedData,
draw_indirect_commands_buffer,
draw_indirect_commands_buffer_size);
copy_buffer(context,
staging_buffer.buffer,
context->render_targets[RENDER_TARGET_3D].draw_indirect_command_buffer.buffer,
draw_indirect_commands_buffer_size,
0);
destroy_buffer(context, &staging_buffer);
}
/* Update instance data uniform buffer */
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);
return status;
}
/**
* @brief Records commands for provided swapchain framebuffer
*
@@ -305,18 +361,13 @@ rse_err_t update_mesh_instances(struct graphics_context_t* context, uint16_t mes
*/
rse_err_t record_command_buffer(struct graphics_context_t* context, uint32_t image_index)
{
VkCommandBuffer command_buffer = context->command_buffers[context->current_frame];
size_t iter = 0U;
VkCommandBuffer command_buffer = context->vulkan_handles.command_buffers[context->current_frame];
VkCommandBufferBeginInfo begin_info = {0};
VkRenderPassBeginInfo render_pass_info = {0};
VkViewport viewport = {0};
VkRect2D scissor = {0};
VkDeviceSize offsets[] = {0};
VkClearValue clear_values[2] = {0}; // For color and depth stencil
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
size_t i = 0U;
// FIXME: Do something with this list
// RSE_LINKED_LIST_TO_ARRAY(context->pipelines_data.descriptor_sets, descriptor_sets);
VkClearValue clear_values[2] = {0}; /* For color and depth stencil */
clear_values[0].color.float32[0] = 0.0f;
clear_values[0].color.float32[1] = 0.0f;
@@ -329,63 +380,43 @@ rse_err_t record_command_buffer(struct graphics_context_t* context, uint32_t ima
begin_info.flags = 0;
begin_info.pInheritanceInfo = NULL;
if (RSE_ERROR_NO_ERROR != descriptor_get_vkhandle(context->descriptor_sets, &descriptor_set)) {
LOGF(_("Failed to get vk handle from descriptor set handle\n"));
return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED;
}
if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
LOGF(_("Failed to start recording command buffer\n"));
return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to start recording command buffer");
return RSE_ERROR_INTERNAL_ERROR;
}
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_info.renderPass = context->render_pass;
render_pass_info.framebuffer = context->swapchain_framebuffers[image_index];
render_pass_info.renderPass = context->vulkan_handles.render_pass;
render_pass_info.framebuffer = context->swapchain_data.swapchain_framebuffers[image_index];
render_pass_info.renderArea.offset.x = 0;
render_pass_info.renderArea.offset.y = 0;
render_pass_info.renderArea.extent = context->swapchain_extent;
render_pass_info.renderArea.extent = context->swapchain_data.swapchain_extent;
render_pass_info.clearValueCount = 2;
render_pass_info.pClearValues = clear_values;
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.y = 0.0f;
viewport.width = (float)(context->swapchain_extent.width);
viewport.height = (float)(context->swapchain_extent.height);
viewport.width = (float)(context->swapchain_data.swapchain_extent.width);
viewport.height = (float)(context->swapchain_data.swapchain_extent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
scissor.offset.x = 0;
scissor.offset.y = 0;
scissor.extent = context->swapchain_extent;
scissor.extent = context->swapchain_data.swapchain_extent;
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
vkCmdBindVertexBuffers(command_buffer, 0, 1, &context->vertex_buffer.buffer, offsets);
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,
1, &descriptor_set, 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 */
for (iter = 0U; iter < context->pipelines_data.pipelines_count; ++iter) {
context->renderer_data.renderers[iter].render_function(context, iter);
}
vkCmdEndRenderPass(command_buffer);
if (VK_SUCCESS != vkEndCommandBuffer(command_buffer)) {
LOGF(_("Failed to record command buffer. Validate your commands.\n"));
return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to record command buffer. Validate your commands.");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
@@ -404,24 +435,27 @@ rse_err_t create_buffers(struct graphics_context_t* context)
void reset_command_buffer(struct graphics_context_t* context)
{
vkResetCommandBuffer(context->command_buffers[context->current_frame], /*VkCommandBufferResetFlagBits*/ 0);
vkResetCommandBuffer(context->vulkan_handles.command_buffers[context->current_frame],
/*VkCommandBufferResetFlagBits*/ 0);
}
void destroy_buffers(struct graphics_context_t* context)
{
size_t i = 0U;
size_t iter = 0U;
vmaDestroyBuffer(context->allocator, context->vertex_buffer.buffer, context->vertex_buffer.allocation);
vmaDestroyBuffer(context->allocator, context->index_buffer.buffer, context->index_buffer.allocation);
vmaDestroyBuffer(context->allocator, context->draw_indirect_command_buffer.buffer,
context->draw_indirect_command_buffer.allocation);
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].vertex_buffer);
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].index_buffer);
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].draw_indirect_command_buffer);
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].instance_buffer);
for (i = 0; i < MAX_MESH_NUMBER; ++i) {
vmaDestroyBuffer(context->allocator, context->instance_buffers[i].buffer,
context->instance_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);
}
RSE_LINKED_LIST_FOREACH(context->uniform_buffers, buffer) {
vmaDestroyBuffer(context->allocator, buffer->data.buffer, buffer->data.allocation);
context->uniform_buffers.vulkan_buffers_count = 0;
for (iter = 0; iter < context->mesh_data.mesh_count; ++iter) {
rse_free(context->mesh_data.mesh_bucket[iter].instances_data.data);
}
}
+22 -29
View File
@@ -1,14 +1,12 @@
#ifndef VULKAN_BUFFERS_H
#define VULKAN_BUFFERS_H
#include "vulkan_commons.h"
#include "utilities/commons.h"
#include "graphics_context.h"
#include <vma/vk_mem_alloc.h>
#include <stdint.h>
#include <vk_mem_alloc.h>
#include "graphics_context.h"
#include "utilities/commons.h"
#include "vulkan_commons.h"
/**
* @brief Create buffers needed by vulkan pipeline
@@ -25,30 +23,20 @@ rse_err_t create_buffers(struct graphics_context_t* context);
* @param buffer Buffer handle to hold new buffer
* @return rse_err_t RSE_ERROR_NO_ERROR on success.
*/
rse_err_t create_uniform_buffer(struct graphics_context_t* context, struct vulkan_buffer_t* buffer);
/**
* @brief Update uniform buffers
*
* @param context Graphics context handle
* @param buffer Uniform buffer to update
*/
void update_uniform_buffers(struct graphics_context_t* context, const struct vulkan_buffer_t* ubo);
rse_err_t buffer_create_uniform(struct graphics_context_t* context, struct vulkan_buffer_t* buffer,
VkDeviceSize buffer_size);
/**
* @brief Add vertices and indices to vulkan buffers
*
* @param vertices_count Number of provided vertices
* @param vertices Vertices to add
* @param indices_count Number of provided indices
* @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
*/
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 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);
/**
* @brief Add instance data to vulkan buffers
@@ -57,7 +45,8 @@ rse_err_t add_vertices(struct graphics_context_t* context, uint16_t mesh_id,
* @param instance_data Instance data
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
rse_err_t update_mesh_instances(struct graphics_context_t* context, uint16_t mesh_id, struct instance_data_t* instance_data);
rse_err_t mesh_add_instance(struct graphics_context_t* context, uint32_t mesh_id,
struct instance_data_t* instance_data);
/**
* @brief Record commands for given image index
@@ -82,7 +71,6 @@ void reset_command_buffer(struct graphics_context_t* context);
*/
void destroy_buffers(struct graphics_context_t* context);
/**
* @brief Helper function. Creates a buffer object
*
@@ -94,12 +82,17 @@ void destroy_buffers(struct graphics_context_t* context);
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED
*/
rse_err_t create_buffer(struct graphics_context_t* context, const VkDeviceSize size,
VkBufferUsageFlags buffer_usage,
VmaMemoryUsage memory_usage,
const VmaAllocationCreateFlags allocation_flags,
struct vulkan_buffer_t* buffer);
rse_err_t create_buffer(struct graphics_context_t* context, const VkDeviceSize size, VkBufferUsageFlags buffer_usage,
VmaMemoryUsage memory_usage, const VmaAllocationCreateFlags allocation_flags,
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
+47 -30
View File
@@ -1,31 +1,31 @@
#include "vulkan_commands.h"
#include <SDL3/SDL_log.h>
#include <vulkan/vulkan_core.h>
#include "src/graphics_context.h"
#include "utilities/commons.h"
#include "vulkan_commons.h"
#include "vulkan_errors.h"
#include "utilities/logger.h"
#include "utilities/errors_common.h"
#include "utilities/localization.h"
/**
* @brief Create Command Pools
* @brief Create Command Pool
*
* @param context Graphics context handle
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED
*/
static rse_err_t create_command_pools(struct graphics_context_t* context)
static rse_err_t create_command_pool(struct graphics_context_t* context)
{
VkCommandPoolCreateInfo create_info;
create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
create_info.pNext = NULL;
create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
create_info.queueFamilyIndex = context->queue_family_indices[0];
create_info.queueFamilyIndex = context->queue_family_indices[QUEUE_FAMILY_INDEX_GRAPHICS];
if (VK_SUCCESS != vkCreateCommandPool(context->device, &create_info, NULL, &context->command_pool)) {
LOGF(_("Failed to create command pool\n"));
return VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED;
if (VK_SUCCESS != vkCreateCommandPool(context->vulkan_handles.device, &create_info, NULL, &context->vulkan_handles.command_pool)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create command pool");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
@@ -45,28 +45,27 @@ static rse_err_t allocate_command_buffers(struct graphics_context_t* context)
/* Allocate memory for command buffers */
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocate_info.pNext = NULL;
allocate_info.commandPool = context->command_pool;
allocate_info.commandPool = context->vulkan_handles.command_pool;
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate_info.commandBufferCount = SWAP_BUFFER_COUNT;
if (VK_SUCCESS != vkAllocateCommandBuffers(context->device,
if (VK_SUCCESS != vkAllocateCommandBuffers(context->vulkan_handles.device,
&allocate_info,
context->command_buffers)) {
LOGF(_("Failed to allocate command buffers\n"));
return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED;
context->vulkan_handles.command_buffers)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to allocate command buffers\n");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
}
VkCommandBuffer begin_single_time_command(struct graphics_context_t* context)
rse_err_t begin_single_time_command(struct graphics_context_t* context, VkCommandBuffer* command_buffer)
{
VkCommandBuffer command_buffer = VK_NULL_HANDLE;
VkCommandBufferBeginInfo begin_info = {};
VkCommandBufferAllocateInfo alloc_info = {};
VkCommandBufferBeginInfo begin_info = {0};
VkCommandBufferAllocateInfo alloc_info = {0};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.pNext = NULL;
alloc_info.commandPool = context->command_pool;
alloc_info.commandPool = context->vulkan_handles.command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = 1;
@@ -75,15 +74,21 @@ VkCommandBuffer begin_single_time_command(struct graphics_context_t* context)
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
begin_info.pInheritanceInfo = NULL;
vkAllocateCommandBuffers(context->device, &alloc_info, &command_buffer);
vkBeginCommandBuffer(command_buffer, &begin_info);
if (vkAllocateCommandBuffers(context->vulkan_handles.device, &alloc_info, command_buffer) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to allocate new command buffer");
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 command_buffer;
return RSE_ERROR_NO_ERROR;
}
void end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer)
rse_err_t end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer)
{
VkSubmitInfo submit_info = {};
VkSubmitInfo submit_info = {0};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = NULL;
@@ -95,18 +100,30 @@ void end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer
submit_info.signalSemaphoreCount = 0;
submit_info.pSignalSemaphores = NULL;
vkEndCommandBuffer(command_buffer);
if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to end command buffer");
return RSE_ERROR_INTERNAL_ERROR;
}
vkQueueSubmit(context->graphics_queue, 1, &submit_info, VK_NULL_HANDLE);
vkQueueWaitIdle(context->graphics_queue);
if (vkQueueSubmit(context->vulkan_handles.graphics_queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed submit queue");
return RSE_ERROR_INTERNAL_ERROR;
}
vkFreeCommandBuffers(context->device, context->command_pool, 1, &command_buffer);
if (vkQueueWaitIdle(context->vulkan_handles.graphics_queue) != VK_SUCCESS) {
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 status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_command_pools(context));
STATUS_CHECK(create_command_pool(context));
STATUS_CHECK(allocate_command_buffers(context));
return status;
@@ -114,5 +131,5 @@ rse_err_t init_commands(struct graphics_context_t* context)
void destroy_commands(struct graphics_context_t* context)
{
vkDestroyCommandPool(context->device, context->command_pool, NULL);
vkDestroyCommandPool(context->vulkan_handles.device, context->vulkan_handles.command_pool, NULL);
}
+6 -4
View File
@@ -12,7 +12,8 @@
#ifndef VULKAN_COMMANDS_H
#define VULKAN_COMMANDS_H
#include <vma/vk_mem_alloc.h>
#include <vk_mem_alloc.h>
#include <vulkan/vulkan_core.h>
#include "utilities/commons.h"
#include "graphics_context.h"
@@ -37,16 +38,17 @@ void destroy_commands(struct graphics_context_t* context);
* @brief Helper function, creates command buffer for recording a command. Useful for copying buffer na images.
*
* @param context Graphics context handle
* @return VkCommandBuffer Created command buffer, ready to be filled with commands.
* @return RSE_ERROR_NO_ERROR on success
*/
VkCommandBuffer begin_single_time_command(struct graphics_context_t* context);
rse_err_t begin_single_time_command(struct graphics_context_t* context, VkCommandBuffer* command_buffer);
/**
* @brief Close command buffer and execute recorded command.
*
* @param context Graphics context handle
* @param command_buffer Command buffer for execution
* @return RSE_ERROR_NO_ERROR on success
*/
void end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer);
rse_err_t end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer);
#endif /* VULKAN_COMMANDS_H */
-24
View File
@@ -1,24 +0,0 @@
#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;
}
+14 -18
View File
@@ -12,10 +12,10 @@
#ifndef VULKAN_GLOBAL_H
#define VULKAN_GLOBAL_H
#include "math.h"
#include <cglm/types.h>
#include <vulkan/vulkan.h>
#include <vma/vk_mem_alloc.h>
#include <vk_mem_alloc.h>
#include <cglm/cglm.h>
#include <stdalign.h>
@@ -27,9 +27,9 @@
*/
struct vertex_t
{
struct vec3_t pos;
struct vec3_t color;
struct vec3_t tex_coords;
vec3 pos;
vec3 color;
vec2 tex_coords;
};
/**
@@ -44,22 +44,18 @@ struct vulkan_buffer_t
VmaAllocationInfo allocation_info;
};
struct uniform_buffer_object_t
struct mvp_data_t
{
alignas(16) struct mat4_t model;
alignas(16) struct mat4_t view;
alignas(16) struct mat4_t proj;
alignas(16) mat4 model;
alignas(16) mat4 view;
alignas(16) mat4 proj;
};
struct instance_data_t {
struct vec3_t pos;
struct vec3_t rot;
float scale;
uint32_t texture_id;
alignas(16) vec3 pos;
alignas(16) vec3 rot;
alignas(4) float scale;
alignas(4) uint32_t texture_id;
};
void init_time();
void update_time();
float get_time_diff();
#endif /* VULKAN_GLOBAL_H */
-64
View File
@@ -1,64 +0,0 @@
#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,
};
#endif
+262 -78
View File
@@ -1,21 +1,22 @@
#include "vulkan_image.h"
#include <SDL3/SDL_log.h>
#include <stdint.h>
#include "SDL3/SDL_video.h"
#include "src/graphics_context.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/localization.h"
#include "utilities/logger.h"
#include "utilities/file_utils.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_buffers.h"
#include "vulkan_commands.h"
#include "vulkan_errors.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define IMAGE_TAKEN 1U
@@ -44,23 +45,26 @@ rse_err_t sampler_create(struct graphics_context_t* context, VkSampler* sampler)
create_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
create_info.unnormalizedCoordinates = VK_FALSE;
if (VK_SUCCESS != vkCreateSampler(context->device, &create_info, NULL, sampler)) {
LOGF("Failed to create image view");
return 1;
if (VK_SUCCESS != vkCreateSampler(context->vulkan_handles.device, &create_info, NULL, sampler)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create image view");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
}
static rse_err_t find_supported_format(struct graphics_context_t* context, const VkFormat* candidates,
size_t candidates_count, VkImageTiling tiling, VkFormatFeatureFlags features,
static rse_err_t find_supported_format(struct graphics_context_t* context,
const VkFormat* candidates,
size_t candidates_count,
VkImageTiling tiling,
VkFormatFeatureFlags features,
VkFormat* found_format)
{
size_t i = 0U;
for (i = 0U; i < candidates_count; ++i) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(context->physical_device, candidates[i], &props);
vkGetPhysicalDeviceFormatProperties(context->vulkan_handles.physical_device, candidates[i], &props);
if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
*found_format = candidates[i];
@@ -71,14 +75,20 @@ static rse_err_t find_supported_format(struct graphics_context_t* context, const
}
}
LOGE(_("Failed to find correct format\n"));
return VULKAN_ERROR_FORMAT_NOT_FOUND;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to find correct format");
return RSE_ERROR_INTERNAL_ERROR;
}
static rse_err_t create_image(struct graphics_context_t* context, struct vulkan_image_t* image, uint32_t width,
uint32_t height, VkFormat format, VkSampleCountFlagBits maa_samples,
VkImageUsageFlags usage, VmaAllocationCreateFlags allocationFlags)
static rse_err_t create_image(struct graphics_context_t* context,
struct vulkan_image_t* image,
uint32_t width,
uint32_t height,
VkFormat format,
VkSampleCountFlagBits maa_samples,
VkImageUsageFlags usage,
VmaAllocationCreateFlags allocationFlags)
{
VkResult result;
VkExtent3D image_extent = {0};
VkImageCreateInfo image_info = {0};
VmaAllocationCreateInfo alloc_create_info = {0};
@@ -113,19 +123,23 @@ static rse_err_t create_image(struct graphics_context_t* context, struct vulkan_
alloc_create_info.pUserData = NULL;
alloc_create_info.priority = 1.0f;
if (VK_SUCCESS != vmaCreateImage(context->allocator, &image_info, &alloc_create_info, &image->image,
&image->allocation, &image->allocation_info)) {
LOGF(_("Failed to create image\n"));
return VULKAN_ERROR_IMAGE_NOT_CREATED;
result = vmaCreateImage(context->vulkan_handles.allocator,
&image_info,
&alloc_create_info,
&image->image,
&image->allocation,
&image->allocation_info);
if (VK_SUCCESS != result) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create image");
return RSE_ERROR_INTERNAL_ERROR;
}
static int count = 1;
LOGI("Created image: %d\n", count++);
return RSE_ERROR_NO_ERROR;
}
static rse_err_t create_image_view(struct graphics_context_t* context, struct vulkan_image_t* image, VkFormat format,
static rse_err_t create_image_view(struct graphics_context_t* context,
struct vulkan_image_t* image,
VkFormat format,
VkImageAspectFlags aspectFlags)
{
VkImageViewCreateInfo image_view_create_info = {0};
@@ -146,9 +160,10 @@ static rse_err_t create_image_view(struct graphics_context_t* context, struct vu
image_view_create_info.subresourceRange.baseArrayLayer = 0U;
image_view_create_info.subresourceRange.layerCount = 1U;
if (VK_SUCCESS != vkCreateImageView(context->device, &image_view_create_info, NULL, &image->image_view)) {
LOGF(_("Failed to create swapchain image view\n"));
return VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED;
if (VK_SUCCESS !=
vkCreateImageView(context->vulkan_handles.device, &image_view_create_info, NULL, &image->image_view)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create swapchain image view");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
}
@@ -157,15 +172,18 @@ rse_err_t create_color_resource(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkFormat color_format = IMAGE_FORMAT;
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
VkExtent2D extent = {0};
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->physical_device, context->surface,
&physical_device_surface_capabilities);
STATUS_CHECK(image_get_extent(context, &extent));
STATUS_CHECK(create_image(context, &context->color_image, physical_device_surface_capabilities.currentExtent.width,
physical_device_surface_capabilities.currentExtent.height, color_format,
STATUS_CHECK(create_image(context,
&context->color_image,
extent.width,
extent.height,
color_format,
VK_SAMPLE_COUNT_8_BIT,
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0U));
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
0U));
STATUS_CHECK(create_image_view(context, &context->color_image, color_format, VK_IMAGE_ASPECT_COLOR_BIT));
return status;
@@ -175,15 +193,20 @@ rse_err_t create_depth_resources(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkFormat depth_format = {0};
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
VkExtent2D extent = {0};
STATUS_CHECK(image_get_extent(context, &extent));
STATUS_CHECK(find_depth_format(context, &depth_format));
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->physical_device, context->surface,
&physical_device_surface_capabilities);
STATUS_CHECK(create_image(context, &context->depth_image, physical_device_surface_capabilities.currentExtent.width,
physical_device_surface_capabilities.currentExtent.height, depth_format,
VK_SAMPLE_COUNT_8_BIT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0U));
STATUS_CHECK(create_image(context,
&context->depth_image,
extent.width,
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));
return status;
@@ -207,21 +230,29 @@ static rse_err_t format_to_pixel_size(VkFormat format, VkDeviceSize* format_size
case VK_FORMAT_R8_UINT:
*format_size = 1;
break;
case VK_FORMAT_R8G8B8_UINT:
case VK_FORMAT_R8G8B8_UNORM:
*format_size = 3;
break;
case VK_FORMAT_R8G8B8A8_SRGB:
case VK_FORMAT_R8G8B8A8_UNORM:
*format_size = 4;
break;
default:
LOGE("Failed translate vulkan format to pixel width");
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed translate vulkan format to pixel width");
break;
}
return RSE_ERROR_NO_ERROR;
}
static rse_err_t transition_image_layout(struct graphics_context_t* context, struct vulkan_image_t* image,
VkImageLayout old_layout, VkImageLayout new_layout)
static rse_err_t transition_image_layout(struct graphics_context_t* context,
struct vulkan_image_t* image,
VkImageLayout old_layout,
VkImageLayout new_layout)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkImageSubresourceRange range = {0};
VkImageMemoryBarrier image_barrier = {0};
VkCommandBuffer command_buffer = {0};
@@ -259,18 +290,22 @@ static rse_err_t transition_image_layout(struct graphics_context_t* context, str
source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
return VULKAN_ERROR_TRANSITION_IMAGE_LAYOUT_FAILED;
return RSE_ERROR_INTERNAL_ERROR;
}
command_buffer = begin_single_time_command(context);
STATUS_CHECK(begin_single_time_command(context, &command_buffer));
vkCmdPipelineBarrier(command_buffer, source_stage, destination_stage, 0, 0, NULL, 0, NULL, 1, &image_barrier);
end_single_time_comands(context, command_buffer);
STATUS_CHECK(end_single_time_comands(context, command_buffer));
return RSE_ERROR_NO_ERROR;
}
static rse_err_t create_textured_image(struct graphics_context_t* context, uint32_t width, uint32_t height,
VkFormat format, const unsigned char* pixels, uint16_t* texture_id)
static rse_err_t create_textured_image(struct graphics_context_t* context,
uint32_t width,
uint32_t height,
VkFormat format,
const unsigned char* pixels,
rse_id_t* texture_id)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct vulkan_buffer_t staging_buffer = {0};
@@ -296,26 +331,38 @@ static rse_err_t create_textured_image(struct graphics_context_t* context, uint3
/* Check if we found free image handle */
if (free_texture_image == NULL) {
LOGE(_("Failed to find free texture image handle\n"));
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to find free texture image handle");
*texture_id = -1;
return VULKAN_ERROR_MAX_TEXTURE_COUNT_REACHED;
return RSE_ERROR_INTERNAL_ERROR;
}
STATUS_CHECK(format_to_pixel_size(format, &staging_buffer_size));
staging_buffer_size = staging_buffer_size * width * height;
STATUS_CHECK(create_image(context, 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));
STATUS_CHECK(
create_image(context,
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));
transition_image_layout(context, free_texture_image, VK_IMAGE_LAYOUT_UNDEFINED,
transition_image_layout(context,
free_texture_image,
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));
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);
@@ -331,12 +378,20 @@ static rse_err_t create_textured_image(struct graphics_context_t* context, uint3
copy_region.imageOffset.z = 0;
copy_region.imageExtent = image_extent;
command_buffer = begin_single_time_command(context);
vkCmdCopyBufferToImage(command_buffer, staging_buffer.buffer, free_texture_image->image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
STATUS_CHECK(begin_single_time_command(context, &command_buffer));
vkCmdCopyBufferToImage(command_buffer,
staging_buffer.buffer,
free_texture_image->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,
free_texture_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
STATUS_CHECK(create_image_view(context, free_texture_image, format, VK_IMAGE_ASPECT_COLOR_BIT));
free_texture_image->id_taken = IMAGE_TAKEN;
@@ -353,8 +408,107 @@ rse_err_t init_vulkan_images(struct graphics_context_t* context)
return status;
}
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)
rse_err_t image_get_extent(struct graphics_context_t* context, VkExtent2D* extent)
{
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;
@@ -364,56 +518,82 @@ rse_err_t load_texture_from_bitmat(struct graphics_context_t* context, const uns
return status;
}
rse_err_t load_texture_from_file(struct graphics_context_t* context, const char* file_path, uint16_t* texture_id)
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 status = RSE_ERROR_NO_ERROR;
int width = 0;
int height = 0;
int channels = 0;
stbi_uc* pixel_buffer = NULL;
pixel_buffer = stbi_load(file_path, &width, &height, &channels, STBI_rgb_alpha);
unsigned char* pixel_buffer = NULL;
rse_err_t status = RSE_ERROR_NO_ERROR;
pixel_buffer = stbi_load_from_memory(data_buffer, (int)buffer_size, &width, &height, &channels, STBI_rgb_alpha);
if (pixel_buffer == NULL) {
LOGE("Failed to load texture from file: %s", file_path);
return -1;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to load image");
return RSE_ERROR_INTERNAL_ERROR;
}
STATUS_CHECK(load_texture_from_bitmat(context, pixel_buffer, width, height, texture_id, VK_FORMAT_R8G8B8A8_SRGB));
if (load_texture_from_bitmat(context, pixel_buffer, width, height, texture_id, VK_FORMAT_R8G8B8A8_SRGB) != RSE_ERROR_NO_ERROR) {
status = RSE_ERROR_INTERNAL_ERROR;
goto mem_free;
}
mem_free:
stbi_image_free(pixel_buffer);
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)
{
size_t i = 0U;
for (i = 0U; i < RSE_MAX_IMAGE_COUNT; ++i) {
if (context->texture_images[i].id_taken == IMAGE_TAKEN) {
vkDestroyImageView(context->device, context->texture_images[i].image_view, NULL);
vmaDestroyImage(context->allocator, context->texture_images[i].image,
vkDestroyImageView(context->vulkan_handles.device, context->texture_images[i].image_view, NULL);
vmaDestroyImage(context->vulkan_handles.allocator,
context->texture_images[i].image,
context->texture_images[i].allocation);
}
}
vkDestroySampler(context->device, context->sampler, NULL);
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)
{
vkDestroyImageView(context->device, context->depth_image.image_view, NULL);
vmaDestroyImage(context->allocator, context->depth_image.image, context->depth_image.allocation);
vkDestroyImageView(context->vulkan_handles.device, context->depth_image.image_view, NULL);
vmaDestroyImage(context->vulkan_handles.allocator, context->depth_image.image, context->depth_image.allocation);
}
void destroy_color_resource(struct graphics_context_t* context)
{
vkDestroyImageView(context->device, context->color_image.image_view, NULL);
vmaDestroyImage(context->allocator, context->color_image.image, context->color_image.allocation);
vkDestroyImageView(context->vulkan_handles.device, context->color_image.image_view, NULL);
vmaDestroyImage(context->vulkan_handles.allocator, context->color_image.image, context->color_image.allocation);
}
uint8_t image_exists(struct graphics_context_t* context, uint8_t image_id)
uint8_t image_exists(struct graphics_context_t* context, rse_id_t image_id)
{
return context->texture_images[image_id].id_taken == IMAGE_TAKEN;
}
@@ -438,8 +618,12 @@ rse_err_t find_depth_format(struct graphics_context_t* context, VkFormat* found_
rse_err_t status;
VkFormat formats[formats_count] = {VK_FORMAT_D32_SFLOAT};
STATUS_CHECK(find_supported_format(context, formats, formats_count, VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, found_format));
STATUS_CHECK(find_supported_format(context,
formats,
formats_count,
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT,
found_format));
return status;
#undef formats_count
+22 -12
View File
@@ -29,16 +29,7 @@
*/
rse_err_t init_vulkan_images(struct graphics_context_t* context);
/**
* @brief Loads image from file, for later to be used as a texture
*
* @param context Graphics context handle
* @param file_path Path to texture file
* @param texture_id Texture ID will be set up here
* @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,
int height, uint16_t* texture_id, VkFormat color_format);
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
@@ -48,7 +39,26 @@ rse_err_t load_texture_from_bitmat(struct graphics_context_t* context, const uns
* @param texture_id Texture ID will be set up here
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
rse_err_t load_texture_from_file(struct graphics_context_t* context, const char* file_path, uint16_t* texture_id);
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 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
*
* @param context Graphics context handle
* @param file_path Path to texture file
* @param texture_id Texture ID will be set up here
* @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 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.
@@ -65,7 +75,7 @@ void destroy_textures(struct graphics_context_t* context);
* @param image_id Image ID
* @return uint8_t 1 on success, 0 on failure
*/
uint8_t image_exists(struct graphics_context_t* context, uint8_t image_id);
uint8_t image_exists(struct graphics_context_t* context, rse_id_t image_id);
/**
* @brief Returns number of loaded texture
+5 -8
View File
@@ -12,10 +12,7 @@
#include "vulkan_render_pass.h"
#include "vulkan_image.h"
#include "vulkan_errors.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)
{
@@ -100,10 +97,10 @@ rse_err_t create_render_pass(struct graphics_context_t* context)
renderpass_info.dependencyCount = 1;
renderpass_info.pDependencies = &dependency;
if (VK_SUCCESS != vkCreateRenderPass(context->device, &renderpass_info,
NULL, &context->render_pass)) {
LOGF(_("Failed to create render pass\n"));
return VULKAN_ERROR_RENDERPASS_CREATION_FAILED;
if (VK_SUCCESS != vkCreateRenderPass(context->vulkan_handles.device, &renderpass_info,
NULL, &context->vulkan_handles.render_pass)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create render pass");
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
@@ -111,5 +108,5 @@ rse_err_t create_render_pass(struct graphics_context_t* context)
void destroy_render_pass(struct graphics_context_t* context)
{
vkDestroyRenderPass(context->device, context->render_pass, NULL);
vkDestroyRenderPass(context->vulkan_handles.device, context->vulkan_handles.render_pass, NULL);
}
+55 -59
View File
@@ -1,16 +1,15 @@
#include "vulkan_swapchain.h"
#include <SDL3/SDL_events.h>
#include <stdlib.h>
#include <string.h>
#include "src/graphics_context.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/logger.h"
#include "utilities/localization.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_commons.h"
#include "vulkan_errors.h"
#include "vulkan_image.h"
#include "vulkan_swapchain.h"
/**
* @brief Create a Swapchain
@@ -22,56 +21,60 @@ static rse_err_t create_swapchain(struct graphics_context_t* context)
{
VkSwapchainCreateInfoKHR create_info;
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
rse_err_t status = RSE_ERROR_NO_ERROR;
VkBool32 surfaceSupported;
VkExtent2D extent = {0};
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
context->physical_device, context->surface,
&physical_device_surface_capabilities);
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->vulkan_handles.physical_device,
context->vulkan_handles.surface,
&physical_device_surface_capabilities);
STATUS_CHECK(image_get_extent(context, &extent));
/* Store extent in global variable, for later use */
context->swapchain_extent =
physical_device_surface_capabilities.currentExtent;
context->swapchain_data.swapchain_extent = extent;
/* Check if device supports surface for presentation */
vkGetPhysicalDeviceSurfaceSupportKHR(context->physical_device,
vkGetPhysicalDeviceSurfaceSupportKHR(context->vulkan_handles.physical_device,
context->queue_family_indices[0],
context->surface, &surfaceSupported);
context->vulkan_handles.surface,
&surfaceSupported);
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
create_info.pNext = NULL;
create_info.flags = 0U;
create_info.surface = context->surface;
create_info.surface = context->vulkan_handles.surface;
create_info.minImageCount = SWAP_BUFFER_COUNT;
create_info.imageFormat = IMAGE_FORMAT;
create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
create_info.imageExtent = context->swapchain_extent;
create_info.imageArrayLayers =
1U; /* For non-stereoscopic-3D applications, this value is 1. */
create_info.imageExtent = context->swapchain_data.swapchain_extent;
create_info.imageArrayLayers = 1U; /* For non-stereoscopic-3D applications, this value is 1. */
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.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.presentMode = VK_PRESENT_MODE_FIFO_KHR;
create_info.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
create_info.clipped = VK_TRUE;
create_info.oldSwapchain = VK_NULL_HANDLE;
if (VK_SUCCESS != vkCreateSwapchainKHR(context->device, &create_info, NULL,
&context->swapchain)) {
LOGF(_("Failed to create swapchain\n"));
return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED;
if (VK_SUCCESS !=
vkCreateSwapchainKHR(context->vulkan_handles.device, &create_info, NULL, &context->swapchain_data.swapchain)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create swapchain");
return RSE_ERROR_INTERNAL_ERROR;
}
/* Obtain swapchain images */
vkGetSwapchainImagesKHR(context->device, context->swapchain,
&context->swapchain_images_count, NULL);
rse_malloc(context->swapchain_images, sizeof(VkImage) * context->swapchain_images_count);
vkGetSwapchainImagesKHR(context->device, context->swapchain,
&context->swapchain_images_count, context->swapchain_images);
vkGetSwapchainImagesKHR(context->vulkan_handles.device,
context->swapchain_data.swapchain,
&context->swapchain_images_count,
NULL);
vkGetSwapchainImagesKHR(context->vulkan_handles.device,
context->swapchain_data.swapchain,
&context->swapchain_images_count,
context->swapchain_data.swapchain_images);
return RSE_ERROR_NO_ERROR;
}
@@ -83,15 +86,13 @@ static rse_err_t create_swapchain(struct graphics_context_t* context)
*/
static rse_err_t create_swapchain_image_views(struct graphics_context_t* context)
{
rse_malloc(context->swapchain_image_views,
sizeof(VkImageView) * context->swapchain_images_count);
for (size_t i = 0; i < context->swapchain_images_count; i++) {
for (size_t i = 0; i < SWAPCHAIN_IMAGE_COUNT; i++) {
VkImageViewCreateInfo create_info;
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.pNext = NULL;
create_info.flags = 0U;
create_info.image = context->swapchain_images[i];
create_info.image = context->swapchain_data.swapchain_images[i];
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */
create_info.format = VK_FORMAT_B8G8R8A8_SRGB;
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
@@ -104,11 +105,12 @@ static rse_err_t create_swapchain_image_views(struct graphics_context_t* context
create_info.subresourceRange.baseArrayLayer = 0U;
create_info.subresourceRange.layerCount = 1U;
if (VK_SUCCESS !=
vkCreateImageView(context->device, &create_info, NULL,
&context->swapchain_image_views[i])) {
LOGF(_("Failed to create swapchain image view\n"));
return VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED;
if (VK_SUCCESS != vkCreateImageView(context->vulkan_handles.device,
&create_info,
NULL,
&context->swapchain_data.swapchain_image_views[i])) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create swapchain image view");
return RSE_ERROR_INTERNAL_ERROR;
}
}
@@ -122,30 +124,26 @@ static rse_err_t create_swapchain_image_views(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++) {
VkImageView attachments[] = {context->color_image.image_view,
context->depth_image.image_view,
context->swapchain_image_views[i]};
context->swapchain_data.swapchain_image_views[i]};
VkFramebufferCreateInfo framebufferInfo = {0};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = context->render_pass;
framebufferInfo.renderPass = context->vulkan_handles.render_pass;
framebufferInfo.attachmentCount = 3;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = context->swapchain_extent.width;
framebufferInfo.height = context->swapchain_extent.height;
framebufferInfo.width = context->swapchain_data.swapchain_extent.width;
framebufferInfo.height = context->swapchain_data.swapchain_extent.height;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer(context->device, &framebufferInfo, NULL,
&context->swapchain_framebuffers[i]) !=
VK_SUCCESS) {
LOGF(_("Failed to create framebuffer\n"));
return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED;
if (vkCreateFramebuffer(context->vulkan_handles.device,
&framebufferInfo,
NULL,
&context->swapchain_data.swapchain_framebuffers[i]) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create framebuffer");
return RSE_ERROR_INTERNAL_ERROR;
}
}
@@ -168,7 +166,7 @@ void recreate_swapchain(struct graphics_context_t* context)
SDL_WaitEvent(&event);
}
vkDeviceWaitIdle(context->device);
vkDeviceWaitIdle(context->vulkan_handles.device);
cleanup_swapchain(context);
@@ -190,16 +188,14 @@ void cleanup_swapchain(struct graphics_context_t* context)
destroy_color_resource(context);
destroy_depth_resource(context);
for (i = 0; i < context->swapchain_images_count; i++) {
vkDestroyFramebuffer(context->device,
context->swapchain_framebuffers[i], NULL);
vkDestroyFramebuffer(context->vulkan_handles.device, context->swapchain_data.swapchain_framebuffers[i], NULL);
}
for (i = 0; i < context->swapchain_images_count; i++) {
vkDestroyImageView(context->device, context->swapchain_image_views[i],
NULL);
vkDestroyImageView(context->vulkan_handles.device, context->swapchain_data.swapchain_image_views[i], NULL);
}
vkDestroySwapchainKHR(context->device, context->swapchain, NULL);
vkDestroySwapchainKHR(context->vulkan_handles.device, context->swapchain_data.swapchain, NULL);
}
rse_err_t create_swapchain_and_framebuffers(struct graphics_context_t* context)
+10 -28
View File
@@ -10,53 +10,35 @@
*/
#include "window.h"
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_timer.h>
#include <SDL3/SDL_video.h>
#include <stdint.h>
#include <stdio.h>
#include "SDL3/SDL_log.h"
#include "utilities/commons.h"
#include "utilities/localization.h"
#include "window_errors.h"
#include "utilities/logger.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)
rse_err_t window_init(SDL_Window** window_handle, bool* is_framebuffer_resized)
{
SDL_Init(SDL_INIT_VIDEO); //TODO: Move to initial phase of RSE initialization
(void)is_framebuffer_resized;
// TODO: Actually use this parameter?
*window_handle = SDL_CreateWindow("RedScarfEngine", 1024, 768, SDL_WINDOW_VULKAN);
if (NULL == *window_handle) {
LOGF(_("Window context creation failed\n"));
return WINDOW_ERROR_WINDOW_NOT_CREATED;
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Window context creation failed. Reason: %s", SDL_GetError());
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t window_loop(struct graphics_context_t* context)
{
uint8_t done = 0;
SDL_Event event;
while (0 == done) {
while (SDL_PollEvent(&event)) {
if (SDL_EVENT_QUIT == event.type) {
done = 1;
}
}
draw_frame(context);
}
vkDeviceWaitIdle(context->device);
return RSE_ERROR_NO_ERROR;
}
void window_terminate(SDL_Window* window_handle)
{
SDL_DestroyWindow(window_handle);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
-8
View File
@@ -32,14 +32,6 @@
*/
rse_err_t window_init(SDL_Window **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
*
-24
View File
@@ -1,24 +0,0 @@
/**
* @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
@@ -0,0 +1,13 @@
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
@@ -1,85 +0,0 @@
#!/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
@@ -1,10 +0,0 @@
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
@@ -0,0 +1,23 @@
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
@@ -0,0 +1,22 @@
/**
* @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_ */
+5 -19
View File
@@ -1,30 +1,16 @@
#include <SDL3/SDL_init.h>
#include "graphics/rse_graphics.h"
#include "red_scarf_engine.h"
#include "utilities/errors_common.h"
#include "utilities/logger.h"
#include "SDL3/SDL_thread.h"
int main(int argc, char** argv)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct rse_graphics_context_t* context = NULL;
SDL_Thread* graphics_task = NULL;
rse_context context;
(void)argc;
(void)argv;
STATUS_CHECK(rse_graphics_init(&context));
rse_graphics_test_function(context);
graphics_task = SDL_CreateThread(rse_graphics_run, "graphics_task", (void*) context);
if (NULL == graphics_task) {
LOGE(_("Failed to create graphics task"));
exit(1);
}
SDL_WaitThread(graphics_task, NULL);
SDL_Quit();
STATUS_CHECK(rse_init(&context));
STATUS_CHECK(rse_run(context));
return 0;
}
+71
View File
@@ -0,0 +1,71 @@
#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;
}
-4
View File
@@ -1,4 +0,0 @@
#!/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
@@ -0,0 +1,10 @@
[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
@@ -0,0 +1,7 @@
project('stb', 'c')
stb_inc = include_directories('.')
stb_dep = declare_dependency(
include_directories: stb_inc,
)
+14
View File
@@ -0,0 +1,14 @@
[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
@@ -0,0 +1,5 @@
[wrap-git]
url = https://github.com/nothings/stb.git
revision = HEAD
depth = 1
patch_directory = stb/
+14
View File
@@ -0,0 +1,14 @@
[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
@@ -0,0 +1,14 @@
[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
@@ -0,0 +1,9 @@
[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.

After

Width:  |  Height:  |  Size: 713 KiB

-51
View File
@@ -1,51 +0,0 @@
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
@@ -1,27 +0,0 @@
/**
* @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
@@ -1,44 +0,0 @@
#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
@@ -1,15 +0,0 @@
#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
@@ -1,12 +0,0 @@
#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
@@ -1,71 +0,0 @@
#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
@@ -1,61 +0,0 @@
/**
* @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
@@ -1,17 +0,0 @@
/**
* @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
@@ -1,60 +0,0 @@
#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
@@ -1,17 +0,0 @@
/**
* @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
@@ -1,231 +0,0 @@
/**
* @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
@@ -1,12 +0,0 @@
/**
* @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);
File diff suppressed because it is too large. Load diff
-17
View File
@@ -1,17 +0,0 @@
find_package(Intl)
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
)
target_link_libraries(utilities PRIVATE
Intl::Intl)
+7240
View File
File diff suppressed because it is too large. Load diff
+14 -9
View File
@@ -12,8 +12,7 @@
#ifndef COMMONS_H
#define COMMONS_H
#include "utilities/logger.h"
#include <SDL3/SDL_log.h>
#include <stdint.h>
#include <stdlib.h>
@@ -22,14 +21,14 @@
*
*/
#ifndef RSE_TEST
#define rse_malloc(ptr, size) \
ptr = malloc(size); \
if (ptr == NULL) { \
LOGF(_("Memory allocation failed\n")); \
exit(1); \
}
#define rse_malloc(ptr, size) \
ptr = calloc(1, size); \
if (ptr == NULL) { \
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Memory allocation failure"); \
exit(1); \
}
#define rse_free(ptr) free(ptr);
#define rse_free(ptr) if (ptr != NULL) free(ptr);
#define rse_memcpy(dst, src, size) memcpy(dst, src, size);
#define rse_memset(ptr, value, size) memset(ptr, value, size);
@@ -46,4 +45,10 @@ if (ptr == NULL) { \
*/
typedef uint32_t rse_err_t;
/**
* @brief General type for ID use
*
*/
typedef uint32_t rse_id_t;
#endif /* COMMONS_H */
+26
View File
@@ -0,0 +1,26 @@
/**
* @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
+9 -4
View File
@@ -2,7 +2,10 @@
#define FILE_UTILS_H
#include <stddef.h>
#include "utilities/commons.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
@@ -12,11 +15,13 @@
* @param bytes_count Returned bytes count
* @param buffer Returned filled buffer
*/
void read_file(const char* file_path, size_t* bytes_count, char* buffer);
rse_err_t file_read_bytes(const char* file_path, size_t* bytes_count, unsigned char* buffer);
void write_to_file(const char* file_name, size_t bytes_count, char* buffer);
rse_err_t file_write(const char* file_name, size_t bytes_count, char* buffer);
rse_err_t get_curr_path(char* path, size_t max_path_length);
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 */
-128
View File
@@ -1,128 +0,0 @@
/**
* @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
@@ -1,31 +0,0 @@
/**
* @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
@@ -1,20 +0,0 @@
/**
* @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 */
-46
View File
@@ -1,46 +0,0 @@
/**
* @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 "utilities/localization.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
@@ -0,0 +1,25 @@
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
@@ -0,0 +1,34 @@
/**
* @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;
}
+147 -60
View File
@@ -1,88 +1,51 @@
#include "utilities/file_utils.h"
#include "file_utils.h"
#define CGLTF_IMPLEMENTATION
#include "cgltf.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/localization.h"
#include "utilities/logger.h"
#include "SDL3/SDL_log.h"
#include "commons.h"
#include "errors_common.h"
#ifdef __linux__
#include <unistd.h>
#include <limits.h>
#include <unistd.h>
#endif /* ifndef __linux */
#define MAX_PATH_LENGTH (4096U)
void read_file(const char* file_name, size_t* bytes_count, char* buffer)
{
FILE* file = NULL;
file = fopen(file_name, "rb");
if (NULL == file) {
LOGE(_("Failed to open file for read: %s\n"), file_name);
return;
}
fseek(file, 0, SEEK_END);
*bytes_count = ftell(file);
rewind(file);
if (buffer == NULL) {
fclose(file);
return;
}
fread(buffer, *bytes_count, 1, file);
if (ferror(file) != 0) {
perror("Error reading file");
}
fclose(file);
}
void write_to_file(const char* file_name, size_t bytes_count, char* buffer)
{
FILE* file = NULL;
file = fopen(file_name, "wb");
if (NULL == file) {
LOGE(_("Failed to open file for write: %s\n"), file_name);
return;
}
fwrite(buffer, bytes_count, 1, file);
fclose(file);
}
rse_err_t get_curr_path(char* path, size_t path_size)
static rse_err_t get_curr_path(char* path)
{
char* last_sep = NULL;
char* tmp_path = NULL;
size_t binary_path_length = 0U;
size_t path_length = 0U;
ssize_t binary_path_length = 0U;
size_t directory_path_length = 0U;
if (NULL == path) {
LOGE(_("Failed to get path. Provided pointer is NULL\n"));
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to get path. Provided pointer is NULL");
return RSE_ERROR_NULL_POINTER;
}
if (0 == path_size) {
LOGE(_("Max path length cannot be 0\n"));
path_length = strlen(path);
if (path_length > MAX_PATH_LENGTH) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Max path length cannot be 0");
return RSE_ERROR_INVALID_PARAM;
}
rse_memset(path, '\0', path_size);
rse_memset(path, '\0', path_length);
rse_malloc(tmp_path, MAX_PATH_LENGTH);
#ifdef __linux__
binary_path_length = readlink("/proc/self/exe", tmp_path, MAX_PATH_LENGTH - 1);
if (binary_path_length == -1) {
LOGF(_("Failed to read current binary path\n"));
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to read current binary path");
return RSE_ERROR_NULL_POINTER;
}
@@ -93,15 +56,15 @@ rse_err_t get_curr_path(char* path, size_t path_size)
last_sep = strrchr(tmp_path, '/') + 1;
if (NULL == last_sep) {
LOGE(_("Failed to get last separator in path\n"));
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to get last separator in path");
return RSE_ERROR_INTERNAL_ERROR;
}
directory_path_length = last_sep - tmp_path;
if (directory_path_length > path_size) {
LOGE(_("Provided path buffer is too small\n"));
if (directory_path_length > MAX_PATH_LENGTH) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided path buffer is too small");
return RSE_ERROR_INTERNAL_ERROR;
}
@@ -112,3 +75,127 @@ rse_err_t get_curr_path(char* path, size_t path_size)
return RSE_ERROR_NO_ERROR;
}
rse_err_t file_append_full_path(char* file_path, size_t max_buffer_size)
{
rse_err_t ret = RSE_ERROR_NO_ERROR;
size_t curr_path_length = 0U;
size_t file_path_length = 0U;
char curr_path[MAX_PATH_LENGTH] = {0};
char temp_path[MAX_PATH_LENGTH] = {0};
file_path_length = strlen(file_path);
if (file_path_length > max_buffer_size) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Provided max_buffer_size is smaller than the file path itself");
return RSE_ERROR_INVALID_PARAM;
}
if ((ret = get_curr_path(curr_path)) != RSE_ERROR_NO_ERROR) {
return ret;
}
curr_path_length = strlen(curr_path);
if ((curr_path_length + file_path_length) > max_buffer_size) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION,
"Provided max_buffer_size is to small to hold concatenated file path. %ld vs %ld",
(curr_path_length + file_path_length),
max_buffer_size);
}
strncpy(temp_path, file_path, MAX_PATH_LENGTH);
memset(file_path, 0, max_buffer_size);
strncat(file_path, curr_path, max_buffer_size);
strncat(file_path, temp_path, max_buffer_size);
return ret;
}
rse_err_t file_read_bytes(const char* file_name, size_t* bytes_count, unsigned char* buffer)
{
rse_err_t ret = RSE_ERROR_NO_ERROR;
FILE* file = NULL;
char real_path[MAX_PATH_LENGTH] = {0};
strncpy(real_path, file_name, MAX_PATH_LENGTH);
if ((ret = file_append_full_path(real_path, MAX_PATH_LENGTH)) != RSE_ERROR_NO_ERROR) {
return ret;
}
file = fopen(real_path, "rb");
if (file == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to open file for read: %s", real_path);
ret = RSE_ERROR_INTERNAL_ERROR;
goto file_close;
}
fseek(file, 0, SEEK_END);
*bytes_count = ftell(file);
fseek(file, 0, SEEK_SET);
if (buffer == NULL) {
goto file_close;
}
fread(buffer, *bytes_count, 1, file);
if (ferror(file) != 0) {
perror("Error reading file");
ret = RSE_ERROR_INTERNAL_ERROR;
}
file_close:
fclose(file);
return ret;
}
rse_err_t file_write(const char* file_name, size_t bytes_count, char* buffer)
{
FILE* file = NULL;
char real_path[MAX_PATH_LENGTH] = {0};
rse_err_t ret = RSE_ERROR_NO_ERROR;
strncpy(real_path, file_name, MAX_PATH_LENGTH);
if ((ret = file_append_full_path(real_path, MAX_PATH_LENGTH)) != RSE_ERROR_NO_ERROR) {
return ret;
}
file = fopen(real_path, "wb");
if (file == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to open file for write: %s", file_name);
return RSE_ERROR_INTERNAL_ERROR;
}
fwrite(buffer, bytes_count, 1, file);
fclose(file);
return ret;
}
rse_err_t file_load_gltf(const char* file_name, cgltf_data** data)
{
char real_path[MAX_PATH_LENGTH] = {0};
rse_err_t ret = RSE_ERROR_NO_ERROR;
cgltf_options options = {0};
strncpy(real_path, file_name, MAX_PATH_LENGTH);
if ((ret = file_append_full_path(real_path, MAX_PATH_LENGTH)) != RSE_ERROR_NO_ERROR) {
return ret;
}
if (cgltf_parse_file(&options, real_path, data) != cgltf_result_success) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to parse GLTF data");
return RSE_ERROR_INTERNAL_ERROR;
}
return ret;
}
-26
View File
@@ -1,26 +0,0 @@
#include "utilities/logger.h"
#include "stdarg.h"
const char* log_levels[] = {
"[TRACE]", "[DEBUG]", "[INFO]", "WARNING", "[ERROR]", "[FATAL]"
};
#ifndef LOGLEVEL
#define LOGLEVEL LOGLEVEL_TRACE
#endif
void _log_log(unsigned int level, const char* file, int line, const char* fmt, ...)
{
va_list list;
if (level < LOGLEVEL)
{
return;
}
va_start(list, fmt);
fprintf(stdout, "%s %s:%d: ", log_levels[level], file, line);
vfprintf(stdout, fmt, list);
fprintf(stdout, "\n");
fflush(stdout);
}
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more