Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d2a2ddde3 | ||
|
|
454aaba349 | ||
|
|
5f759a9dbd | ||
|
|
5ad0ac7fed | ||
|
|
1b5cc7a79c | ||
|
|
393b59ad5d | ||
|
|
9cb692344f | ||
|
|
bd05c03a12 | ||
|
|
45be08019e | ||
|
|
e80faf80b2 | ||
|
|
daed76f02f | ||
|
|
f8eb98ddf4 | ||
|
|
173a82b9d1 | ||
|
|
f5ab5f3f27 | ||
|
|
56c0b7922f | ||
|
|
3c72c9b387 | ||
|
|
f197280e88 | ||
|
|
ad90e5897a | ||
|
|
c9384eb795 | ||
|
|
fb3151c5d1 | ||
|
|
d59d8c640e | ||
|
|
c1a8b14a90 | ||
|
|
99d7546e3d | ||
|
|
b2508ce4ff | ||
|
|
3bd14b20c2 | ||
|
|
2d230d7280 | ||
|
|
3c73e6bac3 | ||
|
|
1f585b4d8e | ||
|
|
42dc4d6fcd | ||
|
|
bd4dd74881 | ||
|
|
d328e38761 | ||
|
|
548ed57969 | ||
|
|
4fcda01ba2 | ||
|
|
0e040dd0d5 | ||
|
|
252a0820ed | ||
|
|
3a58bd8756 | ||
|
|
7ca73f3c60 | ||
|
|
940bc7e512 | ||
|
|
0ef475e57e | ||
|
|
863f502dde | ||
|
|
507db80fb1 | ||
|
|
3d538eaa2c | ||
|
|
bf31b96a2e | ||
|
|
c22569cc48 | ||
|
|
a82550d414 | ||
|
|
320c9293b1 | ||
|
|
6781c1cff1 | ||
|
|
e06288320d | ||
|
|
c4734e99c2 | ||
|
|
6a09350902 | ||
|
|
649e7f76fe | ||
|
|
0c45da5cc4 | ||
|
|
d0ac0f0e97 | ||
|
|
a175045518 | ||
|
|
cce4875329 | ||
|
|
a94e7a95f5 | ||
|
|
f8b32717f3 | ||
|
|
f4a6260a5e | ||
|
|
34665e1c2e | ||
|
|
e7b9d4a32f | ||
|
|
ce0c399810 | ||
|
|
4755371ffa |
No files matched your search
@@ -30,11 +30,3 @@ 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
|
||||
|
||||
@@ -8,5 +8,6 @@ CompileFlags:
|
||||
"-I./",
|
||||
"-I./graphics",
|
||||
"-I./utilites",
|
||||
"-I./third_party/include",
|
||||
]
|
||||
|
||||
@@ -50,9 +50,4 @@ CMakeFiles/
|
||||
# Clang specific files
|
||||
.clangd
|
||||
.clangd/
|
||||
compile_commands.json
|
||||
|
||||
# Ignore subproject path except config files
|
||||
subprojects/*
|
||||
!subprojects/packagefiles/
|
||||
!subprojects/*.wrap
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
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)
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": 4,
|
||||
"vendor": {
|
||||
"conan": {}
|
||||
},
|
||||
"include": [
|
||||
"build/CMakePresets.json"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -2,3 +2,13 @@
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,14 @@
|
||||
[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
|
||||
@@ -1,18 +0,0 @@
|
||||
rse_events_sources = [
|
||||
'src/events_manager.c'
|
||||
]
|
||||
|
||||
sdl3_dep = dependency('sdl3',
|
||||
version : '>=3.4.0')
|
||||
|
||||
rse_events_lib = shared_library(
|
||||
'rse_events',
|
||||
rse_events_sources,
|
||||
include_directories : [
|
||||
'../'
|
||||
],
|
||||
dependencies : [sdl3_dep,
|
||||
],
|
||||
link_with: rse_utilities_lib,
|
||||
install : true
|
||||
)
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* @file events_manager.h
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @brief Events manager for Red Scarf Engine
|
||||
* @version 0.1
|
||||
* @date 2026-03-02
|
||||
*
|
||||
* @copyright Copyright (c) 2026
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RSE_EVENTS_MANAGER_H_
|
||||
#define RSE_EVENTS_MANAGER_H_
|
||||
|
||||
#include "src/events_context.h"
|
||||
#include "utilities/commons.h"
|
||||
|
||||
rse_err_t rse_events_init(struct events_context_t* events_context);
|
||||
|
||||
rse_err_t rse_events_set_event_handler(struct events_context_t* events_context,
|
||||
enum EVENT_HANDLER event_type,
|
||||
rse_event_handler_t event_handler);
|
||||
|
||||
rse_err_t rse_events_main_loop(struct events_context_t* context);
|
||||
|
||||
#endif /* RSE_EVENTS_MANAGER_H_ */
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* @file events_context.h
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @brief Events context for Red Scarf Engine
|
||||
* @version 0.1
|
||||
* @date 2026-03-02
|
||||
*
|
||||
* @copyright Copyright (c) 2026
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EVENT_CONTEXT_H_
|
||||
#define EVENT_CONTEXT_H_
|
||||
|
||||
#include "SDL3/SDL_mouse.h"
|
||||
#include "utilities/commons.h"
|
||||
|
||||
enum EVENT_HANDLER {
|
||||
EVENT_HANDLER_QUIT,
|
||||
EVENT_HANDLER_MOUSE,
|
||||
EVENT_HANDLER_LAST
|
||||
};
|
||||
|
||||
enum EVENT_SHOULD_STOP_ENGINE {
|
||||
EVENT_SHOULD_STOP_ENGINE_FALSE,
|
||||
EVENT_SHOULD_STOP_ENGINE_TRUE
|
||||
};
|
||||
|
||||
struct mouse_event_data_t
|
||||
{
|
||||
float x_pos;
|
||||
float y_pos;
|
||||
SDL_MouseButtonFlags mouse_buttons;
|
||||
};
|
||||
|
||||
typedef rse_err_t (*rse_event_handler_t)(void*);
|
||||
|
||||
struct events_context_t
|
||||
{
|
||||
enum EVENT_SHOULD_STOP_ENGINE should_stop;
|
||||
rse_event_handler_t event_handlers[EVENT_HANDLER_LAST];
|
||||
};
|
||||
|
||||
#endif /* EVENT_CONTEXT_H_ */
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* @file events_manager.c
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @brief Events manager for Red Scarf Engine
|
||||
* @version 0.1
|
||||
* @date 2026-03-02
|
||||
*
|
||||
* @copyright Copyright (c) 2026
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_log.h"
|
||||
#include "SDL3/SDL_mouse.h"
|
||||
#include "events_context.h"
|
||||
#include "rse_events_manager.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "utilities/errors_common.h"
|
||||
|
||||
static rse_err_t default_event(void* args)
|
||||
{
|
||||
(void)args;
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
static rse_err_t run_custom_event_handler(struct events_context_t* context,
|
||||
enum EVENT_HANDLER event_handler,
|
||||
void* args)
|
||||
{
|
||||
return context->event_handlers[event_handler](args);
|
||||
}
|
||||
|
||||
static rse_err_t quit_event(struct events_context_t* context)
|
||||
{
|
||||
context->should_stop = EVENT_SHOULD_STOP_ENGINE_TRUE;
|
||||
|
||||
return run_custom_event_handler(context, EVENT_HANDLER_QUIT, NULL);
|
||||
}
|
||||
|
||||
static rse_err_t mouse_event(struct events_context_t* context)
|
||||
{
|
||||
struct mouse_event_data_t mouse_data = {0};
|
||||
|
||||
mouse_data.mouse_buttons = SDL_GetMouseState(&mouse_data.x_pos, &mouse_data.y_pos);
|
||||
|
||||
return run_custom_event_handler(context, EVENT_HANDLER_MOUSE, (void*)&mouse_data);
|
||||
}
|
||||
|
||||
rse_err_t rse_events_init(struct events_context_t* events_context)
|
||||
{
|
||||
size_t i = 0U;
|
||||
|
||||
for (i = 0U; i < EVENT_HANDLER_LAST; ++i) {
|
||||
events_context->event_handlers[i] = default_event;
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t rse_events_set_event_handler(struct events_context_t* events_context,
|
||||
enum EVENT_HANDLER event_type,
|
||||
rse_event_handler_t event_handler)
|
||||
{
|
||||
if (event_type >= EVENT_HANDLER_LAST) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Invalid event type selected");
|
||||
|
||||
return RSE_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (event_handler == NULL) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Provided NULL event handler");
|
||||
|
||||
return RSE_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
events_context->event_handlers[event_type] = event_handler;
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t rse_events_main_loop(struct events_context_t* context)
|
||||
{
|
||||
SDL_Event event;
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
STATUS_CHECK(quit_event(context));
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
STATUS_CHECK(mouse_event(context));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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
|
||||
)
|
||||
@@ -1,58 +0,0 @@
|
||||
rse_graphics_sources = [
|
||||
'src/descriptor_builder.c',
|
||||
'src/font_manager.c',
|
||||
'src/mesh_controller.c',
|
||||
'src/pipeline_builder.c',
|
||||
'src/renderer.c',
|
||||
'src/rse_graphics.c',
|
||||
'src/vma_port.cpp',
|
||||
'src/vulkan_base.c',
|
||||
'src/vulkan_buffers.c',
|
||||
'src/vulkan_commands.c',
|
||||
'src/vulkan_image.c',
|
||||
'src/vulkan_render_pass.c',
|
||||
'src/vulkan_swapchain.c',
|
||||
'src/window.c',
|
||||
]
|
||||
|
||||
vulkan_dep = dependency('Vulkan')
|
||||
|
||||
sdl3_dep = dependency('sdl3', version: '>=3.4.0', default_options : ['warning_level=0', 'werror=false'])
|
||||
|
||||
freetype_dep = dependency('freetype2', version: '>=22.1.16')
|
||||
glm_dep = dependency('cglm', version: '>=0.9.6', method: 'cmake')
|
||||
|
||||
vma_dep = dependency('VulkanMemoryAllocator', version: '>=3.3.0')
|
||||
|
||||
stb = dependency('stb',
|
||||
fallback: ['stb', 'stb_dep'])
|
||||
|
||||
rse_graphics_lib = shared_library(
|
||||
'rse_graphics',
|
||||
rse_graphics_sources,
|
||||
override_options: [
|
||||
'warning_level=3',
|
||||
'werror=true'
|
||||
],
|
||||
include_directories: ['../'],
|
||||
dependencies: [
|
||||
vulkan_dep,
|
||||
vma_dep,
|
||||
sdl3_dep,
|
||||
freetype_dep,
|
||||
glm_dep,
|
||||
stb
|
||||
],
|
||||
link_with: rse_utilities_lib,
|
||||
install: true,
|
||||
)
|
||||
|
||||
rse_graphics_lib_dep = declare_dependency(
|
||||
link_with: rse_graphics_lib,
|
||||
dependencies: [
|
||||
vulkan_dep,
|
||||
vma_dep,
|
||||
glm_dep
|
||||
],
|
||||
include_directories: include_directories('.')
|
||||
)
|
||||
@@ -1,9 +1,10 @@
|
||||
#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
|
||||
@@ -11,24 +12,20 @@
|
||||
* @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 graphics_context_t* context);
|
||||
rse_err_t rse_graphics_init(struct rse_graphics_context_t** context);
|
||||
|
||||
/**
|
||||
* @brief Runs graphics engine. This has to be called after all objects are added to the scene
|
||||
*
|
||||
* @return rse_err_t RSE_ERROR_NO_ERROR on success
|
||||
*/
|
||||
rse_err_t rse_graphics_run(struct graphics_context_t* context);
|
||||
int rse_graphics_run(void* arg);
|
||||
|
||||
|
||||
rse_err_t rse_graphics_main_loop(struct graphics_context_t* context);
|
||||
|
||||
/**
|
||||
* @brief Custom function for testing engine. TODO: Remove it whe releasing
|
||||
*
|
||||
*/
|
||||
rse_err_t rse_graphics_test_function(struct graphics_context_t* context);
|
||||
|
||||
void rse_graphics_deinit(struct graphics_context_t* context);
|
||||
void rse_graphics_test_function(struct rse_graphics_context_t* context);
|
||||
|
||||
#endif /* RSE_GRAPHICS_H */
|
||||
@@ -1,8 +1,21 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D overlay_tex;
|
||||
layout(location = 0) in vec2 uv;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
/* 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;
|
||||
|
||||
void main() {
|
||||
out_color = texture(overlay_tex, uv);
|
||||
/* vec4 sampled = vec4(1.0, 1.0, 1.0, texture(texSampler[instanceTextureId], fragTexCoord).r);*/
|
||||
/* if (sampled.a < 0.1)
|
||||
discard;
|
||||
outColor = sampled; */
|
||||
outColor = vec4(fragColor, texture(texSampler[instanceTextureId], fragTexCoord).r);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out vec2 uv;
|
||||
vec2 positions[3] = vec2[](
|
||||
vec2(-1.0, -1.0),
|
||||
vec2(3.0, -1.0),
|
||||
vec2(-1.0, 3.0)
|
||||
);
|
||||
void main() {
|
||||
uv = (positions[gl_VertexIndex] + 1.0) * 0.5;
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
}
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
#version 450
|
||||
|
||||
layout(push_constant) uniform pc {
|
||||
layout(binding = 0) uniform UniformBufferObject {
|
||||
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;
|
||||
@@ -29,29 +22,28 @@ 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(instance.rot.x + 0);
|
||||
float c = cos(instance.rot.x + 0);
|
||||
float s = sin(instanceRotation.x + 0);
|
||||
float c = cos(instanceRotation.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(instance.rot.y + 0);
|
||||
c = cos(instance.rot.y + 0);
|
||||
s = sin(instanceRotation.y + 0);
|
||||
c = cos(instanceRotation.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(instance.rot.z + 0);
|
||||
c = cos(instance.rot.z + 0);
|
||||
s = sin(instanceRotation.z + 0);
|
||||
c = cos(instanceRotation.z + 0);
|
||||
|
||||
mz[0] = vec3(c, -s, 0.0);
|
||||
mz[1] = vec3(s, c, 0.0);
|
||||
@@ -59,19 +51,19 @@ void main()
|
||||
|
||||
mat3 rotMat = mz * my * mx;
|
||||
|
||||
s = sin(instance.rot.y + 0);
|
||||
c = cos(instance.rot.y + 0);
|
||||
s = sin(instanceRotation.y + 0);
|
||||
c = cos(instanceRotation.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 * instance.scale) + instance.pos, 1.0);
|
||||
vec4 pos = vec4((locPos.xyz * instanceScale) + instancePosition, 1.0);
|
||||
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * pos;
|
||||
|
||||
fragColor = inColor;
|
||||
fragTexCoord = inTexCoord;
|
||||
outInstanceTextureId = instance.texture_id;
|
||||
outInstanceTextureId = instanceTextureId;
|
||||
}
|
||||
+351
-105
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "descriptor_builder.h"
|
||||
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -19,160 +18,411 @@
|
||||
#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"
|
||||
|
||||
#define POOL_MAX_SETS (32U)
|
||||
RSE_LINKED_LIST_DEFINE(descriptor_set_layout_list_t, VkDescriptorSetLayoutBinding);
|
||||
|
||||
rse_err_t descriptor_pool_add_type(struct graphics_context_t* context, VkDescriptorType type, uint32_t count)
|
||||
struct descriptor_t
|
||||
{
|
||||
context->descriptor_data.pool_sizes[context->descriptor_data.pool_sizes_count].type = type;
|
||||
context->descriptor_data.pool_sizes[context->descriptor_data.pool_sizes_count].descriptorCount = count;
|
||||
uint32_t binding;
|
||||
VkDescriptorType type;
|
||||
uint32_t descriptor_count;
|
||||
VkShaderStageFlags shader_stages;
|
||||
VkSampler* immutable_sampler;
|
||||
};
|
||||
|
||||
context->descriptor_data.pool_sizes_count++;
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
RSE_LINKED_LIST_DEFINE(descriptor_set_list_t, struct descriptor_t);
|
||||
|
||||
rse_err_t descriptor_pool_initialize(struct graphics_context_t* context)
|
||||
struct descriptor_set_handle_t
|
||||
{
|
||||
VkDescriptorPoolCreateInfo create_info = {0};
|
||||
VkDescriptorSet descriptor_sets;
|
||||
VkDescriptorPool descriptor_pool;
|
||||
VkDescriptorSetLayout descriptor_sets_layout;
|
||||
struct descriptor_set_list_t* descriptor_set_list;
|
||||
};
|
||||
|
||||
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;
|
||||
/* 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,
|
||||
};
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
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_create_new_set(struct graphics_context_t* context, rse_id_t* set_id)
|
||||
rse_err_t descriptor_free_handle(struct descriptor_set_handle_t **descriptor_set_handle)
|
||||
{
|
||||
if (context->descriptor_data.descriptor_sets_count >= DESCRIPTOR_MAX_SETS) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Reached maximum number of available descriptor sets");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
if (NULL == *descriptor_set_handle) {
|
||||
LOGE(_("Provided NULL handle\n"));
|
||||
|
||||
return RSE_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
*set_id = context->descriptor_data.descriptor_sets_count++;
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
RSE_LINKED_LIST_CLEAR((*descriptor_set_handle)->descriptor_set_list);
|
||||
rse_free(*descriptor_set_handle);
|
||||
|
||||
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++;
|
||||
*descriptor_set_handle = NULL;
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t descriptor_set_finish(struct graphics_context_t* context,
|
||||
struct descriptor_set_layout_bindings_t* layout_bindings,
|
||||
rse_id_t set_id)
|
||||
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)
|
||||
{
|
||||
VkDescriptorSetLayoutCreateInfo create_info = {0};
|
||||
struct descriptor_t descriptor_sets = {.binding = binding,
|
||||
.type = type,
|
||||
.descriptor_count = descriptor_count,
|
||||
.shader_stages = shader_stages,
|
||||
.immutable_sampler = sampler};
|
||||
|
||||
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);
|
||||
RSE_LINKED_LIST_PUSH_BACK(descriptor_set_handle->descriptor_set_list, descriptor_sets);
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t descriptor_build_sets(struct graphics_context_t* context)
|
||||
rse_err_t descriptors_build(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle)
|
||||
{
|
||||
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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided NULL context");
|
||||
LOGF(_("Provided NULL context\n"));
|
||||
|
||||
return RSE_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
/* Allocate descriptors from the pool */
|
||||
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.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.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
|
||||
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;
|
||||
if (VK_SUCCESS != vkAllocateDescriptorSets(context->device, &alloc_info, &descriptor_sets)) {
|
||||
LOGF(_("Failed to create descriptor sets\n"));
|
||||
return VULKAN_ERROR_DESCRIPTOR_SET_CREATION_FAILED;
|
||||
}
|
||||
|
||||
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_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)
|
||||
rse_err_t descriptor_get_layout(struct descriptor_set_handle_t* descriptor_set_handle,
|
||||
VkDescriptorSetLayout* descriptor_sets_layout)
|
||||
{
|
||||
size_t iter = 0U;
|
||||
VkDescriptorImageInfo* image_infos = NULL;
|
||||
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};
|
||||
VkWriteDescriptorSet write_set = {0};
|
||||
|
||||
rse_malloc(image_infos, sizeof(VkDescriptorImageInfo) * images_count);
|
||||
for (iter = 0U; iter < images_count; ++iter) {
|
||||
image_infos[iter].sampler = sampler;
|
||||
image_infos[iter].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
image_infos[iter].imageView = context->texture_images[texture_ids[iter]].image_view;
|
||||
}
|
||||
image_info.sampler = sampler;
|
||||
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
image_info.imageView = context->texture_images[texture_id].image_view;
|
||||
|
||||
write_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write_set.pImageInfo = image_infos;
|
||||
write_set.dstSet = context->descriptor_data.descriptor_sets[descriptor_set_id];
|
||||
write_set.pImageInfo = &image_info;
|
||||
write_set.dstSet = descriptor_set_handle->descriptor_sets;
|
||||
write_set.dstBinding = binding;
|
||||
write_set.dstArrayElement = 0U;
|
||||
write_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; // TODO: Maybe not only samplers?
|
||||
write_set.descriptorCount = images_count;
|
||||
write_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; //TODO: Maybe not only samplers?
|
||||
write_set.descriptorCount = 1;
|
||||
|
||||
vkUpdateDescriptorSets(context->vulkan_handles.device, 1U, &write_set, 0, NULL);
|
||||
rse_free(image_infos);
|
||||
vkUpdateDescriptorSets(context->device, 1U, &write_set, 0, NULL);
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t descriptor_attach_buffer(struct graphics_context_t* context,
|
||||
rse_id_t descriptor_set_id,
|
||||
struct vulkan_buffer_t* buffer,
|
||||
uint32_t binding,
|
||||
VkDescriptorType type)
|
||||
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)
|
||||
{
|
||||
VkDescriptorBufferInfo buffer_info = {0};
|
||||
VkWriteDescriptorSet write_set = {0};
|
||||
@@ -183,23 +433,19 @@ rse_err_t descriptor_attach_buffer(struct graphics_context_t* context,
|
||||
|
||||
write_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write_set.pBufferInfo = &buffer_info;
|
||||
write_set.dstSet = context->descriptor_data.descriptor_sets[descriptor_set_id];
|
||||
write_set.dstSet = descriptor_set_handle->descriptor_sets;
|
||||
write_set.dstBinding = binding;
|
||||
write_set.dstArrayElement = 0U;
|
||||
write_set.descriptorType = type;
|
||||
write_set.descriptorCount = 1;
|
||||
|
||||
vkUpdateDescriptorSets(context->vulkan_handles.device, 1U, &write_set, 0, NULL);
|
||||
vkUpdateDescriptorSets(context->device, 1U, &write_set, 0, NULL);
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
void destroy_descriptors(struct graphics_context_t* context)
|
||||
{
|
||||
size_t iter = 0U;
|
||||
|
||||
for (iter = 0U; iter < context->descriptor_data.descriptor_sets_count; ++iter) {
|
||||
vkDestroyDescriptorSetLayout(context->vulkan_handles.device, context->descriptor_data.set_layouts[iter], NULL);
|
||||
}
|
||||
vkDestroyDescriptorPool(context->vulkan_handles.device, context->descriptor_data.descriptor_pool, NULL);
|
||||
vkDestroyDescriptorSetLayout(context->device, context->descriptor_sets->descriptor_sets_layout, NULL);
|
||||
vkDestroyDescriptorPool(context->device, context->descriptor_sets->descriptor_pool, NULL);
|
||||
}
|
||||
@@ -20,23 +20,21 @@
|
||||
struct descriptor_set_handle_t;
|
||||
struct descriptor_set_list_t;
|
||||
|
||||
rse_err_t descriptor_pool_add_type(struct graphics_context_t* context, VkDescriptorType type, uint32_t count);
|
||||
/**
|
||||
* @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_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 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);
|
||||
|
||||
/**
|
||||
* @brief Add descriptor set to provided list
|
||||
@@ -46,27 +44,41 @@ rse_err_t descriptor_build_sets(struct graphics_context_t* context);
|
||||
* @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);
|
||||
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_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 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_buffer(struct graphics_context_t* context,
|
||||
rse_id_t descriptor_set_id,
|
||||
struct vulkan_buffer_t* buffer,
|
||||
uint32_t binding,
|
||||
VkDescriptorType type);
|
||||
/**
|
||||
* @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);
|
||||
|
||||
void destroy_descriptors(struct graphics_context_t* context);
|
||||
|
||||
#endif // !DESCRIPTOR_BUILDER_H
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file font_errors.h
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2023-10-06
|
||||
*
|
||||
* @copyright Copyright (c) 2023
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FONT_ERRORS_H
|
||||
#define FONT_ERRORS_H
|
||||
|
||||
#define RSE_FONT_MODULE_ID 0x0400U
|
||||
|
||||
/* Error codes */
|
||||
enum window_error_t {
|
||||
FONT_ERROR_INIT_FAILED = RSE_FONT_MODULE_ID,
|
||||
FONT_ERROR_LOAD_FAILED,
|
||||
FONT_ERROR_CHANGE_SIZE_FAILED,
|
||||
FONT_ERROR_LOAD_GLYPH_FAILED,
|
||||
FONT_ERROR_RENDER_GLYPH_FAILED
|
||||
};
|
||||
|
||||
#endif /* WINDOW_ERRORS_H */
|
||||
@@ -1,229 +0,0 @@
|
||||
/**
|
||||
* @file font_manager.c
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @brief Font manager. Load fonts from file and render it on the scene
|
||||
* @version 0.1
|
||||
* @date 2025-10-08
|
||||
*
|
||||
* @copyright Copyright (c) 2025
|
||||
*
|
||||
*/
|
||||
|
||||
#include "font_manager.h"
|
||||
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <assert.h>
|
||||
#include <cglm/types.h>
|
||||
#include <ft2build.h>
|
||||
#include <freetype/freetype.h>
|
||||
#include <freetype/fttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "freetype/freetype.h"
|
||||
#include "graphics_context.h"
|
||||
#include "mesh_controller.h"
|
||||
#include "src/vulkan_commons.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "utilities/errors_common.h"
|
||||
#include "utilities/file_utils.h"
|
||||
#include "vulkan_image.h"
|
||||
|
||||
#define FONTS_START_CHARACTER_CODE (33U)
|
||||
|
||||
FT_Library g_font_library = NULL;
|
||||
FT_Face g_font_faces[FONTS_MAX_COUNT] = {NULL};
|
||||
size_t g_font_free_index = 0U;
|
||||
|
||||
rse_err_t fonts_init(void)
|
||||
{
|
||||
FT_Error error = FT_Err_Ok;
|
||||
|
||||
error = FT_Init_FreeType(&g_font_library);
|
||||
|
||||
if (error != FT_Err_Ok) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize FreeType2. Error code: %d", error);
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path, rse_id_t* font_id)
|
||||
{
|
||||
FT_Error error = FT_Err_Ok;
|
||||
rse_err_t ret = RSE_ERROR_NO_ERROR;
|
||||
char real_path[MAX_PATH_LENGTH] = {0};
|
||||
|
||||
assert(context != NULL);
|
||||
assert(file_path != NULL);
|
||||
assert(font_id != NULL);
|
||||
assert(g_font_library != NULL);
|
||||
assert(g_font_free_index < FONTS_MAX_COUNT);
|
||||
|
||||
strncpy(real_path, file_path, MAX_PATH_LENGTH);
|
||||
|
||||
if ((ret = file_append_full_path(real_path, MAX_PATH_LENGTH)) != RSE_ERROR_NO_ERROR) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
error = FT_New_Face(g_font_library, real_path, 0, &g_font_faces[g_font_free_index]);
|
||||
if (error != FT_Err_Ok) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to load new font face. Error code: %d", error);
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
*font_id = g_font_free_index++;
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t fonts_set_font_size(struct graphics_context_t* context, const rse_id_t font_id, const uint32_t font_size)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
// size_t iter = 0U;
|
||||
FT_UInt glyph_index = 0U;
|
||||
FT_Error error = FT_Err_Ok;
|
||||
FT_GlyphSlot glyph = NULL;
|
||||
struct vertex_t vertices[4] = {0};
|
||||
uint16_t indices[] = {0, 1, 2, 2, 3, 0};
|
||||
struct sized_font_data_t* font = NULL;
|
||||
|
||||
assert(context != NULL);
|
||||
assert(font_id < FONTS_MAX_COUNT);
|
||||
assert(g_font_faces[font_id] != NULL);
|
||||
|
||||
error = FT_Set_Pixel_Sizes(g_font_faces[font_id], 0, font_size);
|
||||
|
||||
if (error != FT_Err_Ok) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to set font size. Error code: %d", error);
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
font = &context->fonts_data.fonts[font_id];
|
||||
font->font_size = font_size;
|
||||
|
||||
// for (iter = 0U; iter < FONT_CHARACTERS_COUNT; ++iter) {
|
||||
// glyph_index = iter + FONTS_START_CHARACTER_CODE;
|
||||
glyph_index = 33 + FONTS_START_CHARACTER_CODE;
|
||||
|
||||
error = FT_Load_Char(g_font_faces[font_id], glyph_index, FT_LOAD_RENDER);
|
||||
if (error != FT_Err_Ok) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to load glyph with id %d. Error code: %d",
|
||||
glyph_index,
|
||||
error);
|
||||
// continue;
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
glyph = g_font_faces[font_id]->glyph;
|
||||
STATUS_CHECK(load_texture_from_bitmat(context,
|
||||
glyph->bitmap.buffer,
|
||||
glyph->bitmap.width,
|
||||
glyph->bitmap.rows,
|
||||
&font->font_characters[33].texture_id,
|
||||
VK_FORMAT_R8_UNORM));
|
||||
|
||||
vertices[0].pos[0] = 0.0 - glyph->bitmap.width;
|
||||
vertices[0].pos[1] = glyph->bitmap.rows;
|
||||
vertices[0].pos[2] = 0.0;
|
||||
vertices[0].color[0] = 1.0;
|
||||
vertices[0].color[1] = 1.0;
|
||||
vertices[0].color[2] = 1.0;
|
||||
vertices[0].tex_coords[0] = 0.0;
|
||||
vertices[0].tex_coords[1] = 1.0;
|
||||
|
||||
vertices[1].pos[0] = glyph->bitmap.width;
|
||||
vertices[1].pos[1] = glyph->bitmap.rows;
|
||||
vertices[1].pos[2] = 0.0;
|
||||
vertices[1].color[0] = 1.0;
|
||||
vertices[1].color[1] = 1.0;
|
||||
vertices[1].color[2] = 1.0;
|
||||
vertices[1].tex_coords[0] = 1.0;
|
||||
vertices[1].tex_coords[1] = 1.0;
|
||||
|
||||
vertices[2].pos[0] = glyph->bitmap.width;
|
||||
vertices[2].pos[1] = 0.0 - glyph->bitmap.rows;
|
||||
vertices[2].pos[2] = 0.0;
|
||||
vertices[2].color[0] = 1.0;
|
||||
vertices[2].color[1] = 1.0;
|
||||
vertices[2].color[2] = 1.0;
|
||||
vertices[2].tex_coords[0] = 1.0;
|
||||
vertices[2].tex_coords[1] = 0.0;
|
||||
|
||||
vertices[3].pos[0] = 0.0 - glyph->bitmap.width;
|
||||
vertices[3].pos[1] = 0.0 - glyph->bitmap.rows;
|
||||
vertices[3].pos[2] = 0.0;
|
||||
vertices[3].color[0] = 1.0;
|
||||
vertices[3].color[1] = 1.0;
|
||||
vertices[3].color[2] = 1.0;
|
||||
vertices[3].tex_coords[0] = 0.0;
|
||||
vertices[3].tex_coords[1] = 0.0;
|
||||
|
||||
STATUS_CHECK(mesh_create(context, vertices, indices, 4, 6, &font->font_characters[33].mesh_id));
|
||||
// }
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t print_debug_text(struct graphics_context_t* context, rse_id_t font_id, char* text)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
(void)text;
|
||||
|
||||
STATUS_CHECK(mesh_create_instance(
|
||||
context,
|
||||
context->fonts_data.fonts[font_id].font_characters[33].mesh_id,
|
||||
(struct instance_data_t){{0.0f, 0.0f, 2.4f},
|
||||
{0.0f, 0.0f, 0.0},
|
||||
1.0f,
|
||||
context->fonts_data.fonts[font_id].font_characters[33].texture_id}));
|
||||
|
||||
return status;
|
||||
}
|
||||
// rse_err_t print_debug_text(struct graphics_context_t* context, char* text)
|
||||
// {
|
||||
// rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
// size_t width, height;
|
||||
// size_t iter = 0U;
|
||||
// int number_of_quads = 0;
|
||||
// char buffer[99999] = {0};
|
||||
// float *v0, *v2 = NULL;
|
||||
//
|
||||
// int min_x, min_y, max_x, max_y = 0;
|
||||
// int yy, xx;
|
||||
//
|
||||
// width = context->swapchain_data.swapchain_extent.width;
|
||||
// height = context->swapchain_data.swapchain_extent.height;
|
||||
//
|
||||
// context->debug_overlay.pixels_size = width * height * sizeof(uint32_t);
|
||||
//
|
||||
// rse_memset(context->debug_overlay.pixels, 0, context->debug_overlay.pixels_size);
|
||||
//
|
||||
// number_of_quads = stb_easy_font_print(10.0, 20.0, text, NULL, (void*)buffer, sizeof(buffer));
|
||||
//
|
||||
// for (iter = 0U; iter < number_of_quads * 4; ++iter) {
|
||||
// v0 = (float*)&buffer[iter * 16 + 0];
|
||||
// v2 = (float*)&buffer[iter * 16 + 32];
|
||||
//
|
||||
// min_x = v0[0];
|
||||
// min_y = v0[1];
|
||||
// max_x = v2[0];
|
||||
// max_y = v2[1];
|
||||
//
|
||||
// for (yy = min_y; yy < max_y; ++yy) {
|
||||
// for (xx = min_x; xx < max_x; ++xx) {
|
||||
// if (xx >= 0 && yy >= 0 && xx < (int)width && yy < (int)height) {
|
||||
// context->debug_overlay.pixels[yy * width + xx] = 0xFFFFFFFF; // RGBA white
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// STATUS_CHECK(texture_update_image(context, (unsigned char*)context->debug_overlay.pixels, width, height,
|
||||
// context->debug_overlay.texture_id, VK_FORMAT_R8G8B8A8_UNORM));
|
||||
//
|
||||
// return RSE_ERROR_NO_ERROR;
|
||||
// }
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* @file font_manager.h
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @brief Font manager. Load fonts from file and render it on the scene
|
||||
* @version 0.1
|
||||
* @date 2025-10-08
|
||||
*
|
||||
* @copyright Copyright (c) 2025
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FONT_MANAGER_H
|
||||
#define FONT_MANAGER_H
|
||||
|
||||
#include "utilities/commons.h"
|
||||
#include "graphics_context.h"
|
||||
|
||||
rse_err_t fonts_init(void);
|
||||
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path, rse_id_t* font_id);
|
||||
rse_err_t fonts_set_font_size(struct graphics_context_t* context, const rse_id_t font_id, const uint32_t font_size);
|
||||
rse_err_t print_debug_text(struct graphics_context_t* context, rse_id_t font_id, char *text);
|
||||
|
||||
#endif /* FONT_MANAGER_H */
|
||||
+60
-223
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file graphics_context.h
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief Graphics context for Red Scarf Engine
|
||||
* @version 0.1
|
||||
* @date 2024-05-22
|
||||
@@ -12,24 +12,18 @@
|
||||
#ifndef GRAPHICS_CONTEXT_H
|
||||
#define GRAPHICS_CONTEXT_H
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include "SDL3/SDL.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "vma/vk_mem_alloc.h"
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include "utilities/entity.h"
|
||||
#include "utilities/vector.h"
|
||||
#include "vk_mem_alloc.h"
|
||||
#include "utilities/linked_list.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
#include "vulkan_commons.h"
|
||||
|
||||
#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)
|
||||
#define MAX_MESH_NUMBER 2
|
||||
#define MAX_INSTANCE_NUMBER 256
|
||||
|
||||
/**
|
||||
* @brief Maximum number of textures that can be loaded
|
||||
@@ -37,37 +31,11 @@
|
||||
*/
|
||||
#define RSE_MAX_IMAGE_COUNT 256
|
||||
|
||||
#define MAX_VULKAN_BUFFERS_COUNT (32U)
|
||||
|
||||
#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
|
||||
};
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Represents data within vertex or instance buffer.
|
||||
@@ -79,11 +47,6 @@ 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;
|
||||
*
|
||||
@@ -98,7 +61,16 @@ struct vulkan_image_t
|
||||
VmaAllocationInfo allocation_info;
|
||||
};
|
||||
|
||||
RSE_VECTOR_DEFINE(instance_vector_t, struct instance_data_t);
|
||||
/**
|
||||
* @brief Represents instance of a mesh and shows where it's located in instance buffer
|
||||
*
|
||||
*/
|
||||
struct internal_instance_data_t
|
||||
{
|
||||
uint8_t instance_taken;
|
||||
uint8_t instance_buffer_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mesh data, with unique id and dynamic arrays holding vertices and indices.
|
||||
* One mesh can have multiple instances.
|
||||
@@ -106,197 +78,62 @@ RSE_VECTOR_DEFINE(instance_vector_t, struct instance_data_t);
|
||||
*/
|
||||
struct mesh_t
|
||||
{
|
||||
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;
|
||||
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];
|
||||
};
|
||||
|
||||
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_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];
|
||||
VkPipelineLayout pipeline_layout;
|
||||
};
|
||||
|
||||
struct graphics_context_t
|
||||
{
|
||||
bool is_framebuffer_resized;
|
||||
uint32_t queue_family_indices[QUEUE_FAMILY_INDEX_LAST_INDEX];
|
||||
uint32_t current_frame;
|
||||
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;
|
||||
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 render_target_t render_targets[RENDER_TARGET_COUNT];
|
||||
struct vulkan_buffers_t uniform_buffers;
|
||||
struct descriptor_set_handle_t* descriptor_sets;
|
||||
VkDebugUtilsMessengerEXT debug_messenger;
|
||||
bool is_framebuffer_resized; // TODO: Consider moving this to heap
|
||||
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;
|
||||
struct vulkan_image_t texture_images[RSE_MAX_IMAGE_COUNT];
|
||||
struct vulkan_image_t depth_image;
|
||||
struct vulkan_image_t color_image;
|
||||
struct shader_modules_t shader_modules;
|
||||
struct mesh_data_t mesh_data;
|
||||
struct fonts_data_t fonts_data;
|
||||
struct shader_module_list_t* shader_modules;
|
||||
struct mesh_t meshes[MAX_MESH_NUMBER];
|
||||
};
|
||||
|
||||
#endif /* GRAPHICS_CONTEXT_H */
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "math.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define PI 3.14159265359f
|
||||
|
||||
static struct vec3_t substract_vec3(const struct vec3_t* vec1, const struct vec3_t* vec2)
|
||||
{
|
||||
struct vec3_t result;
|
||||
|
||||
result.x = vec1->x - vec2->x;
|
||||
result.y = vec1->y - vec2->y;
|
||||
result.z = vec1->z - vec2->z;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static float length_vec3(const struct vec3_t* vec)
|
||||
{
|
||||
return sqrt((vec->x * vec->x)
|
||||
+(vec->y * vec->y)
|
||||
+(vec->z * vec->z));
|
||||
}
|
||||
|
||||
static float scalar_cross_vec3(const struct vec3_t* vec1, const struct vec3_t* vec2)
|
||||
{
|
||||
return (vec1->x * vec2->x)
|
||||
+(vec1->y * vec2->y)
|
||||
+(vec1->z * vec2->z);
|
||||
}
|
||||
|
||||
float math_deg_to_radians(float degrees)
|
||||
{
|
||||
return degrees * (PI/180.0f);
|
||||
}
|
||||
|
||||
struct vec3_t math_cross_vec3(const struct vec3_t* vec1, const struct vec3_t* vec2)
|
||||
{
|
||||
struct vec3_t result;
|
||||
|
||||
result.x = (vec1->y * vec2->z) - (vec1->z * vec2->y);
|
||||
result.y = (vec1->z * vec2->x) - (vec1->x * vec2->z);
|
||||
result.z = (vec1->x * vec2->y) - (vec1->y * vec2->x);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct vec3_t math_normalize_vec3(const struct vec3_t* vec)
|
||||
{
|
||||
struct vec3_t result = {0};
|
||||
float vec_length = 0.0f;
|
||||
|
||||
vec_length = length_vec3(vec);
|
||||
|
||||
result.x = vec->x / vec_length;
|
||||
result.y = vec->y / vec_length;
|
||||
result.z = vec->z / vec_length;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct mat4_t math_uniform_mat4()
|
||||
{
|
||||
struct mat4_t result= {0.0f};
|
||||
result.x_1 = 1.0f;
|
||||
result.y_2 = 1.0f;
|
||||
result.z_3 = 1.0f;
|
||||
result.w_4 = 1.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct mat4_t math_perspective(float fovy, float aspect_ratio, float z_near, float z_far)
|
||||
{
|
||||
float tan_half_fovy = 0;
|
||||
struct mat4_t result = {0};
|
||||
|
||||
tan_half_fovy = tan(fovy / 2.0f);
|
||||
|
||||
result.x_1 = -1.0f / (aspect_ratio * tan_half_fovy);
|
||||
result.y_2 = 1.0f / (tan_half_fovy);
|
||||
result.z_3 = - (z_far + z_near) / (z_far - z_near);
|
||||
result.w_3 = -1.0f;
|
||||
result.z_4 = - (2.0f * z_far * z_near) / (z_far - z_near);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct mat4_t math_look_at(const struct vec3_t* eye, const struct vec3_t* center, const struct vec3_t* up)
|
||||
{
|
||||
struct mat4_t result = {0.0f};
|
||||
struct vec3_t forward_vec = {0};
|
||||
struct vec3_t right_vec = {0};
|
||||
struct vec3_t up_vec = {0};
|
||||
|
||||
struct vec3_t tmp = substract_vec3(eye, center);
|
||||
|
||||
forward_vec = math_normalize_vec3(&tmp);
|
||||
|
||||
tmp = math_cross_vec3(up, &forward_vec);
|
||||
right_vec = math_normalize_vec3(&tmp);
|
||||
|
||||
up_vec = math_cross_vec3(&forward_vec, &right_vec);
|
||||
|
||||
result.x_1 = right_vec.x;
|
||||
result.x_2 = right_vec.y;
|
||||
result.x_3 = right_vec.z;
|
||||
|
||||
result.y_1 = up_vec.x;
|
||||
result.y_2 = up_vec.y;
|
||||
result.y_3 = up_vec.z;
|
||||
|
||||
result.z_1 = forward_vec.x;
|
||||
result.z_2 = forward_vec.y;
|
||||
result.z_3 = forward_vec.z;
|
||||
|
||||
result.x_4 = -scalar_cross_vec3(&right_vec, eye);
|
||||
result.y_4 = -scalar_cross_vec3(&up_vec, eye);
|
||||
result.z_4 = -scalar_cross_vec3(&forward_vec, eye);
|
||||
|
||||
result.w_1 = 0;
|
||||
result.w_2 = 0;
|
||||
result.w_3 = 0;
|
||||
result.w_4 = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct mat4_t math_rotate(struct mat4_t matrix, float radians, struct vec3_t rotation_vector)
|
||||
{
|
||||
struct mat4_t result = math_uniform_mat4();
|
||||
float cos = cosf(radians);
|
||||
float sin = sinf(radians);
|
||||
|
||||
//TODO: Switch to quaternion
|
||||
result.x_1 = cos + powf(rotation_vector.x, 2.0f) * (1 - cos);
|
||||
result.x_2 = (rotation_vector.x * rotation_vector.y * (1 - cos)) - (rotation_vector.z * sin);
|
||||
result.x_3 = (rotation_vector.x * rotation_vector.z * (1 - cos)) + (rotation_vector.y * sin);
|
||||
|
||||
result.y_1 = (rotation_vector.y * rotation_vector.x * (1 - cos)) + rotation_vector.z * sin;
|
||||
result.y_2 = cos + (powf(rotation_vector.y, 2) * (1 - cos));
|
||||
result.y_3 = (rotation_vector.y * rotation_vector.z * (1 - cos)) - (rotation_vector.x * sin);
|
||||
|
||||
result.z_1 = (rotation_vector.z * rotation_vector.x * (1 - cos)) - (rotation_vector.y * sin);
|
||||
result.z_2 = (rotation_vector.z * rotation_vector.y * (1 - cos)) + (rotation_vector.x * sin);
|
||||
result.z_3 = cos + (powf(rotation_vector.z, 2.0f) * (1 - cos));
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @file math.h
|
||||
* @author Piotr Krygier (piotr.krygier@)
|
||||
* @brief RedScarfEngine math operations
|
||||
* @version 0.1
|
||||
* @date 2023-08-07
|
||||
*
|
||||
* @copyright Copyright (c) 2023
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MATH_H
|
||||
#define MATH_H
|
||||
|
||||
/**
|
||||
* @brief Simple representation of vector
|
||||
*
|
||||
*/
|
||||
struct vec3_t {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
/* 4x4 matrix.
|
||||
* [x_1 | x_2 | x_3 | x_4]
|
||||
* [y_1 | y_2 | y_3 | y_4]
|
||||
* [z_1 | x_2 | z_3 | z_4]
|
||||
* [z_1 | w_2 | w_3 | w_4]
|
||||
*
|
||||
* Vulkan and OpenGL expects matrix to be passed by columns, this is why members are created in this order.
|
||||
* */
|
||||
struct mat4_t {
|
||||
/* First column */
|
||||
float x_1;
|
||||
float y_1;
|
||||
float z_1;
|
||||
float w_1;
|
||||
|
||||
/* Second column */
|
||||
float x_2;
|
||||
float y_2;
|
||||
float z_2;
|
||||
float w_2;
|
||||
|
||||
/* Third column */
|
||||
float x_3;
|
||||
float y_3;
|
||||
float z_3;
|
||||
float w_3;
|
||||
|
||||
/* Fourth column */
|
||||
float x_4;
|
||||
float y_4;
|
||||
float z_4;
|
||||
float w_4;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Calculate radians from provided degree
|
||||
*
|
||||
* @param degrees degrees
|
||||
* @return float radians
|
||||
*/
|
||||
float math_deg_to_radians(float degrees);
|
||||
|
||||
/**
|
||||
* @brief Create an uniform matrix, the one with ones on diagonal
|
||||
*
|
||||
* @return struct mat4_t Diagonal matrix
|
||||
*/
|
||||
struct mat4_t math_uniform_mat4();
|
||||
|
||||
/**
|
||||
* @brief Normalizes provided vector. Changes the values inside the vector to range 0.0...1.0
|
||||
*
|
||||
* @param vec Vector to normalize
|
||||
* @return struct vec3_t Normalized vector
|
||||
*/
|
||||
struct vec3_t math_normalize_vec3(const struct vec3_t* vec);
|
||||
|
||||
/**
|
||||
* @brief Creates vector cross product, a vector, using right hand rule. Note, that order of vectors is important
|
||||
*
|
||||
* @param vec1 Input vector
|
||||
* @param vec2 Input vector
|
||||
* @return struct vec3_t Resulting cross product vector
|
||||
*/
|
||||
struct vec3_t math_cross_vec3(const struct vec3_t* vec1, const struct vec3_t* vec2);
|
||||
|
||||
/**
|
||||
* @brief Calculates transformation matrix for viewing object from and eye cooridinates
|
||||
*
|
||||
* @param eye Location of the camera or the eye
|
||||
* @param center Where are we looking at
|
||||
* @param up Where "up" is located. Usually (0, 1, 0), since Y is up in most cases
|
||||
* @return struct mat4_t Resulting lootAtMatrix
|
||||
*/
|
||||
struct mat4_t math_look_at(const struct vec3_t* eye, const struct vec3_t* center, const struct vec3_t* up);
|
||||
|
||||
/**
|
||||
* @brief Creates perspective matrix, used in projection
|
||||
*
|
||||
* @param fovy_rad Field of view, in radians
|
||||
* @param aspect_ratio Aspect ratio (usally screen width / height)
|
||||
* @param z_near How near objects we can see
|
||||
* @param z_far How far objects we can see
|
||||
* @return struct mat4_t Resulting perspective matrix
|
||||
*/
|
||||
struct mat4_t math_perspective(float fovy_rad, float aspect_ratio, float z_near, float z_far);
|
||||
|
||||
/**
|
||||
* @brief Rotate matrix around vector for radians degrees
|
||||
*
|
||||
* @param matrix Matrix to rotate
|
||||
* @param radians Degrees
|
||||
* @param rotation_vector Rotation vector
|
||||
* @return struct mat4_t resulting matrix
|
||||
*/
|
||||
struct mat4_t math_rotate(struct mat4_t matrix, float radians, struct vec3_t rotation_vector);
|
||||
|
||||
#endif /* MATH_H */
|
||||
+71
-189
@@ -1,209 +1,91 @@
|
||||
/**
|
||||
* @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 "mesh_controller.h"
|
||||
|
||||
#include "vulkan_buffers.h"
|
||||
#include "vulkan_errors.h"
|
||||
#include "utilities/errors_common.h"
|
||||
#include "utilities/logger.h"
|
||||
#include "utilities/localization.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "src/graphics_context.h"
|
||||
#include "src/vulkan_commons.h"
|
||||
#include "utilities/cgltf.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "utilities/entity.h"
|
||||
#include "utilities/errors_common.h"
|
||||
#include "utilities/file_utils.h"
|
||||
#include "vulkan_buffers.h"
|
||||
#include "vulkan_image.h"
|
||||
|
||||
rse_err_t mesh_create(struct graphics_context_t* context,
|
||||
struct vertex_t* vertices,
|
||||
uint16_t* indices,
|
||||
size_t vertices_count,
|
||||
size_t indices_count,
|
||||
rse_id_t* mesh_id)
|
||||
#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 status = RSE_ERROR_NO_ERROR;
|
||||
size_t iter = 0U;
|
||||
|
||||
if (mesh_id == NULL) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided mesh id handle is NULL");
|
||||
return RSE_ERROR_NULL_POINTER;
|
||||
for(iter = 0U; iter < MAX_MESH_NUMBER; ++iter) {
|
||||
if(context->meshes[iter].is_mesh_id_taken == MESH_ID_FREE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*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;
|
||||
if(iter >= MAX_MESH_NUMBER) {
|
||||
LOGE(_("Could not allocate new mesh, reached maximum.\n"));
|
||||
return MAX_MESH_NUMBER;
|
||||
}
|
||||
|
||||
STATUS_CHECK(add_vertices(context, *mesh_id, vertices_count, vertices, indices_count, indices));
|
||||
context->mesh_data.mesh_count++;
|
||||
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);
|
||||
|
||||
return status;
|
||||
add_vertices(context, iter, vertices, indices, &context->meshes[iter].vertex_data, &context->meshes[iter].index_data);
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char* file_name, rse_id_t* mesh_id, rse_id_t* texture_id)
|
||||
rse_err_t create_mesh_instance(struct graphics_context_t* context, uint16_t mesh_id,
|
||||
struct instance_data_t instance_data)
|
||||
{
|
||||
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;
|
||||
size_t iter = 0U;
|
||||
|
||||
(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;
|
||||
}
|
||||
}
|
||||
for(iter = 0U; iter < MAX_INSTANCE_NUMBER; ++iter) {
|
||||
if(context->meshes[mesh_id].instance_data[iter].instance_taken == MESH_ID_FREE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (position_data_offset == NULL) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to retrieve position data for mesh %s", file_name);
|
||||
status = RSE_ERROR_INVALID_PARAM;
|
||||
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data_count == 0) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failure: data count for mesh is 0");
|
||||
status = RSE_ERROR_INVALID_PARAM;
|
||||
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rse_malloc(vertices, sizeof(struct vertex_t) * data_count);
|
||||
bin = (uint8_t*)data->bin;
|
||||
|
||||
for (data_index = 0U; data_index < data_count; ++data_index) {
|
||||
rse_memcpy(vertices[data_index].pos,
|
||||
(bin + *position_data_offset + (sizeof(vec3) * data_index)),
|
||||
sizeof(vec3));
|
||||
if (color_data_offset != NULL) {
|
||||
rse_memcpy(vertices[data_index].color,
|
||||
(bin + *color_data_offset + (sizeof(vec3) * data_index)),
|
||||
sizeof(vec3));
|
||||
} else {
|
||||
vertices[data_index].color[0] = 1.0;
|
||||
vertices[data_index].color[1] = 1.0;
|
||||
vertices[data_index].color[2] = 1.0;
|
||||
}
|
||||
|
||||
if (texture_coord_data_offset != NULL) {
|
||||
rse_memcpy(vertices[data_index].tex_coords,
|
||||
(bin + *texture_coord_data_offset + (sizeof(vec2) * data_index)),
|
||||
sizeof(vec2));
|
||||
}
|
||||
}
|
||||
|
||||
if (texture_data_offset != NULL) {
|
||||
rse_malloc(texture_pixel_buffer, *texture_data_size);
|
||||
rse_memcpy(texture_pixel_buffer, (bin + *texture_data_offset), *texture_data_size);
|
||||
STATUS_CHECK(texture_load_from_buffer(context, texture_pixel_buffer, *texture_data_size, texture_id));
|
||||
}
|
||||
|
||||
rse_memcpy(indices, (bin + *indices_data_offset), (sizeof(uint16_t) * indices_count));
|
||||
|
||||
STATUS_CHECK(mesh_create(context, vertices, indices, data_count, indices_count, mesh_id));
|
||||
}
|
||||
|
||||
exit:
|
||||
rse_free(position_data_offset);
|
||||
rse_free(color_data_offset);
|
||||
rse_free(texture_coord_data_offset);
|
||||
rse_free(indices_data_offset);
|
||||
rse_free(texture_data_size);
|
||||
rse_free(texture_data_offset);
|
||||
rse_free(texture_pixel_buffer);
|
||||
rse_free(vertices);
|
||||
rse_free(indices);
|
||||
cgltf_free(data);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
rse_err_t mesh_create_instance(struct graphics_context_t* context,
|
||||
entity_t entity,
|
||||
struct instance_data_t instance_data)
|
||||
{
|
||||
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;
|
||||
if(iter >= MAX_INSTANCE_NUMBER) {
|
||||
LOGE(_("Could not allocate new mesh, reached maximum.\n"));
|
||||
return VULKAN_ERROR_MAX_INSTANCE_COUNT_REACHED;
|
||||
}
|
||||
|
||||
return mesh_add_instance(context, entity, &instance_data);
|
||||
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;
|
||||
}
|
||||
@@ -12,7 +12,6 @@
|
||||
#ifndef MESH_CONTROLLER_H
|
||||
#define MESH_CONTROLLER_H
|
||||
|
||||
#include "utilities/entity.h"
|
||||
#include "vulkan_commons.h"
|
||||
#include "graphics_context.h"
|
||||
#include "utilities/commons.h"
|
||||
@@ -26,37 +25,42 @@
|
||||
* @param indices_num indices count
|
||||
* @return uint16_t Mesh identifier. Can be useful for getting vertices, indices and instances
|
||||
*/
|
||||
rse_err_t mesh_create(struct graphics_context_t* context, struct vertex_t* vertices,
|
||||
uint16_t create_mesh(struct graphics_context_t* context, struct vertex_t* vertices,
|
||||
uint16_t* indices,
|
||||
size_t vertices_count,
|
||||
size_t indices_count, rse_id_t* mesh_id);
|
||||
|
||||
size_t vertices_num,
|
||||
size_t indices_num);
|
||||
/**
|
||||
* @brief Create a instance for the selected mesh. Instance can have its own location, rotation and scale
|
||||
*
|
||||
* @param context Graphics context
|
||||
* @param entity Entity we wat to update
|
||||
* @param instance_data New instance information
|
||||
* @param mesh_id Mesh id
|
||||
* @param instance_data Transformation info
|
||||
*/
|
||||
rse_err_t mesh_create_instance(struct graphics_context_t* context, entity_t entity,
|
||||
rse_err_t create_mesh_instance(struct graphics_context_t* context, uint16_t mesh_id,
|
||||
struct instance_data_t instance_data);
|
||||
|
||||
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, rse_id_t mesh_id);
|
||||
size_t get_vertices_count(struct graphics_context_t* context, uint16_t mesh_id);
|
||||
|
||||
/**
|
||||
* @brief Get the index count for mesh with mesh_id ID
|
||||
*
|
||||
w @param mesh_id Mesh identifier
|
||||
* @param mesh_id Mesh identifier
|
||||
* @return size_t Number of indices
|
||||
*/
|
||||
// size_t get_indices_count(struct graphics_context_t* context, rse_id_t mesh_id);
|
||||
size_t get_indices_count(struct graphics_context_t* context, uint16_t mesh_id);
|
||||
|
||||
/**
|
||||
* @brief Get the instance count for mesh with mesh_id ID
|
||||
*
|
||||
* @param mesh_id Mesh identifier
|
||||
* @return size_t Number of instances
|
||||
*/
|
||||
size_t get_instances_count(struct graphics_context_t* context, uint16_t mesh_id);
|
||||
|
||||
/**
|
||||
* @brief Get vertex offset in vertex buffer for selected mesh
|
||||
@@ -64,6 +68,6 @@ rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char*
|
||||
* @param mesh_id Mesh identifies
|
||||
* @return size_t Offset
|
||||
*/
|
||||
// size_t get_vertex_offset(struct graphics_context_t* context, rse_id_t mesh_id);
|
||||
size_t get_vertex_offset(struct graphics_context_t* context, uint16_t mesh_id);
|
||||
|
||||
#endif /* MESH_CONTROLLER_H */
|
||||
@@ -1,6 +0,0 @@
|
||||
#include "utilities/commons.h"
|
||||
|
||||
rse_err_t object_load_gltf(const char* file_path)
|
||||
{
|
||||
|
||||
}
|
||||
+234
-159
@@ -12,69 +12,79 @@
|
||||
#include "pipeline_builder.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "SDL3/SDL_log.h"
|
||||
#include "src/descriptor_builder.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_err_t shader_create_module(struct graphics_context_t* context,
|
||||
rse_id_t* shader_module_id,
|
||||
const char* shader_path,
|
||||
const VkShaderStageFlagBits shader_stage)
|
||||
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)
|
||||
{
|
||||
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 = buffer_size;
|
||||
create_info.pCode = (const uint32_t*)(buffer);
|
||||
create_info.codeSize = data_size;
|
||||
create_info.pCode = (const uint32_t*)(binary_data);
|
||||
|
||||
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;
|
||||
}
|
||||
RSE_LINKED_LIST_PUSH_FRONT(context->shader_modules, (VkShaderModule){0});
|
||||
|
||||
if (VK_SUCCESS !=
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -102,13 +112,11 @@ static rse_err_t pipeline_add_viewport_state(struct pipeline_t* pipeline)
|
||||
pipeline->viewport_state.pScissors = NULL; /* This is set during rendering */
|
||||
|
||||
if (pipeline->viewport_state.pViewports == NULL || pipeline->viewport_state.viewportCount == 0U) {
|
||||
pipeline->required_dynamic_states.dynamic_states[pipeline->required_dynamic_states.dynamic_states_count++] =
|
||||
VK_DYNAMIC_STATE_VIEWPORT;
|
||||
RSE_STACK_PUSH(pipeline->required_dynamic_states, VK_DYNAMIC_STATE_VIEWPORT);
|
||||
}
|
||||
|
||||
if (pipeline->viewport_state.pScissors == NULL || pipeline->viewport_state.scissorCount == 0U) {
|
||||
pipeline->required_dynamic_states.dynamic_states[pipeline->required_dynamic_states.dynamic_states_count++] =
|
||||
VK_DYNAMIC_STATE_SCISSOR;
|
||||
RSE_STACK_PUSH(pipeline->required_dynamic_states, VK_DYNAMIC_STATE_SCISSOR);
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
@@ -195,29 +203,35 @@ static rse_err_t pipeline_add_depth_stencil(struct pipeline_t* pipeline)
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t pipeline_add_descriptor_sets(struct graphics_context_t* context,
|
||||
struct pipeline_t* pipeline,
|
||||
rse_id_t descriptor_set_id)
|
||||
/**
|
||||
* @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 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 = &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;
|
||||
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;
|
||||
|
||||
if (VK_SUCCESS !=
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -228,107 +242,139 @@ rse_err_t pipeline_add_descriptor_sets(struct graphics_context_t* context,
|
||||
*
|
||||
* @param[in/out] pipelines List of pipelines to clean up
|
||||
*/
|
||||
static void pipeline_infos_cleanup(struct pipeline_infos_t* pipelines)
|
||||
static void pipeline_builder_cleanup(struct pipeline_infos_list_t* pipelines)
|
||||
{
|
||||
size_t pipeline_count = pipelines->pipeline_create_infos_count;
|
||||
size_t pipeline_count = RSE_LINKED_LIST_SIZE(pipelines);
|
||||
VkVertexInputBindingDescription* vertex_bindings = NULL;
|
||||
VkVertexInputAttributeDescription* shader_attributes = NULL;
|
||||
VkDynamicState* dynamic_states = NULL;
|
||||
|
||||
for (size_t i = 0; i < pipeline_count; i++) {
|
||||
VkGraphicsPipelineCreateInfo* pipeline_info = &pipelines->create_infos[i];
|
||||
VkGraphicsPipelineCreateInfo* pipeline_info = &RSE_LINKED_LIST_FRONT(pipelines);
|
||||
if (pipeline_info != NULL) {
|
||||
vertex_bindings =
|
||||
(VkVertexInputBindingDescription*)pipeline_info->pVertexInputState->pVertexBindingDescriptions;
|
||||
|
||||
rse_free((VkPipelineVertexInputStateCreateInfo*)pipeline_info->pVertexInputState);
|
||||
rse_free((VkPipelineDynamicStateCreateInfo*)pipeline_info->pDynamicState);
|
||||
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);
|
||||
}
|
||||
|
||||
pipelines->pipeline_create_infos_count = 0;
|
||||
if (RSE_LINKED_LIST_IS_EMPTY(pipelines) == 0) {
|
||||
LOGF(_("Failed to clean up pipelines\n"));
|
||||
}
|
||||
}
|
||||
|
||||
rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, VkDynamicState dynamic_state)
|
||||
{
|
||||
if (dynamic_state >= VK_DYNAMIC_STATE_MAX_ENUM) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid dynamic state in pipeline");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Invalid dynamic state in pipeline\n"));
|
||||
return VULKAN_ERROR_PIPELINE_INVALID_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;
|
||||
RSE_LINKED_LIST_PUSH_FRONT(pipeline->dynamic_states, 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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided NULL pointer");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
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;
|
||||
}
|
||||
|
||||
rse_memset(pipeline, 0, sizeof(struct pipeline_t));
|
||||
rse_malloc(pipeline_ptr, sizeof(struct pipeline_t));
|
||||
rse_memset(pipeline_ptr, 0, sizeof(struct pipeline_t));
|
||||
*pipeline = pipeline_ptr;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context,
|
||||
struct pipeline_t* pipeline,
|
||||
const rse_id_t shader_id)
|
||||
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, struct pipeline_t* pipeline,
|
||||
const char* shader_path, const VkShaderStageFlagBits shader_stage)
|
||||
{
|
||||
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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Too many shader stages in pipeline");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Too many shader stages in pipeline\n"));
|
||||
return VULKAN_ERROR_PIPELINE_TOO_MANY_SHADER_STAGES;
|
||||
}
|
||||
|
||||
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 = 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].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].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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid vertex input rate");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Invalid vertex input rate\n"));
|
||||
return VULKAN_ERROR_PIPELINE_INVALID_VERTEX_INPUT_RATE;
|
||||
}
|
||||
|
||||
binding_pipeline_description.binding = binding;
|
||||
binding_pipeline_description.stride = stride;
|
||||
binding_pipeline_description.inputRate = input_rate;
|
||||
|
||||
pipeline->vertex_bindings[pipeline->vertex_bindings_count++] = binding_pipeline_description;
|
||||
RSE_LINKED_LIST_PUSH_FRONT(pipeline->vertex_bindings, binding_pipeline_description);
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid binding in pipeline attribute description");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Invalid binding in pipeline attribute description\n"));
|
||||
return VULKAN_ERROR_PIPELINE_INVALID_BINDING;
|
||||
}
|
||||
if (format >= VK_FORMAT_MAX_ENUM) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Invalid format in pipeline attribute description");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Invalid format in pipeline attribute description\n"));
|
||||
return VULKAN_ERROR_PIPELINE_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
attribute_description.binding = binding;
|
||||
@@ -336,87 +382,114 @@ rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline,
|
||||
attribute_description.format = format;
|
||||
attribute_description.offset = offset;
|
||||
|
||||
pipeline->shader_attributes[pipeline->shader_attributes_count++] = attribute_description;
|
||||
RSE_LINKED_LIST_PUSH_FRONT(pipeline->shader_attributes, attribute_description);
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
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);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
rse_err_t pipeline_add(const struct graphics_context_t* context,
|
||||
struct pipeline_t* pipeline,
|
||||
struct pipeline_infos_t* pipelines_infos)
|
||||
rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline_t* pipeline,
|
||||
struct pipeline_infos_list_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;
|
||||
|
||||
while (pipeline->required_dynamic_states.dynamic_states_count > 0) {
|
||||
uint8_t dynamic_state_found = 0;
|
||||
|
||||
while (RSE_STACK_IS_EMPTY(pipeline->required_dynamic_states) == 0) {
|
||||
dynamic_state_found = 0;
|
||||
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) {
|
||||
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) {
|
||||
dynamic_state_found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dynamic_state_found == 0) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Missing dynamic state in pipeline");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Missing dynamic state in pipeline\n"));
|
||||
return VULKAN_ERROR_PIPELINE_MISSING_DYNAMIC_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for shader modules */
|
||||
if (RSE_LINKED_LIST_IS_EMPTY(context->shader_modules) == 1) {
|
||||
LOGF(_("Missing shader modules in pipeline\n"));
|
||||
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_MODULES;
|
||||
}
|
||||
|
||||
/* Check for required shader stages */
|
||||
if (pipeline->shader_stages_count == 0) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Missing shader stages in pipeline. There must be at least one.");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(
|
||||
_("Missing shader stages in pipeline. There must be at least "
|
||||
"one.\n"));
|
||||
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_STAGES;
|
||||
}
|
||||
|
||||
/* Check for required vertex bindings */
|
||||
if (pipeline->vertex_bindings == NULL) {
|
||||
LOGF(_("Missing vertex bindings in pipeline\n"));
|
||||
return VULKAN_ERROR_PIPELINE_MISSING_VERTEX_BINDINGS;
|
||||
}
|
||||
|
||||
/* Check for required shader attributes */
|
||||
if (pipeline->shader_attributes == NULL) {
|
||||
LOGF(_("Missing shader attributes in pipeline\n"));
|
||||
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_ATTRIBUTES;
|
||||
}
|
||||
|
||||
/* Convert linked list to array */
|
||||
vertex_bindings_count = RSE_LINKED_LIST_TO_ARRAY(pipeline->vertex_bindings, vertex_bindings);
|
||||
shader_attributes_count = RSE_LINKED_LIST_TO_ARRAY(pipeline->shader_attributes, shader_attributes);
|
||||
dynamic_states_count = RSE_LINKED_LIST_TO_ARRAY(pipeline->dynamic_states, dynamic_states);
|
||||
|
||||
/* Build constant pipeline. TODO: Some parts may be customizable */
|
||||
pipeline_add_input_assembly(pipeline);
|
||||
pipeline_add_viewport_state(pipeline);
|
||||
@@ -435,10 +508,10 @@ rse_err_t pipeline_add(const struct graphics_context_t* context,
|
||||
vertex_bindings_ci->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertex_bindings_ci->pNext = NULL;
|
||||
vertex_bindings_ci->flags = 0U;
|
||||
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;
|
||||
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;
|
||||
pipeline_info.pVertexInputState = vertex_bindings_ci;
|
||||
|
||||
pipeline_info.pInputAssemblyState = &pipeline->input_assembly;
|
||||
@@ -453,17 +526,16 @@ rse_err_t pipeline_add(const struct graphics_context_t* context,
|
||||
dynamic_states_ci->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dynamic_states_ci->pNext = NULL;
|
||||
dynamic_states_ci->flags = 0U;
|
||||
dynamic_states_ci->dynamicStateCount = pipeline->dynamic_states_count;
|
||||
dynamic_states_ci->pDynamicStates = pipeline->dynamic_states;
|
||||
dynamic_states_ci->dynamicStateCount = dynamic_states_count;
|
||||
dynamic_states_ci->pDynamicStates = dynamic_states;
|
||||
pipeline_info.pDynamicState = dynamic_states_ci;
|
||||
|
||||
pipeline_info.layout = pipeline->pipeline_layout;
|
||||
pipeline_info.renderPass = context->vulkan_handles.render_pass;
|
||||
pipeline_info.layout = context->pipelines_data.pipeline_layout;
|
||||
pipeline_info.renderPass = context->render_pass;
|
||||
pipeline_info.subpass = 0;
|
||||
pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // TODO: Add possiblity do derive from another pipeline
|
||||
pipeline_info.basePipelineIndex = -1;
|
||||
|
||||
pipelines_infos->create_infos[pipelines_infos->pipeline_create_infos_count++] = pipeline_info;
|
||||
RSE_LINKED_LIST_PUSH_BACK(*pipelines_infos, pipeline_info);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -471,17 +543,20 @@ rse_err_t pipeline_add(const struct graphics_context_t* context,
|
||||
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->vulkan_handles.device, context->pipelines_data.pipelines[i], NULL);
|
||||
vkDestroyPipeline(context->device, context->pipelines_data.pipelines[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < context->shader_modules.shader_modules_count; i++) {
|
||||
vkDestroyShaderModule(context->vulkan_handles.device, context->shader_modules.shader_modules[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);
|
||||
}
|
||||
}
|
||||
@@ -12,37 +12,39 @@
|
||||
#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"
|
||||
|
||||
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);
|
||||
#define MAX_SHADER_STAGES 12U
|
||||
|
||||
struct pipeline_t;
|
||||
struct pipeline_infos_list_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize pipeline builder
|
||||
*
|
||||
* @param[out] pipeline Pipeline to initialize. Must not be NULL
|
||||
* @param[out] pipeline Pipeline to initialize. Must be NULL
|
||||
* @return RSE_SUCCESS on success, error code otherwise
|
||||
*/
|
||||
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_id ID of the shader we want to add to pipeline
|
||||
* @param[in] shader_path Path to the shader file
|
||||
* @param[in] shader_stage Shader stage to add
|
||||
* @return RSE_SUCCESS on success, error code otherwise
|
||||
*/
|
||||
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context,
|
||||
struct pipeline_t* pipeline,
|
||||
const rse_id_t shader_id);
|
||||
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context, struct pipeline_t* pipeline,
|
||||
const char* shader_path, const VkShaderStageFlagBits shader_stage);
|
||||
|
||||
/**
|
||||
* @brief Add vertex input binding to pipeline
|
||||
@@ -53,9 +55,7 @@ rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context,
|
||||
* @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,11 +68,8 @@ rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline,
|
||||
* @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
|
||||
@@ -83,27 +80,6 @@ rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline,
|
||||
*/
|
||||
rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, const VkDynamicState dynamic_state);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
@@ -113,19 +89,18 @@ rse_err_t pipeline_add_push_constants(struct pipeline_t* pipeline,
|
||||
* @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_t* pipelines);
|
||||
rse_err_t pipelines_build(struct graphics_context_t* context, struct pipeline_infos_list_t* pipelines, struct descriptor_set_handle_t* descriptor_set_handle);
|
||||
|
||||
/**
|
||||
* @brief Add pipeline to list of pipelines
|
||||
*
|
||||
* @param[in] context Graphics context handle
|
||||
* @param[in] pipeline Pipeline to add
|
||||
* @param[out] pipeline_infos Array of pipelines infos, where new info should be added
|
||||
* @param[out] pipeline_info List of pipelines infos, where new info should be added
|
||||
* @return RSE_SUCCESS on success, error code otherwise
|
||||
*/
|
||||
rse_err_t pipeline_add(const struct graphics_context_t* context,
|
||||
struct pipeline_t* pipeline,
|
||||
struct pipeline_infos_t* pipelines_infos);
|
||||
rse_err_t add_pipeline(const struct graphics_context_t* context, struct pipeline_t* pipeline,
|
||||
struct pipeline_infos_list_t** pipelines);
|
||||
|
||||
/**
|
||||
* @brief Destroy all pipelines
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* @file renderer.c
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2025-10-15
|
||||
*
|
||||
* @copyright Copyright (c) 2025
|
||||
*
|
||||
*/
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "src/graphics_context.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "utilities/errors_common.h"
|
||||
|
||||
rse_err_t renderer_get_new_id(struct graphics_context_t* context, uint32_t* id)
|
||||
{
|
||||
if (context->renderer_data.renderers_count >= RENDERER_MAX_COUNT) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Reached maximum number of renderers");
|
||||
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
*id = context->renderer_data.renderers_count++;
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t renderer_set_render_function(struct graphics_context_t* context,
|
||||
uint32_t renderer_id,
|
||||
renderer_function_t renderer_function)
|
||||
{
|
||||
context->renderer_data.renderers[renderer_id].render_function = renderer_function;
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* @file renderer.h
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2025-10-15
|
||||
*
|
||||
* @copyright Copyright (c) 2025
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
|
||||
#include "src/graphics_context.h"
|
||||
#include "utilities/commons.h"
|
||||
|
||||
rse_err_t renderer_get_new_id(struct graphics_context_t* context, uint32_t* id);
|
||||
|
||||
rse_err_t renderer_set_render_function(struct graphics_context_t* context,
|
||||
uint32_t renderer_id,
|
||||
renderer_function_t renderer_function);
|
||||
|
||||
#endif /* !RENDERER_H */
|
||||
+101
-244
@@ -1,32 +1,17 @@
|
||||
#include "rse_graphics.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#endif
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_error.h>
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <SDL3/SDL_render.h>
|
||||
#ifdef __unix__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#include <cglm/util.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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/time_utils.h"
|
||||
#include "utilities/file_utils.h"
|
||||
#include "utilities/localization.h"
|
||||
#include "utilities/logger.h"
|
||||
#include "vulkan_base.h"
|
||||
#include "vulkan_commons.h"
|
||||
#include "vulkan_image.h"
|
||||
@@ -37,268 +22,140 @@ struct rse_graphics_context_t
|
||||
struct 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 rse_graphics_init(struct rse_graphics_context_t** context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
struct rse_graphics_context_t* ctx_ptr = NULL;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
if (*context != NULL) {
|
||||
LOGF(_("Graphics already initialized!\n"));
|
||||
return RSE_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
STATUS_CHECK(window_init(&context->window_handle, &context->is_framebuffer_resized));
|
||||
STATUS_CHECK(vulkan_init(context));
|
||||
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));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#define TEXTURES_COUNT (3U)
|
||||
rse_err_t rse_graphics_test_function(struct graphics_context_t* context)
|
||||
void rse_graphics_test_function(struct rse_graphics_context_t* rse_context)
|
||||
{
|
||||
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;
|
||||
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};
|
||||
|
||||
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));
|
||||
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();
|
||||
|
||||
// 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}, {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 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_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}},
|
||||
};
|
||||
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}}};
|
||||
|
||||
// FIXME: Temporary array of vertices, for testing purposes
|
||||
uint16_t indices[] = {0, 1, 2, 2, 3, 0, 0, 3, 2, 2, 1, 0};
|
||||
uint16_t indices[] = {0, 1, 2, 2, 3, 0};
|
||||
|
||||
STATUS_CHECK(mesh_create(context, vertices, indices, 4, 12, &mesh_id_1));
|
||||
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);
|
||||
}
|
||||
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});
|
||||
|
||||
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);
|
||||
}
|
||||
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});
|
||||
|
||||
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);
|
||||
}
|
||||
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});
|
||||
|
||||
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));
|
||||
// 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);
|
||||
|
||||
/* Initialize pipeline */
|
||||
pipeline_builder_init(&pipeline);
|
||||
|
||||
shader_create_module(context, &vertex_shader_id, "shader.vert.num", VK_SHADER_STAGE_VERTEX_BIT);
|
||||
pipeline_add_shader_stage(context, &pipeline, 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);
|
||||
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);
|
||||
|
||||
pipeline_add_vertex_input_binding(&pipeline, 0, sizeof(struct vertex_t), VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
pipeline_add_vertex_input_binding(pipeline, 0, sizeof(struct vertex_t), VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
pipeline_add_vertex_input_binding(pipeline, 1, sizeof(struct instance_data_t), VK_VERTEX_INPUT_RATE_INSTANCE);
|
||||
|
||||
pipeline_add_vertex_input_attribute(&pipeline, 0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(struct vertex_t, pos));
|
||||
pipeline_add_vertex_input_attribute(&pipeline, 0, 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, 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_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);
|
||||
|
||||
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);
|
||||
sampler_create(context, &context->sampler);
|
||||
|
||||
pipeline_add(context, &pipeline, &pipeline_infos);
|
||||
descriptor_initialize_handle(&descriptor_set_handle);
|
||||
|
||||
pipelines_build(context, &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);
|
||||
|
||||
renderer_get_new_id(context, &static_mesh_renderer);
|
||||
descriptors_build(context, descriptor_set_handle);
|
||||
|
||||
renderer_set_render_function(context, static_mesh_renderer, render_static_mesh);
|
||||
add_pipeline(context, pipeline, &pipeline_infos);
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
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);
|
||||
}
|
||||
|
||||
rse_err_t rse_graphics_run(struct graphics_context_t* context)
|
||||
int rse_graphics_run(void* arg)
|
||||
{
|
||||
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(vulkan_run(context));
|
||||
STATUS_CHECK(run_vulkan(context));
|
||||
STATUS_CHECK(window_loop(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);
|
||||
deinit_vulkan(context);
|
||||
|
||||
window_terminate(context->window_handle);
|
||||
rse_free(context->debug_overlay.pixels);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "text_renderer.h"
|
||||
#include <stdint.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "freetype/fttypes.h"
|
||||
#include "font_errors.h"
|
||||
#include "src/mesh_controller.h"
|
||||
#include "src/math.h"
|
||||
#include "src/vulkan_commons.h"
|
||||
#include "utilities/logger.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "utilities/errors_common.h"
|
||||
#include "utilities/localization.h"
|
||||
#include "vulkan_image.h"
|
||||
|
||||
#include "vulkan/vulkan_core.h"
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
/* Load only ASCII characters that are needed. TODO: Add unicode characters */
|
||||
#define FONT_CHARACTERS_COUNT 94
|
||||
#define FONT_START_CHARACTER 32U
|
||||
|
||||
#define FONT_SIZE 200
|
||||
/* Use this to scale font to 0.0 <-> 1.0 range */
|
||||
#define FONT_SCALING 1.0f/FONT_SIZE
|
||||
|
||||
struct font_data_t {
|
||||
uint16_t mesh_id;
|
||||
uint16_t texture_id;
|
||||
float width;
|
||||
float height;
|
||||
float bearing_x;
|
||||
float bearing_y;
|
||||
float advance;
|
||||
};
|
||||
|
||||
struct font_data_t g_fonts_data[FONT_CHARACTERS_COUNT];
|
||||
|
||||
FT_Library g_library;
|
||||
rse_err_t fonts_initialize()
|
||||
{
|
||||
FT_Error error;
|
||||
|
||||
memset(g_fonts_data, 0, sizeof(g_fonts_data));
|
||||
error = FT_Init_FreeType(&g_library);
|
||||
|
||||
if (error) {
|
||||
LOGF(_("Font initialization failed\n"));
|
||||
return FONT_ERROR_INIT_FAILED;
|
||||
}
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
FT_Error error;
|
||||
FT_Face face;
|
||||
size_t iter = 0U;
|
||||
struct vertex_t font_vertices[4];
|
||||
uint16_t font_indices[] = {0, 1, 2, 2, 3, 0};
|
||||
|
||||
error = FT_New_Face(g_library, file_path, 0, &face);
|
||||
|
||||
if (error) {
|
||||
LOGF(_("Failed to load new font\n"));
|
||||
return FONT_ERROR_LOAD_FAILED;
|
||||
}
|
||||
|
||||
/* By default we set font size to FONT_SIZE.*/
|
||||
error = FT_Set_Pixel_Sizes(face, 0, FONT_SIZE);
|
||||
|
||||
if (error) {
|
||||
LOGF(_("Failed to change font size\n"));
|
||||
return FONT_ERROR_CHANGE_SIZE_FAILED;
|
||||
}
|
||||
|
||||
/* Convert font face to textured image */
|
||||
/* Loading only needed ascii characters, from 40 (space) to 126 (~) */
|
||||
for (iter = 0U; iter < face->num_glyphs && iter < FONT_CHARACTERS_COUNT; ++iter) {
|
||||
error = FT_Load_Char(face, iter + FONT_START_CHARACTER, FT_LOAD_RENDER);
|
||||
|
||||
if (error) {
|
||||
LOGF(_("Failed to load glyph into memeory\n"));
|
||||
return FONT_ERROR_LOAD_GLYPH_FAILED;
|
||||
}
|
||||
|
||||
g_fonts_data[iter].width = face->glyph->bitmap.width;
|
||||
g_fonts_data[iter].height = face->glyph->bitmap.rows;
|
||||
g_fonts_data[iter].bearing_x = face->glyph->bitmap_left;
|
||||
g_fonts_data[iter].bearing_y = face->glyph->bitmap_top;
|
||||
g_fonts_data[iter].advance = (face->glyph->advance.x >> 6);
|
||||
|
||||
/* Skip generating mesh if character is space */
|
||||
if ((char)(iter + FONT_START_CHARACTER) == ' ') {
|
||||
continue;
|
||||
}
|
||||
|
||||
font_vertices[0] = (struct vertex_t) {
|
||||
{g_fonts_data[iter].bearing_x, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}
|
||||
};
|
||||
font_vertices[1] = (struct vertex_t) {
|
||||
{g_fonts_data[iter].bearing_x + g_fonts_data[iter].width, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}
|
||||
};
|
||||
font_vertices[2] = (struct vertex_t) {
|
||||
{g_fonts_data[iter].bearing_x + g_fonts_data[iter].width, g_fonts_data[iter].height, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}
|
||||
};
|
||||
font_vertices[3] = (struct vertex_t) {
|
||||
{g_fonts_data[iter].bearing_x, g_fonts_data[iter].height, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}
|
||||
};
|
||||
|
||||
g_fonts_data[iter].mesh_id = create_mesh(context, font_vertices, font_indices, 8, 6);
|
||||
|
||||
STATUS_CHECK(load_texture_from_bitmat(context, face->glyph->bitmap.buffer,
|
||||
face->glyph->bitmap.width,
|
||||
face->glyph->bitmap.rows,
|
||||
&g_fonts_data[iter].texture_id, VK_FORMAT_R8_SRGB));
|
||||
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
rse_err_t print_text(struct graphics_context_t* context, const char* text, struct vec3_t position, float font_size)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
size_t iter = 0U;
|
||||
size_t charactet_code = 0U;
|
||||
float advance = 0.0f;
|
||||
float x_pos = 0.0f;
|
||||
float y_pos = 0.0f;
|
||||
float scale = FONT_SCALING * font_size;
|
||||
|
||||
for (iter = 0U; iter < strlen(text); ++iter) {
|
||||
if (text[iter] == ' ') {
|
||||
advance += g_fonts_data[charactet_code].advance * scale;
|
||||
continue;
|
||||
}
|
||||
charactet_code = (size_t)text[iter] - FONT_START_CHARACTER;
|
||||
|
||||
x_pos = position.x + advance - (g_fonts_data[charactet_code].bearing_x * scale);
|
||||
y_pos = position.y - (g_fonts_data[charactet_code].bearing_y * scale);
|
||||
create_mesh_instance(context, g_fonts_data[charactet_code].mesh_id, (struct instance_data_t){
|
||||
{x_pos, y_pos, 0.0f},
|
||||
{0.0f, 0.0f, math_deg_to_radians(0.0f)},
|
||||
FONT_SCALING * font_size,
|
||||
g_fonts_data[charactet_code].texture_id
|
||||
});
|
||||
|
||||
advance += g_fonts_data[charactet_code].advance * scale;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
/**
|
||||
* @file text_renderer.h
|
||||
* @author Piotr Krygier (piotr.krygier@meshsystems.com)
|
||||
* @brief Load and render text on screen or in space
|
||||
* @version 0.1
|
||||
* @date 2024-02-02
|
||||
*
|
||||
* @copyright Copyright (c) 2023
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TEXT_RENDERER_H
|
||||
#define TEXT_RENDERER_H
|
||||
|
||||
#include "utilities/commons.h"
|
||||
#include "graphics_context.h"
|
||||
#include "math.h"
|
||||
|
||||
rse_err_t fonts_initialize(void);
|
||||
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path);
|
||||
rse_err_t print_text(struct graphics_context_t* context, const char* text, struct vec3_t position, float font_size);
|
||||
|
||||
#endif // !TEXT_RENDERER_H_
|
||||
@@ -14,7 +14,6 @@
|
||||
#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"
|
||||
@@ -23,7 +22,8 @@
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
#pragma GCC diagnostic ignored "-Wparentheses"
|
||||
#endif
|
||||
#include "vk_mem_alloc.h"
|
||||
#include <cstdio>
|
||||
#include "vma/vk_mem_alloc.h"
|
||||
#ifdef __unix__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
+151
-245
@@ -12,27 +12,28 @@
|
||||
#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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <vma/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_buffers.h"
|
||||
#include "vulkan_commands.h"
|
||||
#include "vulkan_errors.h"
|
||||
|
||||
#include "vulkan_commons.h"
|
||||
#include "vulkan_commands.h"
|
||||
#include "vulkan_buffers.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"
|
||||
@@ -47,10 +48,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;
|
||||
@@ -59,15 +60,17 @@ 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) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage);
|
||||
log_level = LOGLEVEL_ERROR;
|
||||
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage);
|
||||
log_level = LOGLEVEL_WARNING;
|
||||
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage);
|
||||
log_level = LOGLEVEL_INFO;
|
||||
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
|
||||
SDL_LogTrace(SDL_LOG_CATEGORY_GPU, "%s", callback_data->pMessage);
|
||||
log_level = LOGLEVEL_TRACE;
|
||||
}
|
||||
|
||||
LOGGER(log_level, callback_data->pMessage);
|
||||
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
@@ -80,10 +83,9 @@ 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;
|
||||
|
||||
@@ -109,19 +111,20 @@ 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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to setup debug messenger");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Failed to setup debug messenger\n"));
|
||||
return VULKAN_ERROR_DEBUG_MESSENGER_CREATION_FAILED;
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
@@ -134,9 +137,8 @@ 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");
|
||||
@@ -174,7 +176,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",
|
||||
};
|
||||
@@ -199,47 +201,43 @@ static rse_err_t create_instance(VkInstance* instance)
|
||||
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions_properties);
|
||||
|
||||
#ifndef NDEBUG
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "Supported Layers: ");
|
||||
LOGD("Supported Layers: \n");
|
||||
for (i = 0; i < layersCount; i++) {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "\t-%s", layer_properties[i].layerName);
|
||||
LOGD("\t-%s\n", layer_properties[i].layerName);
|
||||
}
|
||||
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "Supported Extensions: ");
|
||||
LOGD("Supported Extensions: \n");
|
||||
for (size_t i = 0; i < extensionCount; i++) {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_GPU, "\t-%s", extensions_properties[i].extensionName);
|
||||
LOGD("\t-%s\n", 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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Selected layer is not supported: %s", layer_properties[j].layerName);
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
if(supported == 0) {
|
||||
LOGF(_("Selected layer is not supported: %s\n"), layer_properties[j].layerName);
|
||||
return VULKAN_ERROR_LAYER_NOT_SUPPORTED;
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
for(j = 0; j < extensionCount; ++j) {
|
||||
if(strcmp(extensions_properties[j].extensionName, enabled_instance_extensions_names[i]) == 0) {
|
||||
supported = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (supported == 0) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU,
|
||||
"Selected extension is not supported: %s",
|
||||
extensions_properties[j].extensionName);
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
if(supported == 0) {
|
||||
LOGF(_("Selected extension is not supported: %s\n"), extensions_properties[j].extensionName);
|
||||
return VULKAN_ERROR_EXTENSION_NOT_SUPPORTED;
|
||||
}
|
||||
supported = 0;
|
||||
}
|
||||
@@ -266,8 +264,8 @@ static rse_err_t create_instance(VkInstance* instance)
|
||||
|
||||
result = vkCreateInstance(&create_info, NULL, instance);
|
||||
if (VK_SUCCESS != result) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Vulkan instance initialization failed");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Vulkan instance initialization failed\n"));
|
||||
return VULKAN_ERROR_INSTANCE_INIT_FAILED;
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
@@ -281,8 +279,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)) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create window surface. Error: %s", SDL_GetError());
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Failed to create window surface. Error: %s\n"), SDL_GetError());
|
||||
return VULKAN_ERROR_SURFACE_CREATION_FAILED;
|
||||
}
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
@@ -298,16 +296,15 @@ 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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "No physical graphical physical devices found in system");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("No physical graphical physical devices found in system\n"));
|
||||
return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND;
|
||||
}
|
||||
|
||||
rse_malloc(physical_devices, sizeof(VkPhysicalDevice) * physical_device_count);
|
||||
@@ -331,12 +328,10 @@ static rse_err_t pick_physical_device(VkInstance instance, VkPhysicalDevice* phy
|
||||
|
||||
if (VK_NULL_HANDLE == physical_device) {
|
||||
/* No suitable device found */
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "No suitable physical device found");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("No suitable physical device found\n"));
|
||||
return VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND;
|
||||
}
|
||||
|
||||
rse_free(physical_devices);
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -354,64 +349,39 @@ 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;
|
||||
int64_t graphics_family_idx = 0;
|
||||
uint32_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 device_feature_supported = VK_FALSE;
|
||||
VkBool32 bindless_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 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 */
|
||||
/* Enable descriptori 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->vulkan_handles.physical_device, &physical_features2);
|
||||
vkGetPhysicalDeviceFeatures2(context->physical_device, &physical_features2);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Get information about supported queue families */
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device,
|
||||
&queue_families_property_count,
|
||||
NULL);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(context->physical_device, &queue_families_property_count, NULL);
|
||||
rse_malloc(queue_family_properties, sizeof(VkQueueFamilyProperties) * queue_families_property_count);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device,
|
||||
&queue_families_property_count,
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(context->physical_device, &queue_families_property_count,
|
||||
queue_family_properties);
|
||||
|
||||
/* Get queue families that support graphics operations AND have most
|
||||
@@ -419,12 +389,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) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Checking queue: %d", familyIdx);
|
||||
LOGI("Checking queue: %ld", familyIdx);
|
||||
if (queue_family_properties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
if (queue_family_properties[familyIdx].queueCount > numberOfQueues) {
|
||||
graphics_family_idx = familyIdx;
|
||||
graphics_family_found = true;
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Setting graphics family idx to %d", familyIdx);
|
||||
LOGI("Setting graphics family idx to %ld", familyIdx);
|
||||
numberOfQueues = queue_family_properties[familyIdx].queueCount;
|
||||
device_queues_count++;
|
||||
}
|
||||
@@ -432,10 +402,7 @@ static rse_err_t create_device(struct graphics_context_t* context)
|
||||
|
||||
/* Check for family with surface support */
|
||||
VkBool32 presentSupport = VK_FALSE;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(context->vulkan_handles.physical_device,
|
||||
familyIdx,
|
||||
context->vulkan_handles.surface,
|
||||
&presentSupport);
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(context->physical_device, familyIdx, context->surface, &presentSupport);
|
||||
if (presentSupport) {
|
||||
presentation_familiy_idx = familyIdx;
|
||||
presentation_familiy_found = true;
|
||||
@@ -452,25 +419,23 @@ static rse_err_t create_device(struct graphics_context_t* context)
|
||||
}
|
||||
|
||||
/* This is not magic number ;P. We want to support exact number of required queues flags */
|
||||
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;
|
||||
if (0 > graphics_family_idx) {
|
||||
LOGF(_("Selected physical device does not support required queue capabilities\n"));
|
||||
return VULKAN_ERROR_QUEUE_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
context->queue_family_indices[QUEUE_FAMILY_INDEX_GRAPHICS] = graphics_family_idx;
|
||||
context->queue_family_indices[QUEUE_FAMILY_INDEX_PRESENTATION] = presentation_familiy_idx;
|
||||
context->queue_family_indices[0] = graphics_family_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;
|
||||
@@ -498,19 +463,13 @@ 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->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;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
vkGetDeviceQueue(context->device, graphics_family_idx, 0, &context->graphics_queue);
|
||||
vkGetDeviceQueue(context->device, presentation_familiy_idx, 0, &context->present_queue);
|
||||
|
||||
rse_free(presentation_familiy_queue_priorities);
|
||||
rse_free(graphics_familiy_queue_priorities);
|
||||
@@ -533,19 +492,20 @@ 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->vulkan_handles.physical_device;
|
||||
allocator_create_info.device = context->vulkan_handles.device;
|
||||
allocator_create_info.instance = context->vulkan_handles.instance;
|
||||
allocator_create_info.physicalDevice = context->physical_device;
|
||||
allocator_create_info.device = context->device;
|
||||
allocator_create_info.instance = context->instance;
|
||||
allocator_create_info.pVulkanFunctions = &vulkan_functions;
|
||||
|
||||
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;
|
||||
if (VK_SUCCESS != vmaCreateAllocator(&allocator_create_info, &context->allocator)) {
|
||||
LOGF(_("Failed to create Vulkan Memory Allocator\n"));
|
||||
return VULKAN_ERROR_ALLOCATOR_CREATION_FAILED;
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a Sync Objects
|
||||
*
|
||||
@@ -555,57 +515,33 @@ 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;
|
||||
|
||||
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;
|
||||
VkFenceCreateInfo fenceInfo = {0};
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||
|
||||
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 vulkan_init(struct graphics_context_t* context)
|
||||
|
||||
rse_err_t init_vulkan(struct graphics_context_t* context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
|
||||
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_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_device(context));
|
||||
STATUS_CHECK(create_memory_allocator(context));
|
||||
STATUS_CHECK(create_render_pass(context));
|
||||
@@ -617,135 +553,105 @@ rse_err_t vulkan_init(struct graphics_context_t* context)
|
||||
return status;
|
||||
}
|
||||
|
||||
rse_err_t vulkan_run(struct graphics_context_t* context)
|
||||
rse_err_t run_vulkan(struct graphics_context_t* context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
|
||||
STATUS_CHECK(create_sync_objects(context));
|
||||
|
||||
init_time();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
rse_err_t vulkan_draw_frame(struct graphics_context_t* context)
|
||||
rse_err_t draw_frame(struct graphics_context_t* context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
uint32_t image_index = 0U;
|
||||
VkResult result = VK_FALSE;
|
||||
VkSubmitInfo2 submit_info = {0};
|
||||
VkSubmitInfo submit_info = {0};
|
||||
VkSemaphore wait_semaphores[1];
|
||||
VkPipelineStageFlags wait_stages[1];
|
||||
VkSemaphore signal_semaphores[1];
|
||||
VkPresentInfoKHR present_info = {0};
|
||||
VkSwapchainKHR swap_chains[1];
|
||||
VkSemaphoreWaitInfo timeline_wait_info = {0};
|
||||
VkSemaphoreSubmitInfo timeline_signal_info = {0};
|
||||
VkSemaphoreSubmitInfo binary_signal_info = {0};
|
||||
VkSemaphoreSubmitInfo binary_wait_info = {0};
|
||||
VkSemaphoreSubmitInfo signal_infos[2] = {0};
|
||||
VkCommandBufferSubmitInfo cmd_buffer_submit_info = {0};
|
||||
|
||||
timeline_wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;
|
||||
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;
|
||||
vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
|
||||
|
||||
vkWaitSemaphores(context->vulkan_handles.device, &timeline_wait_info, UINT64_MAX);
|
||||
update_time();
|
||||
|
||||
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);
|
||||
result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to acquire swapchain image");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Failed to acquire swapchain image\n"));
|
||||
return VULKAN_ERROR_ACQUIRE_SWAPCHAIN_FAILED;
|
||||
}
|
||||
|
||||
// update_uniform_buffers(context);
|
||||
|
||||
/* Only reset the fence if we are submitting work */
|
||||
vkResetFences(context->device, 1, &context->in_flight_fences[context->current_frame]);
|
||||
reset_command_buffer(context);
|
||||
|
||||
record_command_buffer(context, image_index);
|
||||
|
||||
context->frame_finished_idx++;
|
||||
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
|
||||
binary_signal_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
|
||||
present_info.waitSemaphoreCount = 1;
|
||||
present_info.pWaitSemaphores = &context->vulkan_handles.render_finished_semaphores[image_index];
|
||||
present_info.pWaitSemaphores = &context->render_finished_semaphores[context->current_frame];
|
||||
|
||||
swap_chains[0] = context->swapchain_data.swapchain;
|
||||
swap_chains[0] = context->swapchain;
|
||||
present_info.swapchainCount = 1;
|
||||
present_info.pSwapchains = swap_chains;
|
||||
|
||||
present_info.pImageIndices = &image_index;
|
||||
|
||||
result = vkQueuePresentKHR(context->vulkan_handles.present_queue, &present_info);
|
||||
result = vkQueuePresentKHR(context->present_queue, &present_info);
|
||||
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || context->is_framebuffer_resized) {
|
||||
context->is_framebuffer_resized = false;
|
||||
recreate_swapchain(context);
|
||||
} else if (result != VK_SUCCESS) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to submit draw command buffer!");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Failed to submit draw command buffer!\n"));
|
||||
return VULKAN_ERROR_DRAW_FAILED;
|
||||
}
|
||||
|
||||
context->current_frame = (context->current_frame + 1) % SWAP_BUFFER_COUNT;
|
||||
|
||||
return status;
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
void vulkan_deinit(struct graphics_context_t* context)
|
||||
void deinit_vulkan(struct graphics_context_t* context)
|
||||
{
|
||||
vkDeviceWaitIdle(context->vulkan_handles.device);
|
||||
destroy_buffers(context);
|
||||
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
|
||||
vkDestroySemaphore(context->vulkan_handles.device, context->vulkan_handles.image_available_semaphores[i], NULL);
|
||||
vkDestroySemaphore(context->vulkan_handles.device, context->vulkan_handles.render_finished_semaphores[i], NULL);
|
||||
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.timeline_semaphore, NULL);
|
||||
|
||||
destroy_textures(context);
|
||||
|
||||
@@ -756,9 +662,9 @@ void vulkan_deinit(struct graphics_context_t* context)
|
||||
destroy_pipelines(context);
|
||||
destroy_descriptors(context);
|
||||
|
||||
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);
|
||||
vmaDestroyAllocator(context->allocator);
|
||||
vkDestroyDevice(context->device, NULL);
|
||||
DestroyDebugUtilsMessengerEXT(context->instance, context->debug_messenger, NULL);
|
||||
vkDestroySurfaceKHR(context->instance, context->surface, NULL);
|
||||
vkDestroyInstance(context->instance, NULL);
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
* @param context Graphics context handle
|
||||
* @return rse_err_t 0 on success. Status code on failure
|
||||
*/
|
||||
rse_err_t vulkan_init(struct graphics_context_t* context);
|
||||
rse_err_t init_vulkan(struct graphics_context_t* context);
|
||||
|
||||
/**
|
||||
* @brief Initialize Vulkan backend with descriptors
|
||||
@@ -31,14 +31,14 @@ rse_err_t vulkan_init(struct graphics_context_t* context);
|
||||
* @param context Graphics context handle
|
||||
* @return rse_err_t 0 on success. Status code on failure
|
||||
*/
|
||||
rse_err_t vulkan_run(struct graphics_context_t* context);
|
||||
rse_err_t run_vulkan(struct graphics_context_t* context);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize Vulkan backend
|
||||
*
|
||||
* @param context Graphics context handle
|
||||
* */
|
||||
void vulkan_deinit(struct graphics_context_t* context);
|
||||
void deinit_vulkan(struct graphics_context_t* context);
|
||||
|
||||
/**
|
||||
* @brief Draw frame on the screen.
|
||||
@@ -46,6 +46,6 @@ void vulkan_deinit(struct graphics_context_t* context);
|
||||
* @param context Graphics context
|
||||
* @return rse_err_t
|
||||
*/
|
||||
rse_err_t vulkan_draw_frame(struct graphics_context_t* context);
|
||||
rse_err_t draw_frame(struct graphics_context_t* context);
|
||||
|
||||
#endif /* GRAPHICS_VULKANBASE_H */
|
||||
+184
-218
@@ -1,24 +1,22 @@
|
||||
#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/vector.h"
|
||||
#include "utilities/linked_list.h"
|
||||
#include "utilities/localization.h"
|
||||
#include "utilities/logger.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
|
||||
@@ -27,18 +25,14 @@
|
||||
* @param dst Destination buffer
|
||||
* @param size Size of buffer to copy
|
||||
*/
|
||||
static rse_err_t copy_buffer(struct graphics_context_t* context,
|
||||
VkBuffer src,
|
||||
VkBuffer dst,
|
||||
VkDeviceSize size,
|
||||
VkDeviceSize dest_offset)
|
||||
static void 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 = VK_NULL_HANDLE;
|
||||
VkCommandBuffer command_buffer = {0};
|
||||
|
||||
STATUS_CHECK(begin_single_time_command(context, &command_buffer));
|
||||
command_buffer = begin_single_time_command(context);
|
||||
|
||||
copy_region.srcOffset = 0U;
|
||||
copy_region.dstOffset = dest_offset;
|
||||
@@ -46,9 +40,7 @@ static rse_err_t copy_buffer(struct graphics_context_t* context,
|
||||
|
||||
vkCmdCopyBuffer(command_buffer, src, dst, 1, ©_region);
|
||||
|
||||
STATUS_CHECK(end_single_time_comands(context, command_buffer));
|
||||
|
||||
return status;
|
||||
end_single_time_comands(context, command_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,16 +59,13 @@ 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->render_targets[RENDER_TARGET_3D].vertex_buffer));
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
|
||||
&context->vertex_buffer));
|
||||
|
||||
context->render_targets[RENDER_TARGET_3D].vertex_buffer.allocated_size = 0;
|
||||
|
||||
return status;
|
||||
context->vertex_buffer.allocated_size = 0;
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,19 +83,17 @@ 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->render_targets[RENDER_TARGET_3D].index_buffer));
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
|
||||
&context->index_buffer));
|
||||
|
||||
context->render_targets[RENDER_TARGET_3D].index_buffer.allocated_size = 0;
|
||||
context->index_buffer.allocated_size = 0;
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create instance buffer alongside draw indirect command buffer
|
||||
* @brief Create instance buffers
|
||||
*
|
||||
* @param[in/out] context Graphics context handle
|
||||
* @return RSE_ERROR_NO_ERROR on success.
|
||||
@@ -114,54 +101,48 @@ 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;
|
||||
|
||||
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));
|
||||
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]));
|
||||
|
||||
context->render_targets[RENDER_TARGET_3D].instance_buffer.allocated_size = 0;
|
||||
context->instance_buffers[iter].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 buffer_create_uniform(struct graphics_context_t* context,
|
||||
struct vulkan_buffer_t* buffer,
|
||||
VkDeviceSize buffer_size)
|
||||
rse_err_t create_uniform_buffer(struct graphics_context_t* context, struct vulkan_buffer_t* buffer)
|
||||
{
|
||||
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));
|
||||
|
||||
context->uniform_buffers.vulkan_buffers[context->uniform_buffers.vulkan_buffers_count++] = *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);
|
||||
|
||||
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};
|
||||
@@ -185,16 +166,15 @@ rse_err_t create_buffer(struct graphics_context_t* context,
|
||||
create_info.pUserData = VK_NULL_HANDLE;
|
||||
create_info.priority = 0.0f;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
static int count = 1;
|
||||
LOGI("Created buffers: %d\n", count++);
|
||||
|
||||
buffer->allocated_size = size;
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
@@ -202,153 +182,117 @@ rse_err_t create_buffer(struct graphics_context_t* context,
|
||||
|
||||
void destroy_buffer(struct graphics_context_t* context, struct vulkan_buffer_t* buffer)
|
||||
{
|
||||
vmaDestroyBuffer(context->vulkan_handles.allocator, buffer->buffer, buffer->allocation);
|
||||
vmaDestroyBuffer(context->allocator, buffer->buffer, buffer->allocation);
|
||||
|
||||
}
|
||||
|
||||
rse_err_t add_vertices(struct graphics_context_t* context,
|
||||
uint16_t mesh_id,
|
||||
size_t vertices_count,
|
||||
const struct vertex_t* vertices,
|
||||
size_t indices_count,
|
||||
uint16_t* indices)
|
||||
void update_uniform_buffers(struct graphics_context_t* context, const struct vulkan_buffer_t* buffer)
|
||||
{
|
||||
// static uint16_t last_index = 0U;
|
||||
// uint16_t max_index = 0U;
|
||||
// size_t idx = 0U;
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
struct vulkan_buffer_t staging_buffer;
|
||||
size_t vertices_size = sizeof(vertices[0]) * vertices_count;
|
||||
size_t indices_size = sizeof(indices[0]) * indices_count;
|
||||
size_t staging_buffer_size = vertices_size > indices_size ? vertices_size : indices_size;
|
||||
struct 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;
|
||||
|
||||
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;
|
||||
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, ¢er, &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 status = RSE_ERROR_NO_ERROR;
|
||||
VkDrawIndexedIndirectCommand draw_indirect_command;
|
||||
struct vulkan_buffer_t staging_buffer;
|
||||
size_t vertices_size = sizeof(vertices[0]) * vertex_data->count;
|
||||
size_t indices_size = sizeof(indices[0]) * index_data->count;
|
||||
|
||||
struct vulkan_buffer_t* index_buffer = &context->index_buffer;
|
||||
|
||||
/* Creating staging buffer*/
|
||||
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, 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));
|
||||
|
||||
/* 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, vertex_buffer->buffer, vertices_size, vertex_buffer->allocated_size);
|
||||
vertex_buffer->allocated_size += vertices_size;
|
||||
copy_buffer(context, staging_buffer.buffer, context->vertex_buffer.buffer, vertices_size,
|
||||
context->vertex_buffer.allocated_size);
|
||||
|
||||
vertex_data->buffer_offset = context->vertex_offset;
|
||||
context->vertex_offset += vertex_data->count;
|
||||
context->vertex_buffer.allocated_size += vertices_size;
|
||||
|
||||
/* Fill staging buffer with index data */
|
||||
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);
|
||||
|
||||
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;
|
||||
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));
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t mesh_add_instance(struct graphics_context_t* context, uint32_t mesh_id, struct instance_data_t* instance_data)
|
||||
{
|
||||
/* 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 update_mesh_instances(struct graphics_context_t* context, uint16_t mesh_id,
|
||||
struct instance_data_t* instance_data)
|
||||
{
|
||||
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;
|
||||
VkDrawIndexedIndirectCommand draw_indirect_command;
|
||||
struct vulkan_buffer_t staging_buffer;
|
||||
struct mesh_t* mesh = NULL;
|
||||
uint32_t indices_count = 0U;
|
||||
struct vulkan_buffer_t* instance_buffer = &context->instance_buffers[mesh_id];
|
||||
size_t instance_size = sizeof(struct instance_data_t);
|
||||
|
||||
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);
|
||||
/* 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));
|
||||
|
||||
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 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);
|
||||
|
||||
/* 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));
|
||||
destroy_buffer(context, &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);
|
||||
instance_buffer->allocated_size += instance_size;
|
||||
|
||||
destroy_buffer(context, &staging_buffer);
|
||||
}
|
||||
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));
|
||||
|
||||
/* 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;
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,13 +305,18 @@ rse_err_t buffers_update(struct graphics_context_t* context)
|
||||
*/
|
||||
rse_err_t record_command_buffer(struct graphics_context_t* context, uint32_t image_index)
|
||||
{
|
||||
size_t iter = 0U;
|
||||
VkCommandBuffer command_buffer = context->vulkan_handles.command_buffers[context->current_frame];
|
||||
VkCommandBuffer command_buffer = context->command_buffers[context->current_frame];
|
||||
VkCommandBufferBeginInfo begin_info = {0};
|
||||
VkRenderPassBeginInfo render_pass_info = {0};
|
||||
VkViewport viewport = {0};
|
||||
VkRect2D scissor = {0};
|
||||
VkClearValue clear_values[2] = {0}; /* For color and depth stencil */
|
||||
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);
|
||||
|
||||
clear_values[0].color.float32[0] = 0.0f;
|
||||
clear_values[0].color.float32[1] = 0.0f;
|
||||
@@ -380,43 +329,63 @@ 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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to start recording command buffer");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Failed to start recording command buffer\n"));
|
||||
return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED;
|
||||
}
|
||||
|
||||
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
render_pass_info.renderPass = context->vulkan_handles.render_pass;
|
||||
render_pass_info.framebuffer = context->swapchain_data.swapchain_framebuffers[image_index];
|
||||
render_pass_info.renderPass = context->render_pass;
|
||||
render_pass_info.framebuffer = context->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_data.swapchain_extent;
|
||||
render_pass_info.renderArea.extent = context->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_data.swapchain_extent.width);
|
||||
viewport.height = (float)(context->swapchain_data.swapchain_extent.height);
|
||||
viewport.width = (float)(context->swapchain_extent.width);
|
||||
viewport.height = (float)(context->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_data.swapchain_extent;
|
||||
scissor.extent = context->swapchain_extent;
|
||||
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||
|
||||
for (iter = 0U; iter < context->pipelines_data.pipelines_count; ++iter) {
|
||||
context->renderer_data.renderers[iter].render_function(context, iter);
|
||||
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 */
|
||||
}
|
||||
|
||||
vkCmdEndRenderPass(command_buffer);
|
||||
|
||||
if (VK_SUCCESS != vkEndCommandBuffer(command_buffer)) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to record command buffer. Validate your commands.");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Failed to record command buffer. Validate your commands.\n"));
|
||||
return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED;
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
@@ -435,27 +404,24 @@ rse_err_t create_buffers(struct graphics_context_t* context)
|
||||
|
||||
void reset_command_buffer(struct graphics_context_t* context)
|
||||
{
|
||||
vkResetCommandBuffer(context->vulkan_handles.command_buffers[context->current_frame],
|
||||
/*VkCommandBufferResetFlagBits*/ 0);
|
||||
vkResetCommandBuffer(context->command_buffers[context->current_frame], /*VkCommandBufferResetFlagBits*/ 0);
|
||||
}
|
||||
|
||||
void destroy_buffers(struct graphics_context_t* context)
|
||||
{
|
||||
size_t iter = 0U;
|
||||
size_t i = 0U;
|
||||
|
||||
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].vertex_buffer);
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
for (i = 0; i < MAX_MESH_NUMBER; ++i) {
|
||||
vmaDestroyBuffer(context->allocator, context->instance_buffers[i].buffer,
|
||||
context->instance_buffers[i].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);
|
||||
RSE_LINKED_LIST_FOREACH(context->uniform_buffers, buffer) {
|
||||
vmaDestroyBuffer(context->allocator, buffer->data.buffer, buffer->data.allocation);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
#ifndef VULKAN_BUFFERS_H
|
||||
#define VULKAN_BUFFERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
#include "graphics_context.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "vulkan_commons.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "graphics_context.h"
|
||||
|
||||
#include <vma/vk_mem_alloc.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create buffers needed by vulkan pipeline
|
||||
@@ -23,20 +25,30 @@ 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 buffer_create_uniform(struct graphics_context_t* context, struct vulkan_buffer_t* buffer,
|
||||
VkDeviceSize buffer_size);
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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, size_t vertices_count,
|
||||
const struct vertex_t* vertices, size_t indices_count, uint16_t* indices);
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Add instance data to vulkan buffers
|
||||
@@ -45,8 +57,7 @@ rse_err_t add_vertices(struct graphics_context_t* context, uint16_t mesh_id, siz
|
||||
* @param instance_data Instance data
|
||||
* @return rse_err_t RSE_ERROR_NO_ERROR on success
|
||||
*/
|
||||
rse_err_t mesh_add_instance(struct graphics_context_t* context, uint32_t mesh_id,
|
||||
struct instance_data_t* instance_data);
|
||||
rse_err_t update_mesh_instances(struct graphics_context_t* context, uint16_t mesh_id, struct instance_data_t* instance_data);
|
||||
|
||||
/**
|
||||
* @brief Record commands for given image index
|
||||
@@ -71,6 +82,7 @@ void reset_command_buffer(struct graphics_context_t* context);
|
||||
*/
|
||||
void destroy_buffers(struct graphics_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper function. Creates a buffer object
|
||||
*
|
||||
@@ -82,17 +94,12 @@ 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
|
||||
|
||||
@@ -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 Pool
|
||||
* @brief Create Command Pools
|
||||
*
|
||||
* @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_pool(struct graphics_context_t* context)
|
||||
static rse_err_t create_command_pools(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[QUEUE_FAMILY_INDEX_GRAPHICS];
|
||||
create_info.queueFamilyIndex = context->queue_family_indices[0];
|
||||
|
||||
if (VK_SUCCESS != vkCreateCommandPool(context->vulkan_handles.device, &create_info, NULL, &context->vulkan_handles.command_pool)) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create command pool");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
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;
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
@@ -45,27 +45,28 @@ static rse_err_t allocate_command_buffers(struct graphics_context_t* context)
|
||||
/* Allocate memory for command buffers */
|
||||
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
allocate_info.pNext = NULL;
|
||||
allocate_info.commandPool = context->vulkan_handles.command_pool;
|
||||
allocate_info.commandPool = context->command_pool;
|
||||
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
allocate_info.commandBufferCount = SWAP_BUFFER_COUNT;
|
||||
|
||||
if (VK_SUCCESS != vkAllocateCommandBuffers(context->vulkan_handles.device,
|
||||
if (VK_SUCCESS != vkAllocateCommandBuffers(context->device,
|
||||
&allocate_info,
|
||||
context->vulkan_handles.command_buffers)) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to allocate command buffers\n");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
context->command_buffers)) {
|
||||
LOGF(_("Failed to allocate command buffers\n"));
|
||||
return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED;
|
||||
}
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t begin_single_time_command(struct graphics_context_t* context, VkCommandBuffer* command_buffer)
|
||||
VkCommandBuffer begin_single_time_command(struct graphics_context_t* context)
|
||||
{
|
||||
VkCommandBufferBeginInfo begin_info = {0};
|
||||
VkCommandBufferAllocateInfo alloc_info = {0};
|
||||
VkCommandBuffer command_buffer = VK_NULL_HANDLE;
|
||||
VkCommandBufferBeginInfo begin_info = {};
|
||||
VkCommandBufferAllocateInfo alloc_info = {};
|
||||
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
alloc_info.pNext = NULL;
|
||||
alloc_info.commandPool = context->vulkan_handles.command_pool;
|
||||
alloc_info.commandPool = context->command_pool;
|
||||
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
alloc_info.commandBufferCount = 1;
|
||||
|
||||
@@ -74,21 +75,15 @@ rse_err_t begin_single_time_command(struct graphics_context_t* context, VkComman
|
||||
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
begin_info.pInheritanceInfo = NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
vkAllocateCommandBuffers(context->device, &alloc_info, &command_buffer);
|
||||
vkBeginCommandBuffer(command_buffer, &begin_info);
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
return command_buffer;
|
||||
}
|
||||
|
||||
rse_err_t end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer)
|
||||
void end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer)
|
||||
{
|
||||
VkSubmitInfo submit_info = {0};
|
||||
VkSubmitInfo submit_info = {};
|
||||
|
||||
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit_info.pNext = NULL;
|
||||
@@ -100,30 +95,18 @@ rse_err_t end_single_time_comands(struct graphics_context_t* context, VkCommandB
|
||||
submit_info.signalSemaphoreCount = 0;
|
||||
submit_info.pSignalSemaphores = NULL;
|
||||
|
||||
if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to end command buffer");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
vkEndCommandBuffer(command_buffer);
|
||||
|
||||
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;
|
||||
}
|
||||
vkQueueSubmit(context->graphics_queue, 1, &submit_info, VK_NULL_HANDLE);
|
||||
vkQueueWaitIdle(context->graphics_queue);
|
||||
|
||||
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;
|
||||
vkFreeCommandBuffers(context->device, context->command_pool, 1, &command_buffer);
|
||||
}
|
||||
|
||||
rse_err_t init_commands(struct graphics_context_t* context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
STATUS_CHECK(create_command_pool(context));
|
||||
STATUS_CHECK(create_command_pools(context));
|
||||
STATUS_CHECK(allocate_command_buffers(context));
|
||||
|
||||
return status;
|
||||
@@ -131,5 +114,5 @@ rse_err_t init_commands(struct graphics_context_t* context)
|
||||
|
||||
void destroy_commands(struct graphics_context_t* context)
|
||||
{
|
||||
vkDestroyCommandPool(context->vulkan_handles.device, context->vulkan_handles.command_pool, NULL);
|
||||
vkDestroyCommandPool(context->device, context->command_pool, NULL);
|
||||
}
|
||||
@@ -12,8 +12,7 @@
|
||||
#ifndef VULKAN_COMMANDS_H
|
||||
#define VULKAN_COMMANDS_H
|
||||
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <vma/vk_mem_alloc.h>
|
||||
|
||||
#include "utilities/commons.h"
|
||||
#include "graphics_context.h"
|
||||
@@ -38,17 +37,16 @@ void destroy_commands(struct graphics_context_t* context);
|
||||
* @brief Helper function, creates command buffer for recording a command. Useful for copying buffer na images.
|
||||
*
|
||||
* @param context Graphics context handle
|
||||
* @return RSE_ERROR_NO_ERROR on success
|
||||
* @return VkCommandBuffer Created command buffer, ready to be filled with commands.
|
||||
*/
|
||||
rse_err_t begin_single_time_command(struct graphics_context_t* context, VkCommandBuffer* command_buffer);
|
||||
VkCommandBuffer begin_single_time_command(struct graphics_context_t* context);
|
||||
|
||||
/**
|
||||
* @brief Close command buffer and execute recorded command.
|
||||
*
|
||||
* @param context Graphics context handle
|
||||
* @param command_buffer Command buffer for execution
|
||||
* @return RSE_ERROR_NO_ERROR on success
|
||||
*/
|
||||
rse_err_t end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer);
|
||||
void end_single_time_comands(struct graphics_context_t* context, VkCommandBuffer command_buffer);
|
||||
|
||||
#endif /* VULKAN_COMMANDS_H */
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "vulkan_commons.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
time_t g_last_frame_time;
|
||||
time_t g_this_frame_time;
|
||||
float g_float_delta_time;
|
||||
|
||||
void init_time()
|
||||
{
|
||||
time(&g_last_frame_time);
|
||||
}
|
||||
|
||||
void update_time()
|
||||
{
|
||||
g_last_frame_time = g_this_frame_time;
|
||||
time(&g_this_frame_time);
|
||||
g_float_delta_time = (float)difftime(g_this_frame_time, g_last_frame_time);
|
||||
}
|
||||
|
||||
float get_time_diff()
|
||||
{
|
||||
return g_float_delta_time;
|
||||
}
|
||||
@@ -12,10 +12,10 @@
|
||||
#ifndef VULKAN_GLOBAL_H
|
||||
#define VULKAN_GLOBAL_H
|
||||
|
||||
#include <cglm/types.h>
|
||||
#include "math.h"
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <cglm/cglm.h>
|
||||
#include <vma/vk_mem_alloc.h>
|
||||
|
||||
#include <stdalign.h>
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
*/
|
||||
struct vertex_t
|
||||
{
|
||||
vec3 pos;
|
||||
vec3 color;
|
||||
vec2 tex_coords;
|
||||
struct vec3_t pos;
|
||||
struct vec3_t color;
|
||||
struct vec3_t tex_coords;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -44,18 +44,22 @@ struct vulkan_buffer_t
|
||||
VmaAllocationInfo allocation_info;
|
||||
};
|
||||
|
||||
struct mvp_data_t
|
||||
struct uniform_buffer_object_t
|
||||
{
|
||||
alignas(16) mat4 model;
|
||||
alignas(16) mat4 view;
|
||||
alignas(16) mat4 proj;
|
||||
alignas(16) struct mat4_t model;
|
||||
alignas(16) struct mat4_t view;
|
||||
alignas(16) struct mat4_t proj;
|
||||
};
|
||||
|
||||
struct instance_data_t {
|
||||
alignas(16) vec3 pos;
|
||||
alignas(16) vec3 rot;
|
||||
alignas(4) float scale;
|
||||
alignas(4) uint32_t texture_id;
|
||||
struct vec3_t pos;
|
||||
struct vec3_t rot;
|
||||
float scale;
|
||||
uint32_t texture_id;
|
||||
};
|
||||
|
||||
void init_time();
|
||||
void update_time();
|
||||
float get_time_diff();
|
||||
|
||||
#endif /* VULKAN_GLOBAL_H */
|
||||
@@ -0,0 +1,64 @@
|
||||
#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
|
||||
+78
-262
@@ -1,22 +1,21 @@
|
||||
#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/file_utils.h"
|
||||
#include "utilities/localization.h"
|
||||
#include "utilities/logger.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
|
||||
@@ -45,26 +44,23 @@ 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->vulkan_handles.device, &create_info, NULL, sampler)) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create image view");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
if (VK_SUCCESS != vkCreateSampler(context->device, &create_info, NULL, sampler)) {
|
||||
LOGF("Failed to create image view");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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->vulkan_handles.physical_device, candidates[i], &props);
|
||||
vkGetPhysicalDeviceFormatProperties(context->physical_device, candidates[i], &props);
|
||||
|
||||
if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
|
||||
*found_format = candidates[i];
|
||||
@@ -75,20 +71,14 @@ static rse_err_t find_supported_format(struct graphics_context_t* context,
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to find correct format");
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGE(_("Failed to find correct format\n"));
|
||||
return VULKAN_ERROR_FORMAT_NOT_FOUND;
|
||||
}
|
||||
|
||||
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};
|
||||
@@ -123,23 +113,19 @@ static rse_err_t create_image(struct graphics_context_t* context,
|
||||
alloc_create_info.pUserData = NULL;
|
||||
alloc_create_info.priority = 1.0f;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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};
|
||||
@@ -160,10 +146,9 @@ static rse_err_t create_image_view(struct graphics_context_t* context,
|
||||
image_view_create_info.subresourceRange.baseArrayLayer = 0U;
|
||||
image_view_create_info.subresourceRange.layerCount = 1U;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
@@ -172,18 +157,15 @@ rse_err_t create_color_resource(struct graphics_context_t* context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
VkFormat color_format = IMAGE_FORMAT;
|
||||
VkExtent2D extent = {0};
|
||||
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
|
||||
|
||||
STATUS_CHECK(image_get_extent(context, &extent));
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->physical_device, context->surface,
|
||||
&physical_device_surface_capabilities);
|
||||
|
||||
STATUS_CHECK(create_image(context,
|
||||
&context->color_image,
|
||||
extent.width,
|
||||
extent.height,
|
||||
color_format,
|
||||
STATUS_CHECK(create_image(context, &context->color_image, physical_device_surface_capabilities.currentExtent.width,
|
||||
physical_device_surface_capabilities.currentExtent.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;
|
||||
@@ -193,20 +175,15 @@ rse_err_t create_depth_resources(struct graphics_context_t* context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
VkFormat depth_format = {0};
|
||||
VkExtent2D extent = {0};
|
||||
|
||||
STATUS_CHECK(image_get_extent(context, &extent));
|
||||
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
|
||||
|
||||
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,
|
||||
extent.width,
|
||||
extent.height,
|
||||
depth_format,
|
||||
VK_SAMPLE_COUNT_8_BIT,
|
||||
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
||||
0U));
|
||||
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_view(context, &context->depth_image, depth_format, VK_IMAGE_ASPECT_DEPTH_BIT));
|
||||
|
||||
return status;
|
||||
@@ -230,29 +207,21 @@ 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:
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed translate vulkan format to pixel width");
|
||||
LOGE("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};
|
||||
@@ -290,22 +259,18 @@ static rse_err_t transition_image_layout(struct graphics_context_t* context,
|
||||
source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
} else {
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
return VULKAN_ERROR_TRANSITION_IMAGE_LAYOUT_FAILED;
|
||||
}
|
||||
|
||||
STATUS_CHECK(begin_single_time_command(context, &command_buffer));
|
||||
command_buffer = begin_single_time_command(context);
|
||||
vkCmdPipelineBarrier(command_buffer, source_stage, destination_stage, 0, 0, NULL, 0, NULL, 1, &image_barrier);
|
||||
STATUS_CHECK(end_single_time_comands(context, command_buffer));
|
||||
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,
|
||||
rse_id_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, uint16_t* texture_id)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
struct vulkan_buffer_t staging_buffer = {0};
|
||||
@@ -331,38 +296,26 @@ static rse_err_t create_textured_image(struct graphics_context_t* context,
|
||||
|
||||
/* Check if we found free image handle */
|
||||
if (free_texture_image == NULL) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to find free texture image handle");
|
||||
LOGE(_("Failed to find free texture image handle\n"));
|
||||
*texture_id = -1;
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
return VULKAN_ERROR_MAX_TEXTURE_COUNT_REACHED;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -378,20 +331,12 @@ static rse_err_t create_textured_image(struct graphics_context_t* context,
|
||||
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,
|
||||
free_texture_image->image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
©_region);
|
||||
command_buffer = begin_single_time_command(context);
|
||||
vkCmdCopyBufferToImage(command_buffer, staging_buffer.buffer, free_texture_image->image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_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;
|
||||
@@ -408,107 +353,8 @@ rse_err_t init_vulkan_images(struct graphics_context_t* context)
|
||||
return status;
|
||||
}
|
||||
|
||||
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,
|
||||
©_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 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 status = RSE_ERROR_NO_ERROR;
|
||||
|
||||
@@ -518,82 +364,56 @@ rse_err_t load_texture_from_bitmat(struct graphics_context_t* context,
|
||||
return status;
|
||||
}
|
||||
|
||||
rse_err_t texture_load_from_buffer(struct graphics_context_t* context, const unsigned char* data_buffer, size_t buffer_size, rse_id_t* texture_id)
|
||||
rse_err_t load_texture_from_file(struct graphics_context_t* context, const char* file_path, uint16_t* texture_id)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels = 0;
|
||||
unsigned char* pixel_buffer = NULL;
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
stbi_uc* pixel_buffer = NULL;
|
||||
|
||||
pixel_buffer = stbi_load(file_path, &width, &height, &channels, STBI_rgb_alpha);
|
||||
|
||||
pixel_buffer = stbi_load_from_memory(data_buffer, (int)buffer_size, &width, &height, &channels, STBI_rgb_alpha);
|
||||
if (pixel_buffer == NULL) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to load image");
|
||||
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGE("Failed to load texture from file: %s", file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
STATUS_CHECK(load_texture_from_bitmat(context, pixel_buffer, width, height, texture_id, VK_FORMAT_R8G8B8A8_SRGB));
|
||||
|
||||
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->vulkan_handles.device, context->texture_images[i].image_view, NULL);
|
||||
vmaDestroyImage(context->vulkan_handles.allocator,
|
||||
context->texture_images[i].image,
|
||||
vkDestroyImageView(context->device, context->texture_images[i].image_view, NULL);
|
||||
vmaDestroyImage(context->allocator, context->texture_images[i].image,
|
||||
context->texture_images[i].allocation);
|
||||
}
|
||||
}
|
||||
|
||||
vkDestroySampler(context->vulkan_handles.device, context->vulkan_handles.sampler, NULL);
|
||||
vkDestroySampler(context->vulkan_handles.device, context->debug_overlay.sampler, NULL);
|
||||
vkDestroySampler(context->device, context->sampler, NULL);
|
||||
}
|
||||
|
||||
void destroy_depth_resource(struct graphics_context_t* context)
|
||||
{
|
||||
vkDestroyImageView(context->vulkan_handles.device, context->depth_image.image_view, NULL);
|
||||
vmaDestroyImage(context->vulkan_handles.allocator, context->depth_image.image, context->depth_image.allocation);
|
||||
vkDestroyImageView(context->device, context->depth_image.image_view, NULL);
|
||||
vmaDestroyImage(context->allocator, context->depth_image.image, context->depth_image.allocation);
|
||||
}
|
||||
|
||||
void destroy_color_resource(struct graphics_context_t* context)
|
||||
{
|
||||
vkDestroyImageView(context->vulkan_handles.device, context->color_image.image_view, NULL);
|
||||
vmaDestroyImage(context->vulkan_handles.allocator, context->color_image.image, context->color_image.allocation);
|
||||
vkDestroyImageView(context->device, context->color_image.image_view, NULL);
|
||||
vmaDestroyImage(context->allocator, context->color_image.image, context->color_image.allocation);
|
||||
}
|
||||
|
||||
uint8_t image_exists(struct graphics_context_t* context, rse_id_t image_id)
|
||||
uint8_t image_exists(struct graphics_context_t* context, uint8_t image_id)
|
||||
{
|
||||
return context->texture_images[image_id].id_taken == IMAGE_TAKEN;
|
||||
}
|
||||
@@ -618,12 +438,8 @@ 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
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
*/
|
||||
rse_err_t init_vulkan_images(struct graphics_context_t* context);
|
||||
|
||||
rse_err_t image_get_extent(struct graphics_context_t* context, VkExtent2D* extent);
|
||||
|
||||
/**
|
||||
* @brief Loads image from file, for later to be used as a texture
|
||||
*
|
||||
@@ -40,9 +38,8 @@ rse_err_t image_get_extent(struct graphics_context_t* context, VkExtent2D* exten
|
||||
* @return rse_err_t RSE_ERROR_NO_ERROR on success
|
||||
*/
|
||||
rse_err_t load_texture_from_bitmat(struct graphics_context_t* context, const unsigned char* pixel_buffer, int width,
|
||||
int height, rse_id_t* texture_id, VkFormat color_format);
|
||||
int height, uint16_t* texture_id, VkFormat color_format);
|
||||
|
||||
rse_err_t texture_load_from_buffer(struct graphics_context_t* context, const unsigned char* data_buffer, size_t buffer_size, rse_id_t* texture_id);
|
||||
/**
|
||||
* @brief Loads image from file, for later to be used as a texture
|
||||
*
|
||||
@@ -51,14 +48,7 @@ rse_err_t texture_load_from_buffer(struct graphics_context_t* context, const uns
|
||||
* @param texture_id Texture ID will be set up here
|
||||
* @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);
|
||||
rse_err_t load_texture_from_file(struct graphics_context_t* context, const char* file_path, uint16_t* texture_id);
|
||||
|
||||
/**
|
||||
* @brief Destroy all loaded textures. Usefull for closing application or just clearing.
|
||||
@@ -75,7 +65,7 @@ void destroy_textures(struct graphics_context_t* context);
|
||||
* @param image_id Image ID
|
||||
* @return uint8_t 1 on success, 0 on failure
|
||||
*/
|
||||
uint8_t image_exists(struct graphics_context_t* context, rse_id_t image_id);
|
||||
uint8_t image_exists(struct graphics_context_t* context, uint8_t image_id);
|
||||
|
||||
/**
|
||||
* @brief Returns number of loaded texture
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
#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)
|
||||
{
|
||||
@@ -97,10 +100,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->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;
|
||||
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;
|
||||
}
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
@@ -108,5 +111,5 @@ rse_err_t create_render_pass(struct graphics_context_t* context)
|
||||
|
||||
void destroy_render_pass(struct graphics_context_t* context)
|
||||
{
|
||||
vkDestroyRenderPass(context->vulkan_handles.device, context->vulkan_handles.render_pass, NULL);
|
||||
vkDestroyRenderPass(context->device, context->render_pass, NULL);
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
#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
|
||||
@@ -21,60 +22,56 @@ 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->vulkan_handles.physical_device,
|
||||
context->vulkan_handles.surface,
|
||||
&physical_device_surface_capabilities);
|
||||
|
||||
STATUS_CHECK(image_get_extent(context, &extent));
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
||||
context->physical_device, context->surface,
|
||||
&physical_device_surface_capabilities);
|
||||
|
||||
/* Store extent in global variable, for later use */
|
||||
context->swapchain_data.swapchain_extent = extent;
|
||||
context->swapchain_extent =
|
||||
physical_device_surface_capabilities.currentExtent;
|
||||
|
||||
/* Check if device supports surface for presentation */
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(context->vulkan_handles.physical_device,
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(context->physical_device,
|
||||
context->queue_family_indices[0],
|
||||
context->vulkan_handles.surface,
|
||||
&surfaceSupported);
|
||||
context->surface, &surfaceSupported);
|
||||
|
||||
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
create_info.pNext = NULL;
|
||||
create_info.flags = 0U;
|
||||
create_info.surface = context->vulkan_handles.surface;
|
||||
create_info.surface = context->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_data.swapchain_extent;
|
||||
create_info.imageArrayLayers = 1U; /* For non-stereoscopic-3D applications, this value is 1. */
|
||||
create_info.imageExtent = context->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_IMMEDIATE_KHR;
|
||||
create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
create_info.clipped = VK_TRUE;
|
||||
create_info.oldSwapchain = VK_NULL_HANDLE;
|
||||
|
||||
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;
|
||||
if (VK_SUCCESS != vkCreateSwapchainKHR(context->device, &create_info, NULL,
|
||||
&context->swapchain)) {
|
||||
LOGF(_("Failed to create swapchain\n"));
|
||||
return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED;
|
||||
}
|
||||
|
||||
/* Obtain 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);
|
||||
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);
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
@@ -86,13 +83,15 @@ static rse_err_t create_swapchain(struct graphics_context_t* context)
|
||||
*/
|
||||
static rse_err_t create_swapchain_image_views(struct graphics_context_t* context)
|
||||
{
|
||||
for (size_t i = 0; i < SWAPCHAIN_IMAGE_COUNT; i++) {
|
||||
rse_malloc(context->swapchain_image_views,
|
||||
sizeof(VkImageView) * context->swapchain_images_count);
|
||||
for (size_t i = 0; i < context->swapchain_images_count; i++) {
|
||||
VkImageViewCreateInfo create_info;
|
||||
|
||||
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
create_info.pNext = NULL;
|
||||
create_info.flags = 0U;
|
||||
create_info.image = context->swapchain_data.swapchain_images[i];
|
||||
create_info.image = context->swapchain_images[i];
|
||||
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */
|
||||
create_info.format = VK_FORMAT_B8G8R8A8_SRGB;
|
||||
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
@@ -105,12 +104,11 @@ static rse_err_t create_swapchain_image_views(struct graphics_context_t* context
|
||||
create_info.subresourceRange.baseArrayLayer = 0U;
|
||||
create_info.subresourceRange.layerCount = 1U;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,26 +122,30 @@ static rse_err_t create_swapchain_image_views(struct graphics_context_t* context
|
||||
*/
|
||||
static rse_err_t create_framebuffers(struct graphics_context_t* context)
|
||||
{
|
||||
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_data.swapchain_image_views[i]};
|
||||
context->swapchain_image_views[i]};
|
||||
|
||||
VkFramebufferCreateInfo framebufferInfo = {0};
|
||||
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
framebufferInfo.renderPass = context->vulkan_handles.render_pass;
|
||||
framebufferInfo.renderPass = context->render_pass;
|
||||
framebufferInfo.attachmentCount = 3;
|
||||
framebufferInfo.pAttachments = attachments;
|
||||
framebufferInfo.width = context->swapchain_data.swapchain_extent.width;
|
||||
framebufferInfo.height = context->swapchain_data.swapchain_extent.height;
|
||||
framebufferInfo.width = context->swapchain_extent.width;
|
||||
framebufferInfo.height = context->swapchain_extent.height;
|
||||
framebufferInfo.layers = 1;
|
||||
|
||||
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;
|
||||
if (vkCreateFramebuffer(context->device, &framebufferInfo, NULL,
|
||||
&context->swapchain_framebuffers[i]) !=
|
||||
VK_SUCCESS) {
|
||||
LOGF(_("Failed to create framebuffer\n"));
|
||||
return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +168,7 @@ void recreate_swapchain(struct graphics_context_t* context)
|
||||
SDL_WaitEvent(&event);
|
||||
}
|
||||
|
||||
vkDeviceWaitIdle(context->vulkan_handles.device);
|
||||
vkDeviceWaitIdle(context->device);
|
||||
|
||||
cleanup_swapchain(context);
|
||||
|
||||
@@ -188,14 +190,16 @@ 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->vulkan_handles.device, context->swapchain_data.swapchain_framebuffers[i], NULL);
|
||||
vkDestroyFramebuffer(context->device,
|
||||
context->swapchain_framebuffers[i], NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < context->swapchain_images_count; i++) {
|
||||
vkDestroyImageView(context->vulkan_handles.device, context->swapchain_data.swapchain_image_views[i], NULL);
|
||||
vkDestroyImageView(context->device, context->swapchain_image_views[i],
|
||||
NULL);
|
||||
}
|
||||
|
||||
vkDestroySwapchainKHR(context->vulkan_handles.device, context->swapchain_data.swapchain, NULL);
|
||||
vkDestroySwapchainKHR(context->device, context->swapchain, NULL);
|
||||
}
|
||||
|
||||
rse_err_t create_swapchain_and_framebuffers(struct graphics_context_t* context)
|
||||
|
||||
+28
-10
@@ -10,35 +10,53 @@
|
||||
*/
|
||||
|
||||
#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)
|
||||
{
|
||||
(void)is_framebuffer_resized;
|
||||
// TODO: Actually use this parameter?
|
||||
SDL_Init(SDL_INIT_VIDEO); //TODO: Move to initial phase of RSE initialization
|
||||
|
||||
*window_handle = SDL_CreateWindow("RedScarfEngine", 1024, 768, SDL_WINDOW_VULKAN);
|
||||
|
||||
if (NULL == *window_handle) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Window context creation failed. Reason: %s", SDL_GetError());
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
LOGF(_("Window context creation failed\n"));
|
||||
return WINDOW_ERROR_WINDOW_NOT_CREATED;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -32,6 +32,14 @@
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @file window_errors.h
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2023-10-06
|
||||
*
|
||||
* @copyright Copyright (c) 2023
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WINDOW_ERRORS_H
|
||||
#define WINDOW_ERRORS_H
|
||||
|
||||
#define RSE_WINDOW_MODULE_ID 0x0200U
|
||||
|
||||
/* Error codes */
|
||||
enum window_error_t {
|
||||
WINDOW_ERROR_GLFW_INIT_FAILED = RSE_WINDOW_MODULE_ID,
|
||||
WINDOW_ERROR_VULKAN_NOT_LOADED,
|
||||
WINDOW_ERROR_WINDOW_NOT_CREATED
|
||||
};
|
||||
|
||||
#endif /* WINDOW_ERRORS_H */
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
project('RedScarfEngine',
|
||||
['c', 'cpp'],
|
||||
version: '0.0.1',
|
||||
)
|
||||
|
||||
# add_project_arguments('-DWAYLAND=1', language : 'c')
|
||||
|
||||
subdir('utilities')
|
||||
subdir('graphics')
|
||||
subdir('events')
|
||||
|
||||
subdir('red_scarf_engine')
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# !WARNING! When run, this script will create prepare.lock file, which will indicate that the script has been run before.
|
||||
# This is used for automation with CMake, which will run this script if and only if this file does not exists.
|
||||
# If you want to run this script again, remove this file manually first.
|
||||
|
||||
# Script used for preparing build envirionment. I cannot use submodules, because I cannot specify commit/tag
|
||||
# in submodule config. What's more, preparing vulkan SDK is complicated, so a custom scrip is needed for that.
|
||||
|
||||
import io
|
||||
import re
|
||||
import git
|
||||
import os
|
||||
import requests
|
||||
import subprocess
|
||||
|
||||
REPO_DIR = os.path.join(os.getcwd(), "third_party")
|
||||
|
||||
class Dependency:
|
||||
def __init__(self, name, file: io.TextIOWrapper):
|
||||
self.name = name
|
||||
while 1:
|
||||
line = file.readline().split("=", 1)
|
||||
if len(line) > 1:
|
||||
match line[0].strip():
|
||||
case "git_url":
|
||||
self.git_url = line[1].strip()
|
||||
case "dl_url":
|
||||
self.dl_url = line[1].strip()
|
||||
case "tag":
|
||||
self.tag = line[1].strip()
|
||||
case "custom_commands":
|
||||
self.custom_commands = line[1].strip()
|
||||
else:
|
||||
break
|
||||
|
||||
def download(self):
|
||||
dl_counter = 0
|
||||
# Git repository
|
||||
if hasattr(self, "git_url") and self.git_url:
|
||||
repo = git.Repo.clone_from(self.git_url, os.path.join(REPO_DIR, self.name))
|
||||
repo.git.checkout(self.tag)
|
||||
elif hasattr(self, "dl_url") and self.dl_url:
|
||||
while dl_counter < 10:
|
||||
response = requests.get(self.dl_url)
|
||||
if response.status_code == 200: # everything went fine
|
||||
open(os.path.join(REPO_DIR, self.name), "wb").write(response.content)
|
||||
break
|
||||
dl_counter += 1
|
||||
print("Failed to download {}. Retrying {}th time...", self.name, dl_counter)
|
||||
time.sleep(5.0)
|
||||
|
||||
if hasattr(self, "custom_commands") and self.custom_commands:
|
||||
p = subprocess.run(self.custom_commands, shell=True)
|
||||
p.chek_returncode()
|
||||
|
||||
def main():
|
||||
if not os.path.isdir(REPO_DIR):
|
||||
os.mkdir(REPO_DIR)
|
||||
|
||||
f = open("dependencies.dep", "r")
|
||||
os.chdir(REPO_DIR)
|
||||
while True:
|
||||
line = f.readline()
|
||||
|
||||
if not line:
|
||||
break
|
||||
stripped = line.strip()
|
||||
# Remove comments
|
||||
if stripped.startswith("#"):
|
||||
return
|
||||
uncommented = stripped.split("#", 1)[0]
|
||||
if len(uncommented) == 0:
|
||||
return
|
||||
if uncommented.startswith("["):
|
||||
dep_name = re.search('\[(.*)\]', uncommented).group(1)
|
||||
dep = Dependency(dep_name, f)
|
||||
print("Downloading dependency " + dep_name + "...")
|
||||
dep.download()
|
||||
|
||||
f.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,10 @@
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror")
|
||||
|
||||
add_executable(${PROJECT_NAME} src/main.c)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${CMAKE_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
graphics
|
||||
utilities)
|
||||
@@ -1,23 +0,0 @@
|
||||
red_scarf_engine_srcs = [
|
||||
'src/red_scarf_engine.c',
|
||||
'src/main.c'
|
||||
]
|
||||
|
||||
sdl3_dep = dependency('sdl3',
|
||||
version : '>=3.4.0')
|
||||
|
||||
executable(
|
||||
'RedScarfEngine',
|
||||
red_scarf_engine_srcs,
|
||||
include_directories : [
|
||||
'../'
|
||||
],
|
||||
dependencies : [
|
||||
sdl3_dep,
|
||||
rse_graphics_lib_dep,
|
||||
],
|
||||
link_with: [
|
||||
rse_events_lib,
|
||||
rse_utilities_lib,
|
||||
],
|
||||
)
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* @file red_scarf_engine.h
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @brief Red Scarf Engine main entry point
|
||||
* @version 0.1
|
||||
* @date 2026-03-03
|
||||
*
|
||||
* @copyright Copyright (c) 2026
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RED_SCARF_ENGINE_H_
|
||||
#define RED_SCARF_ENGINE_H_
|
||||
|
||||
#include "utilities/commons.h"
|
||||
|
||||
typedef struct rse_context_t* rse_context;
|
||||
|
||||
rse_err_t rse_init(rse_context* context);
|
||||
rse_err_t rse_run(rse_context context);
|
||||
|
||||
#endif /* RED_SCARF_ENGINE_H_ */
|
||||
@@ -1,16 +1,30 @@
|
||||
#include "red_scarf_engine.h"
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include "graphics/rse_graphics.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;
|
||||
rse_context context;
|
||||
|
||||
struct rse_graphics_context_t* context = NULL;
|
||||
SDL_Thread* graphics_task = NULL;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
STATUS_CHECK(rse_init(&context));
|
||||
STATUS_CHECK(rse_run(context));
|
||||
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();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "red_scarf_engine.h"
|
||||
#include "graphics/rse_graphics.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "utilities/errors_common.h"
|
||||
#include "utilities/time_utils.h"
|
||||
#include "events/rse_events_manager.h"
|
||||
|
||||
struct rse_context_t {
|
||||
struct graphics_context_t graphics_context;
|
||||
struct events_context_t events_context;
|
||||
};
|
||||
|
||||
static void fps_print(double* start_time)
|
||||
{
|
||||
static uint32_t frame_counts = 0U;
|
||||
|
||||
double now = time_get_sec_from_start();
|
||||
|
||||
frame_counts++;
|
||||
if ((now - *start_time) > 1.0) {
|
||||
printf("\rFPS: %u", frame_counts);
|
||||
fflush(stdout);
|
||||
frame_counts = 0;
|
||||
*start_time += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
rse_err_t rse_init(rse_context* context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
struct rse_context_t* rse_context = NULL;
|
||||
|
||||
rse_malloc(*context, sizeof(struct rse_context_t));
|
||||
|
||||
rse_context = *context;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
|
||||
|
||||
STATUS_CHECK(time_init());
|
||||
STATUS_CHECK(rse_events_init(&rse_context->events_context));
|
||||
STATUS_CHECK(rse_graphics_init(&rse_context->graphics_context));
|
||||
|
||||
return RSE_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
rse_err_t rse_run(rse_context context)
|
||||
{
|
||||
rse_err_t status = RSE_ERROR_NO_ERROR;
|
||||
double start_time;
|
||||
|
||||
start_time = time_get_sec_from_start();
|
||||
|
||||
STATUS_CHECK(rse_graphics_test_function(&context->graphics_context));
|
||||
|
||||
STATUS_CHECK(rse_graphics_run(&context->graphics_context));
|
||||
while(true) {
|
||||
time_update();
|
||||
rse_events_main_loop(&context->events_context);
|
||||
if (context->events_context.should_stop == EVENT_SHOULD_STOP_ENGINE_TRUE) {
|
||||
break;
|
||||
}
|
||||
rse_graphics_main_loop(&context->graphics_context);
|
||||
fps_print(&start_time);
|
||||
}
|
||||
|
||||
rse_graphics_deinit(&context->graphics_context);
|
||||
rse_free(context);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
pushd build/red_scarf_engine/
|
||||
VULKAN_SDK=${PWD}/third_party/vulkan/x86_64/ VK_LAYER_PATH=${PWD}/third_party/vulkan/x86_64/etc/vulkan/explicit_layer.d PATH=${PATH}:${PWD}/third_party/vulkan/x86_64/bin LD_LIBRARY_PATH=:${PWD}/third_party/vulkan/x86_64/lib ./RedScarfEngine
|
||||
popd
|
||||
@@ -1,10 +0,0 @@
|
||||
[wrap-file]
|
||||
directory = cglm-0.9.6
|
||||
source_url = https://github.com/recp/cglm/archive/refs/tags/v0.9.6.tar.gz
|
||||
source_filename = cglm-0.9.6.tar.gz
|
||||
source_hash = be5e7d384561eb0fca59724a92b7fb44bf03e588a7eae5123a7d796002928184
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/cglm_0.9.6-1/cglm-0.9.6.tar.gz
|
||||
wrapdb_version = 0.9.6-1
|
||||
|
||||
[provide]
|
||||
cglm = cglm_dep
|
||||
@@ -1,7 +0,0 @@
|
||||
project('stb', 'c')
|
||||
|
||||
stb_inc = include_directories('.')
|
||||
|
||||
stb_dep = declare_dependency(
|
||||
include_directories: stb_inc,
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
[wrap-file]
|
||||
directory = SDL3-3.4.12
|
||||
source_url = https://github.com/libsdl-org/SDL/releases/download/release-3.4.12/SDL3-3.4.12.tar.gz
|
||||
source_filename = SDL3-3.4.12.tar.gz
|
||||
source_hash = f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7
|
||||
source_fallback_url = https://wrapdb.mesonbuild.com/v2/sdl3_3.4.12-1/get_source/SDL3-3.4.12.tar.gz
|
||||
patch_filename = sdl3_3.4.12-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/sdl3_3.4.12-1/get_patch
|
||||
patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sdl3_3.4.12-1/sdl3_3.4.12-1_patch.zip
|
||||
patch_hash = 67e859d280c9cc2de422efebc4f1edf954984b59e79eddf6c92ab61d89cd874d
|
||||
wrapdb_version = 3.4.12-1
|
||||
|
||||
[provide]
|
||||
dependency_names = sdl3
|
||||
@@ -1,5 +0,0 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/nothings/stb.git
|
||||
revision = HEAD
|
||||
depth = 1
|
||||
patch_directory = stb/
|
||||
@@ -1,14 +0,0 @@
|
||||
[wrap-file]
|
||||
directory = Vulkan-Headers-1.4.346
|
||||
source_url = https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.4.346.tar.gz
|
||||
source_filename = vulkan-headers-1.4.346.tar.gz
|
||||
source_hash = 5bb77f5d7b460e255a9e51affc00d64354986b55cf577d8eab28529cad01fc80
|
||||
source_fallback_url = https://wrapdb.mesonbuild.com/v2/vulkan-headers_1.4.346-1/get_source/vulkan-headers-1.4.346.tar.gz
|
||||
patch_filename = vulkan-headers_1.4.346-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/vulkan-headers_1.4.346-1/get_patch
|
||||
patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/vulkan-headers_1.4.346-1/vulkan-headers_1.4.346-1_patch.zip
|
||||
patch_hash = b85cbf6793193b28c5bd80858308b214816e66041b10ec00f0115b19f3a7a60e
|
||||
wrapdb_version = 1.4.346-1
|
||||
|
||||
[provide]
|
||||
dependency_names = VulkanHeaders
|
||||
@@ -1,14 +0,0 @@
|
||||
[wrap-file]
|
||||
directory = VulkanMemoryAllocator-3.3.0
|
||||
source_url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/archive/refs/tags/v3.3.0.tar.gz
|
||||
source_filename = VulkanMemoryAllocator-3.3.0.tar.gz
|
||||
source_hash = c4f6bbe6b5a45c2eb610ca9d231158e313086d5b1a40c9922cb42b597419b14e
|
||||
source_fallback_url = https://wrapdb.mesonbuild.com/v2/vulkan-memory-allocator_3.3.0-1/get_source/VulkanMemoryAllocator-3.3.0.tar.gz
|
||||
patch_filename = vulkan-memory-allocator_3.3.0-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/vulkan-memory-allocator_3.3.0-1/get_patch
|
||||
patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/vulkan-memory-allocator_3.3.0-1/vulkan-memory-allocator_3.3.0-1_patch.zip
|
||||
patch_hash = 85f3c1c82f3d1938fa295c80b06fc1c5c001f939f009b566c336a941d5d2d8b5
|
||||
wrapdb_version = 3.3.0-1
|
||||
|
||||
[provide]
|
||||
dependency_names = VulkanMemoryAllocator
|
||||
@@ -1,9 +0,0 @@
|
||||
[wrap-file]
|
||||
directory = Vulkan-ValidationLayers-1.2.158
|
||||
source_url = https://github.com/KhronosGroup/Vulkan-ValidationLayers/archive/v1.2.158.tar.gz
|
||||
source_filename = vulkan-validationlayers-1.2.158.tar.gz
|
||||
source_hash = 9d7a6eba2db88b8af6457acce0b638324ca03cde2e9589ae96749114091c6e9e
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/vulkan-validationlayers_1.2.158-2/get_patch
|
||||
patch_filename = vulkan-validationlayers-1.2.158-2-wrap.zip
|
||||
patch_hash = d38dae45ca6a7124ee8df045a8d5288390ab836b4d28c53234f90baee5c649fc
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 713 KiB |
@@ -0,0 +1,51 @@
|
||||
find_package(Vulkan REQUIRED)
|
||||
find_package(vma REQUIRED)
|
||||
find_package(glfw3 REQUIRED)
|
||||
find_package(Cmock REQUIRED)
|
||||
find_package(stb REQUIRED)
|
||||
|
||||
add_definitions( -DRSE_TEST=1 )
|
||||
|
||||
add_executable(tests
|
||||
src/main.c
|
||||
src/mocks/vulkan/Mockvulkan_core.c
|
||||
src/mocks/GLFW/Mockglfw3.c
|
||||
src/mocks/vk_mem_alloc.c
|
||||
src/math_tests.c
|
||||
src/graphics_window_tests.c
|
||||
src/graphics_vulkan_tests.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/window.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_base.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_pipeline.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_commands.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_buffers.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_descriptors.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/graphics.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/math.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/mesh_controller.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_commons.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/vulkan_image.c
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/text_renderer.c
|
||||
${PROJECT_SOURCE_DIR}/utilities/src/logger.c
|
||||
${PROJECT_SOURCE_DIR}/utilities/src/file_utils.c
|
||||
${PROJECT_SOURCE_DIR}/utilities/src/task_linux.c
|
||||
)
|
||||
|
||||
|
||||
target_include_directories(tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${glfw3_INCLUDE_DIR}
|
||||
${Vulkan_INCLUDE_DIR}
|
||||
${unity_INCLUDE_DIR}
|
||||
${cmock_INCLUDE_DIR}
|
||||
src/mocks/
|
||||
${PROJECT_SOURCE_DIR}/graphics/
|
||||
${PROJECT_SOURCE_DIR}/graphics/src/
|
||||
${PROJECT_SOURCE_DIR}/utilities/
|
||||
)
|
||||
|
||||
target_link_libraries(tests PRIVATE
|
||||
${cmock}
|
||||
${unity}
|
||||
m
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @file main.c
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief Main file for tests
|
||||
* @version 0.1
|
||||
* @date 2024-02-16
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#define RSE_TEST 1
|
||||
#include "rse_commons.h"
|
||||
#define VMA_IMPLEMENTATION
|
||||
#include "unity_internals.h"
|
||||
#include "rse_graphics_window_tests.h"
|
||||
#include "rse_math_tests.h"
|
||||
#include "rse_graphics_vulkan_tests.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
rse_graphics_window_tests();
|
||||
rse_math_tests();
|
||||
rse_graphics_vulkan_tests();
|
||||
return UNITY_END();
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,44 @@
|
||||
#ifndef FTTYPES_H
|
||||
#define FTTYPES_H
|
||||
|
||||
#define FT_FREETYPE_H <stdio.h>
|
||||
#define FT_LOAD_RENDER 1
|
||||
|
||||
typedef void* FT_Library;
|
||||
typedef int FT_Error;
|
||||
|
||||
typedef long FT_Long;
|
||||
typedef unsigned long FT_ULong;
|
||||
typedef unsigned int FT_UInt;
|
||||
typedef FT_Long FT_Int32;
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
} FT_Vector;
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int rows;
|
||||
void *buffer;
|
||||
} FT_Bitmap;
|
||||
|
||||
typedef struct {
|
||||
int bitmap_left;
|
||||
int bitmap_top;
|
||||
FT_Vector advance;
|
||||
FT_Bitmap bitmap;
|
||||
} FT_GlyphSlotRec_;
|
||||
|
||||
typedef struct {
|
||||
int num_glyphs;
|
||||
FT_GlyphSlotRec_ *glyph;
|
||||
} FT_FaceRec_;
|
||||
|
||||
typedef FT_FaceRec_* FT_Face;
|
||||
|
||||
FT_Error FT_Init_FreeType( FT_Library *alibrary ) { return 0; }
|
||||
FT_Error FT_New_Face( FT_Library library, const char* filepathname, FT_Long face_index, FT_Face *aface ) { return 0; }
|
||||
FT_Error FT_Set_Pixel_Sizes( FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height ) { return 0; }
|
||||
FT_Error FT_Load_Char( FT_Face face, FT_ULong char_code, FT_Int32 load_flags ) { return 0; }
|
||||
#endif // FTTYPES_H
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef STBI_IMAGE_H
|
||||
#define STBI_IMAGE_H
|
||||
|
||||
#define STBI_rgb_alpha 4
|
||||
|
||||
typedef unsigned char stbi_uc;
|
||||
|
||||
stbi_uc g_mocked_uc;
|
||||
|
||||
void stbi_image_free (void *retval_from_stbi_load) {}
|
||||
|
||||
stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) { return &g_mocked_uc; }
|
||||
|
||||
#endif // STBI_IMAGE_H
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "vk_mem_alloc.h"
|
||||
|
||||
VkResult vmaCreateAllocator(VmaAllocatorCreateInfo* pCreateInfo, VmaAllocator* pAllocator) { return VK_SUCCESS; }
|
||||
|
||||
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo* pBufferCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, VkBuffer* pBuffer, VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { return VK_SUCCESS; }
|
||||
|
||||
VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo* pImageCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, VkImage* pImage, VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { return VK_SUCCESS; }
|
||||
|
||||
void vmaDestroyAllocator(VmaAllocator allocator) {}
|
||||
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation) {}
|
||||
void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation) {}
|
||||
VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void** ppData) { return VK_SUCCESS; }
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
|
||||
#define AMD_VULKAN_MEMORY_ALLOCATOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#define VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE 1
|
||||
#define VMA_ALLOCATION_CREATE_MAPPED_BIT 2
|
||||
#define VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT 3
|
||||
#define VMA_MEMORY_USAGE_AUTO_PREFER_HOST 4
|
||||
#define VMA_MEMORY_USAGE_AUTO 5;
|
||||
#define VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT 6
|
||||
|
||||
typedef int VmaAllocation;
|
||||
typedef int VmaAllocator;
|
||||
typedef int VmaMemoryUsage;
|
||||
typedef int VmaAllocationCreateFlags;
|
||||
|
||||
typedef struct {
|
||||
VkBuffer buffer;
|
||||
VmaAllocation allocation;
|
||||
} VmaBuffer;
|
||||
|
||||
typedef struct {
|
||||
VkImage image;
|
||||
VmaAllocation allocation;
|
||||
} VmaImage;
|
||||
|
||||
typedef struct {
|
||||
float priority;
|
||||
uint32_t flags;
|
||||
uint32_t usage;
|
||||
uint32_t requiredFlags;
|
||||
uint32_t preferredFlags;
|
||||
uint32_t memoryTypeBits;
|
||||
void* pool;
|
||||
void* pUserData;
|
||||
} VmaAllocationCreateInfo;
|
||||
|
||||
typedef struct {
|
||||
VkDeviceMemory memory;
|
||||
VkDeviceSize offset;
|
||||
VkDeviceSize size;
|
||||
void* pMappedData;
|
||||
} VmaAllocationInfo;
|
||||
|
||||
typedef struct {
|
||||
void* vkGetInstanceProcAddr;
|
||||
void* vkGetDeviceProcAddr;
|
||||
} VmaVulkanFunctions;
|
||||
|
||||
typedef struct {
|
||||
uint32_t vulkanApiVersion;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
VkDevice device;
|
||||
VkInstance instance;
|
||||
VmaVulkanFunctions* pVulkanFunctions;
|
||||
} VmaAllocatorCreateInfo;
|
||||
|
||||
VkResult vmaCreateAllocator(VmaAllocatorCreateInfo* pCreateInfo, VmaAllocator* pAllocator);
|
||||
|
||||
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo* pBufferCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, VkBuffer* pBuffer, VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo);
|
||||
|
||||
VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo* pImageCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, VkImage* pImage, VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo);
|
||||
|
||||
void vmaDestroyAllocator(VmaAllocator allocator);
|
||||
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation);
|
||||
void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation);
|
||||
VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void** ppData);
|
||||
|
||||
#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @file rse_graphics_vulkan_tests.c
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2024-02-16
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mocks/vulkan/Mockvulkan_core.h"
|
||||
#include "mocks/GLFW/Mockglfw3.h"
|
||||
#include "rse_graphics_vulkan_tests.h"
|
||||
#include <stdio.h>
|
||||
#include "graphics/rse_graphics.h"
|
||||
#include "utilities/rse_errors_common.h"
|
||||
#include "unity.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
static void test_rse_rse_graphics_init(void)
|
||||
{
|
||||
VkExtensionProperties extenstion_properties[1];
|
||||
VkLayerProperties layer_properties[1];
|
||||
uint32_t count = 1;
|
||||
|
||||
sprintf(extenstion_properties[0].extensionName, "VK_KHR_surface");
|
||||
sprintf(layer_properties[0].layerName, "VK_LAYER_KHRONOS_validation");
|
||||
|
||||
/* initialize GLFW */
|
||||
glfwInit_ExpectAndReturn(GLFW_TRUE);
|
||||
glfwVulkanSupported_ExpectAndReturn(GLFW_TRUE);
|
||||
glfwSetErrorCallback_ExpectAndReturn(NULL, NULL);
|
||||
glfwSetErrorCallback_IgnoreArg_callback();
|
||||
glfwWindowHint_Expect(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
glfwCreateWindow_ExpectAndReturn(640, 480, "RedScarfEngine", NULL, NULL, (GLFWwindow*)1);
|
||||
glfwSetKeyCallback_ExpectAndReturn((GLFWwindow*)1, NULL, NULL);
|
||||
glfwSetKeyCallback_IgnoreArg_callback();
|
||||
glfwSetFramebufferSizeCallback_ExpectAndReturn((GLFWwindow*)1, NULL, NULL);
|
||||
glfwSetFramebufferSizeCallback_IgnoreArg_callback();
|
||||
|
||||
/* create instance */
|
||||
vkEnumerateInstanceLayerProperties_ExpectAnyArgsAndReturn(VK_SUCCESS);
|
||||
vkEnumerateInstanceLayerProperties_ExpectAnyArgsAndReturn(VK_SUCCESS);
|
||||
vkEnumerateInstanceLayerProperties_ReturnThruPtr_pPropertyCount(&count);
|
||||
/* TODO: Currently, due to the implementation, layer_properties that we get here is NULL.
|
||||
* This is because mocked rse_rse_malloc does nothing. Fix this somehow */
|
||||
vkEnumerateInstanceLayerProperties_ReturnThruPtr_pProperties(layer_properties);
|
||||
|
||||
vkEnumerateInstanceExtensionProperties_ExpectAnyArgsAndReturn(VK_SUCCESS);
|
||||
vkEnumerateInstanceExtensionProperties_ExpectAnyArgsAndReturn(VK_SUCCESS);
|
||||
vkEnumerateInstanceExtensionProperties_ReturnThruPtr_pPropertyCount(&count);
|
||||
vkEnumerateInstanceExtensionProperties_ReturnThruPtr_pProperties(extenstion_properties);
|
||||
|
||||
TEST_ASSERT_EQUAL(RSE_ERROR_NO_ERROR, rse_rse_graphics_init());
|
||||
}
|
||||
|
||||
void rse_graphics_vulkan_tests(void)
|
||||
{
|
||||
RUN_TEST(test_rse_rse_graphics_init);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file rse_graphics_vulkan_tests.h
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief Tests for rse_graphics_vulkan module
|
||||
* @version 0.1
|
||||
* @date 2024-02-16
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICS_VULKAN_TESTS_H
|
||||
#define GRAPHICS_VULKAN_TESTS_H
|
||||
|
||||
void rse_graphics_vulkan_tests(void);
|
||||
|
||||
#endif // GRAPHICS_VULKAN_TESTS_H
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "rse_graphics_window_tests.h"
|
||||
|
||||
/*-----------------RSE WINDOW TESTS------------------------*/
|
||||
#include "mocks/GLFW/Mockglfw3.h"
|
||||
#include "graphics/src/window.h"
|
||||
#include "utilities/rse_errors_common.h"
|
||||
#include "graphics/src/rse_window_errors.h"
|
||||
|
||||
void setUp (void) {}
|
||||
void tearDown (void) {}
|
||||
|
||||
static void test_rse_window_init_window_init_success()
|
||||
{
|
||||
uint8_t window = 0;
|
||||
|
||||
glfwInit_ExpectAndReturn(GLFW_TRUE);
|
||||
glfwVulkanSupported_ExpectAndReturn(GLFW_TRUE);
|
||||
glfwSetErrorCallback_ExpectAndReturn(NULL, NULL);
|
||||
glfwSetErrorCallback_IgnoreArg_callback();
|
||||
glfwWindowHint_Expect(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
glfwCreateWindow_ExpectAndReturn(640, 480, "RedScarfEngine", NULL, NULL, (GLFWwindow*)&window);
|
||||
glfwSetKeyCallback_ExpectAndReturn((GLFWwindow*)&window, NULL, NULL);
|
||||
glfwSetKeyCallback_IgnoreArg_callback();
|
||||
glfwSetFramebufferSizeCallback_ExpectAndReturn((GLFWwindow*)&window, NULL, NULL);
|
||||
glfwSetFramebufferSizeCallback_IgnoreArg_callback();
|
||||
|
||||
TEST_ASSERT_EQUAL(RSE_ERROR_NO_ERROR, window_init());
|
||||
}
|
||||
|
||||
static void test_rse_window_init_glfw_init_failed()
|
||||
{
|
||||
glfwInit_ExpectAndReturn(GLFW_FALSE);
|
||||
TEST_ASSERT_EQUAL(WINDOW_ERROR_GLFW_INIT_FAILED, window_init());
|
||||
}
|
||||
|
||||
static void test_rse_window_init_glfw_vulkan_not_supported()
|
||||
{
|
||||
glfwInit_ExpectAndReturn(GLFW_TRUE);
|
||||
glfwVulkanSupported_ExpectAndReturn(GLFW_FALSE);
|
||||
TEST_ASSERT_EQUAL(WINDOW_ERROR_VULKAN_NOT_LOADED, window_init());
|
||||
}
|
||||
|
||||
static void test_rse_window_init_window_not_created()
|
||||
{
|
||||
glfwInit_ExpectAndReturn(GLFW_TRUE);
|
||||
glfwVulkanSupported_ExpectAndReturn(GLFW_TRUE);
|
||||
glfwSetErrorCallback_ExpectAndReturn(NULL, NULL);
|
||||
glfwSetErrorCallback_IgnoreArg_callback();
|
||||
glfwWindowHint_Expect(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
glfwCreateWindow_ExpectAndReturn(640, 480, "RedScarfEngine", NULL, NULL, NULL);
|
||||
TEST_ASSERT_EQUAL(WINDOW_ERROR_WINDOW_NOT_CREATED, window_init());
|
||||
}
|
||||
|
||||
void rse_graphics_window_tests(void)
|
||||
{
|
||||
RUN_TEST(test_rse_window_init_window_init_success);
|
||||
RUN_TEST(test_rse_window_init_glfw_init_failed);
|
||||
RUN_TEST(test_rse_window_init_glfw_vulkan_not_supported);
|
||||
RUN_TEST(test_rse_window_init_window_not_created);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file rse_graphics_window_tests.h
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief Tests for rse_graphics module
|
||||
* @version 0.1
|
||||
* @date 2024-02-16
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICS_TESTS_H
|
||||
#define GRAPHICS_TESTS_H
|
||||
|
||||
void rse_graphics_window_tests(void);
|
||||
|
||||
#endif // GRAPHICS_TESTS_H
|
||||
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* @file rse_math_tests.c
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief Tests for rse_math module
|
||||
* @version 0.1
|
||||
* @date 2024-02-16
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rse_math_tests.h"
|
||||
#include "graphics/src/rse_math.h"
|
||||
#include "unity.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
static void test_rse_math_test_vector3_normalize(void)
|
||||
{
|
||||
float res_x = 0.8017837f;
|
||||
float res_y = 0.2672612f;
|
||||
float res_z = 0.5345224f;
|
||||
struct vec3_t test_vec = {3.0f, 1.0f, 2.0f};
|
||||
|
||||
struct vec3_t result = rse_math_normalize_vec3(&test_vec);
|
||||
|
||||
TEST_ASSERT_EQUAL_FLOAT(res_x, result.x);
|
||||
TEST_ASSERT_EQUAL_FLOAT(res_y, result.y);
|
||||
TEST_ASSERT_EQUAL_FLOAT(res_z, result.z);
|
||||
}
|
||||
|
||||
static void test_rse_math_test_vector3_cross(void)
|
||||
{
|
||||
float res_x = -3.0f;
|
||||
float res_y = 6.0f;
|
||||
float res_z = -3.0f;
|
||||
struct vec3_t test_vec1 = {2.0f, 3.0f, 4.0f};
|
||||
struct vec3_t test_vec2 = {5.0f, 6.0f, 7.0f};
|
||||
|
||||
struct vec3_t result = rse_math_cross_vec3(&test_vec1, &test_vec2);
|
||||
|
||||
TEST_ASSERT_EQUAL_FLOAT(res_x, result.x);
|
||||
TEST_ASSERT_EQUAL_FLOAT(res_y, result.y);
|
||||
TEST_ASSERT_EQUAL_FLOAT(res_z, result.z);
|
||||
}
|
||||
|
||||
static void test_rse_math_test_look_at(void)
|
||||
{
|
||||
struct vec3_t eye;
|
||||
struct vec3_t center;
|
||||
struct vec3_t up;
|
||||
struct mat4_t result;
|
||||
struct mat4_t expected;
|
||||
|
||||
eye.x = 0.0f;
|
||||
eye.y = 0.0f;
|
||||
eye.z = 5.0f;
|
||||
|
||||
center.x = 0.0f;
|
||||
center.y = 0.0f;
|
||||
center.z = 0.0f;
|
||||
|
||||
up.x = 0.0f;
|
||||
up.y = 0.1f;
|
||||
up.z = 0.0f;
|
||||
|
||||
expected.x_1 = 1.0f;
|
||||
expected.y_1 = 0.0f;
|
||||
expected.z_1 = 0.0f;
|
||||
expected.w_1 = 0.0f;
|
||||
|
||||
expected.x_2 = 0.0f;
|
||||
expected.y_2 = 1.0f;
|
||||
expected.z_2 = 0.0f;
|
||||
expected.w_2 = 0.0f;
|
||||
|
||||
expected.x_3 = 0.0f;
|
||||
expected.y_3 = 0.0f;
|
||||
expected.z_3 = 1.0f;
|
||||
expected.w_3 = 0.0f;
|
||||
|
||||
expected.x_4 = 0.0f;
|
||||
expected.y_4 = 0.0f;
|
||||
expected.z_4 = -5.0f;
|
||||
expected.w_4 = 1.0f;
|
||||
|
||||
result = rse_math_look_at(&eye, ¢er, &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);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @file rse_math_tests.h
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief Tests for rse_math module
|
||||
* @version 0.1
|
||||
* @date 2024-02-16
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
void rse_math_tests(void);
|
||||
+19530
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,17 @@
|
||||
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
File diff suppressed because it is too large.
Load diff
+9
-14
@@ -12,7 +12,8 @@
|
||||
#ifndef COMMONS_H
|
||||
#define COMMONS_H
|
||||
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include "utilities/logger.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -21,14 +22,14 @@
|
||||
*
|
||||
*/
|
||||
#ifndef RSE_TEST
|
||||
#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_malloc(ptr, size) \
|
||||
ptr = malloc(size); \
|
||||
if (ptr == NULL) { \
|
||||
LOGF(_("Memory allocation failed\n")); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define rse_free(ptr) if (ptr != NULL) free(ptr);
|
||||
#define rse_free(ptr) free(ptr);
|
||||
|
||||
#define rse_memcpy(dst, src, size) memcpy(dst, src, size);
|
||||
#define rse_memset(ptr, value, size) memset(ptr, value, size);
|
||||
@@ -45,10 +46,4 @@
|
||||
*/
|
||||
typedef uint32_t rse_err_t;
|
||||
|
||||
/**
|
||||
* @brief General type for ID use
|
||||
*
|
||||
*/
|
||||
typedef uint32_t rse_id_t;
|
||||
|
||||
#endif /* COMMONS_H */
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* @file entity.h
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @brief Entity from ECS
|
||||
* @version 0.1
|
||||
* @date 2025-09-25
|
||||
*
|
||||
* @copyright Copyright (c) 2025
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ENTITY_H
|
||||
#define ENTITY_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ENTITY_MAX_COUNT 65535
|
||||
#define COMPONENT_MAX_COUNT 65535
|
||||
|
||||
typedef uint32_t entity_t;
|
||||
|
||||
entity_t entity_create(void);
|
||||
void entity_free(entity_t entity);
|
||||
|
||||
#endif // !ENTITY_H
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
#define FILE_UTILS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "commons.h"
|
||||
#include "cgltf.h"
|
||||
|
||||
#define MAX_PATH_LENGTH (4096U)
|
||||
#include "utilities/commons.h"
|
||||
|
||||
/**
|
||||
* @brief Reads file and puts its content into char buffer. If NULL is provided as buffer, than just bytes_count
|
||||
@@ -15,13 +12,11 @@
|
||||
* @param bytes_count Returned bytes count
|
||||
* @param buffer Returned filled buffer
|
||||
*/
|
||||
rse_err_t file_read_bytes(const char* file_path, size_t* bytes_count, unsigned char* buffer);
|
||||
void read_file(const char* file_path, size_t* bytes_count, char* buffer);
|
||||
|
||||
rse_err_t file_write(const char* file_name, size_t bytes_count, char* buffer);
|
||||
void write_to_file(const char* file_name, size_t bytes_count, char* buffer);
|
||||
|
||||
rse_err_t file_append_full_path(char* file_path, size_t max_buffer_size);
|
||||
|
||||
rse_err_t file_load_gltf(const char* file_name, cgltf_data** data);
|
||||
rse_err_t get_curr_path(char* path, size_t max_path_length);
|
||||
|
||||
#endif /* FILE_UTILS_H */
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
|
||||
/**
|
||||
* @file linked_list.h
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2024-05-01
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINKED_LIST_H
|
||||
#define LINKED_LIST_H
|
||||
|
||||
#include "utilities/commons.h"
|
||||
|
||||
/**
|
||||
* @brief Define a linked list structure
|
||||
*
|
||||
* @param name Name of the linked list structure
|
||||
* @param type Type of the data stored in the linked list
|
||||
* @return struct name { type data; struct name *next; }
|
||||
* @note This macro defines a linked list structure with a given name and data type
|
||||
*/
|
||||
#define RSE_LINKED_LIST_DEFINE(name, type) \
|
||||
struct name { \
|
||||
type data; \
|
||||
struct name *next; \
|
||||
}
|
||||
|
||||
#define RSE_LINKED_LIST_PUSH_FRONT(list, value) \
|
||||
__typeof__(list) new_node = NULL; \
|
||||
rse_malloc(new_node, sizeof(*(list))); \
|
||||
new_node->data = value; \
|
||||
new_node->next = list; \
|
||||
list = new_node
|
||||
|
||||
#define RSE_LINKED_LIST_PUSH_BACK(list, value) \
|
||||
__typeof__((list)) new_node = NULL; \
|
||||
rse_malloc(new_node, sizeof(*(list))); \
|
||||
new_node->data = value; \
|
||||
new_node->next = NULL; \
|
||||
if (list == NULL) { \
|
||||
list = new_node; \
|
||||
} else { \
|
||||
__typeof__(list) current = list; \
|
||||
while (current->next != NULL) { \
|
||||
current = current->next; \
|
||||
} \
|
||||
current->next = new_node; \
|
||||
}
|
||||
|
||||
#define RSE_LINKED_LIST_POP_FRONT(list) \
|
||||
if (list != NULL) { \
|
||||
__typeof__(list) temp = list; \
|
||||
list = list->next; \
|
||||
rse_free(temp); \
|
||||
}
|
||||
|
||||
#define RSE_LINKED_LIST_POP_BACK(list) \
|
||||
if (list != NULL) { \
|
||||
if (list->next == NULL) { \
|
||||
rse_free(list); \
|
||||
list = NULL; \
|
||||
} else { \
|
||||
__typeof__(list) *current = list; \
|
||||
while (current->next->next != NULL) { \
|
||||
current = current->next; \
|
||||
} \
|
||||
rse_free(current->next); \
|
||||
current->next = NULL; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define RSE_LINKED_LIST_FRONT(list) \
|
||||
list->data
|
||||
|
||||
#define RSE_LINKED_LIST_BACK(list) \
|
||||
if (list != NULL) { \
|
||||
__typeof__(list) *current = list; \
|
||||
while (current->next != NULL) { \
|
||||
current = current->next; \
|
||||
} \
|
||||
current->data; \
|
||||
}
|
||||
|
||||
#define RSE_LINKED_LIST_IS_EMPTY(list) \
|
||||
(list == NULL)
|
||||
|
||||
#define RSE_LINKED_LIST_SIZE(list) \
|
||||
({ \
|
||||
int size = 0; \
|
||||
__typeof__(list) current = list; \
|
||||
while (current != NULL) { \
|
||||
size++; \
|
||||
current = current->next; \
|
||||
} \
|
||||
size; \
|
||||
})
|
||||
|
||||
#define RSE_LINKED_LIST_CLEAR(list) \
|
||||
while (list != NULL) { \
|
||||
__typeof__(list) temp = list; \
|
||||
list = list->next; \
|
||||
rse_free(temp); \
|
||||
}
|
||||
|
||||
#define RSE_LINKED_LIST_FOREACH(list, i) \
|
||||
for (__typeof__(list) i = list; i != NULL; i = i->next)
|
||||
|
||||
|
||||
#define RSE_LINKED_LIST_FOREACH_REVERSE(list, i) \
|
||||
for (__typeof__(list) i = list; i != NULL; i = i->next)
|
||||
|
||||
#define RSE_LINKED_LIST_TO_ARRAY(list, array) \
|
||||
({ \
|
||||
int size = RSE_LINKED_LIST_SIZE(list); \
|
||||
rse_malloc(array, size * sizeof(*(list))); \
|
||||
int i = 0; \
|
||||
RSE_LINKED_LIST_FOREACH(list, current) { \
|
||||
array[i++] = current->data; \
|
||||
} \
|
||||
size; \
|
||||
})
|
||||
|
||||
#endif // LINKED_LIST_H
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @file locale_common.h
|
||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||
* @brief Error messages for Vulkan initializers
|
||||
* @version 0.1
|
||||
* @date 2022-05-13
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOCALE_COMMON_H
|
||||
#define LOCALE_COMMON_H
|
||||
|
||||
#include "localization.h"
|
||||
|
||||
#define common_messages common_messages_nolocale[SELECTED_LANGUAGE]
|
||||
|
||||
enum common_errors_t {
|
||||
COMMON_ERROR_MEMORY_ALLOCATION_FAILED,
|
||||
COMMON_ERROR_LAST_MESSAGE
|
||||
};
|
||||
|
||||
static const char common_messages_nolocale[LAST_LANGUAGE][COMMON_ERROR_LAST_MESSAGE][MAX_MESSAGE_LENGHT] = {
|
||||
{/* EN_US */
|
||||
/* COMMON_MEMORY_ALLOCATION_FAILED */
|
||||
"Unable to allocate memory"
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* LOCALE_COMMON_H */
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @file localization.h
|
||||
* @author Piotr Krygier (everyonencancode@gmail.com)
|
||||
* @brief Simple localization header. Gettext is used for localization
|
||||
* @version 0.1
|
||||
* @date 2022-02-15
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOCALIZATION_H
|
||||
#define LOCALIZATION_H
|
||||
|
||||
#include <libintl.h>
|
||||
|
||||
/* Enable localization */
|
||||
#define _(STRING) gettext(STRING)
|
||||
|
||||
#endif /* LOCALIZATION_H */
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @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 */
|
||||
@@ -1,25 +0,0 @@
|
||||
rse_utilities_srcs = [
|
||||
'src/file_utils.c',
|
||||
'src/time_utils.c'
|
||||
]
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
sdl3_dep = dependency('sdl3',
|
||||
version : '>=3.4.0')
|
||||
stb = dependency('stb',
|
||||
fallback: ['stb', 'stb_dep'])
|
||||
|
||||
m_dep = cc.find_library('m')
|
||||
|
||||
rse_utilities_lib = shared_library(
|
||||
'rse_utilities',
|
||||
rse_utilities_srcs,
|
||||
include_directories : ['.'],
|
||||
dependencies : [
|
||||
sdl3_dep,
|
||||
stb,
|
||||
m_dep,
|
||||
],
|
||||
install : true
|
||||
)
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @file entity.c
|
||||
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
|
||||
* @brief Entity from ECS
|
||||
* @version 0.1
|
||||
* @date 2025-09-25
|
||||
*
|
||||
* @copyright Copyright (c) 2025
|
||||
*
|
||||
*/
|
||||
|
||||
#include "utilities/entity.h"
|
||||
#include <stdint.h>
|
||||
|
||||
static entity_t g_free_list[ENTITY_MAX_COUNT];
|
||||
static uint32_t g_free_count = 0U;
|
||||
static uint32_t g_living_count = 0U;
|
||||
|
||||
entity_t entity_create(void)
|
||||
{
|
||||
entity_t e = 0U;
|
||||
if (g_free_count > 0) {
|
||||
e = g_free_list[g_free_count--];
|
||||
} else {
|
||||
e = g_living_count++;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void entity_free(entity_t entity)
|
||||
{
|
||||
g_free_list[g_free_count++] = entity;
|
||||
}
|
||||
+60
-147
@@ -1,51 +1,88 @@
|
||||
#include "file_utils.h"
|
||||
#include "utilities/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 "SDL3/SDL_log.h"
|
||||
#include "commons.h"
|
||||
#include "errors_common.h"
|
||||
#include "utilities/commons.h"
|
||||
#include "utilities/errors_common.h"
|
||||
#include "utilities/localization.h"
|
||||
#include "utilities/logger.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#endif /* ifndef __linux */
|
||||
|
||||
static rse_err_t get_curr_path(char* path)
|
||||
#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)
|
||||
{
|
||||
char* last_sep = NULL;
|
||||
char* tmp_path = NULL;
|
||||
size_t path_length = 0U;
|
||||
ssize_t binary_path_length = 0U;
|
||||
size_t binary_path_length = 0U;
|
||||
size_t directory_path_length = 0U;
|
||||
|
||||
if (NULL == path) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to get path. Provided pointer is NULL");
|
||||
LOGE(_("Failed to get path. Provided pointer is NULL\n"));
|
||||
|
||||
return RSE_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
path_length = strlen(path);
|
||||
if (path_length > MAX_PATH_LENGTH) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Max path length cannot be 0");
|
||||
if (0 == path_size) {
|
||||
LOGE(_("Max path length cannot be 0\n"));
|
||||
|
||||
return RSE_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
rse_memset(path, '\0', path_length);
|
||||
rse_memset(path, '\0', path_size);
|
||||
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) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to read current binary path");
|
||||
LOGF(_("Failed to read current binary path\n"));
|
||||
|
||||
return RSE_ERROR_NULL_POINTER;
|
||||
}
|
||||
@@ -56,15 +93,15 @@ static rse_err_t get_curr_path(char* path)
|
||||
last_sep = strrchr(tmp_path, '/') + 1;
|
||||
|
||||
if (NULL == last_sep) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to get last separator in path");
|
||||
LOGE(_("Failed to get last separator in path\n"));
|
||||
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
directory_path_length = last_sep - tmp_path;
|
||||
|
||||
if (directory_path_length > MAX_PATH_LENGTH) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided path buffer is too small");
|
||||
if (directory_path_length > path_size) {
|
||||
LOGE(_("Provided path buffer is too small\n"));
|
||||
|
||||
return RSE_ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@@ -75,127 +112,3 @@ static rse_err_t get_curr_path(char* path)
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#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
Reference in new issue
Block a user