16 Commits

Author SHA1 Message Date
everyonecancode bbccf7f0ba Add wrap dependency for vulkan headers
Don't expect the vulkan headers to be present on the development system. Instead, download the using meson wrap.

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Piotr Krygier <piotrkrygier@everyonecancode@xyz>
2026-02-10 13:09:18 +01:00
49 changed files with 8292 additions and 421 deletions
+4 -2
View File
@@ -52,5 +52,7 @@ CMakeFiles/
.clangd/
compile_commands.json
# Environment preparation scripts
setup-env.sh
# Ignore subproject path except config files
subprojects/*
!subprojects/packagefiles/
!subprojects/*.wrap
BIN
View File
Binary file not shown.
+18
View File
@@ -0,0 +1,18 @@
rse_events_sources = [
'src/events_manager.c'
]
sdl3_dep = dependency('sdl3',
version : '>=3.4.0')
rse_events_lib = shared_library(
'rse_events',
rse_events_sources,
include_directories : [
'../'
],
dependencies : [sdl3_dep,
],
link_with: rse_utilities_lib,
install : true
)
+26
View File
@@ -0,0 +1,26 @@
/**
* @file events_manager.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Events manager for Red Scarf Engine
* @version 0.1
* @date 2026-03-02
*
* @copyright Copyright (c) 2026
*
*/
#ifndef RSE_EVENTS_MANAGER_H_
#define RSE_EVENTS_MANAGER_H_
#include "src/events_context.h"
#include "utilities/commons.h"
rse_err_t rse_events_init(struct events_context_t* events_context);
rse_err_t rse_events_set_event_handler(struct events_context_t* events_context,
enum EVENT_HANDLER event_type,
rse_event_handler_t event_handler);
rse_err_t rse_events_main_loop(struct events_context_t* context);
#endif /* RSE_EVENTS_MANAGER_H_ */
+44
View File
@@ -0,0 +1,44 @@
/**
* @file events_context.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Events context for Red Scarf Engine
* @version 0.1
* @date 2026-03-02
*
* @copyright Copyright (c) 2026
*
*/
#ifndef EVENT_CONTEXT_H_
#define EVENT_CONTEXT_H_
#include "SDL3/SDL_mouse.h"
#include "utilities/commons.h"
enum EVENT_HANDLER {
EVENT_HANDLER_QUIT,
EVENT_HANDLER_MOUSE,
EVENT_HANDLER_LAST
};
enum EVENT_SHOULD_STOP_ENGINE {
EVENT_SHOULD_STOP_ENGINE_FALSE,
EVENT_SHOULD_STOP_ENGINE_TRUE
};
struct mouse_event_data_t
{
float x_pos;
float y_pos;
SDL_MouseButtonFlags mouse_buttons;
};
typedef rse_err_t (*rse_event_handler_t)(void*);
struct events_context_t
{
enum EVENT_SHOULD_STOP_ENGINE should_stop;
rse_event_handler_t event_handlers[EVENT_HANDLER_LAST];
};
#endif /* EVENT_CONTEXT_H_ */
+99
View File
@@ -0,0 +1,99 @@
/**
* @file events_manager.c
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Events manager for Red Scarf Engine
* @version 0.1
* @date 2026-03-02
*
* @copyright Copyright (c) 2026
*
*/
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_log.h"
#include "SDL3/SDL_mouse.h"
#include "events_context.h"
#include "rse_events_manager.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
static rse_err_t default_event(void* args)
{
(void)args;
return RSE_ERROR_NO_ERROR;
}
static rse_err_t run_custom_event_handler(struct events_context_t* context,
enum EVENT_HANDLER event_handler,
void* args)
{
return context->event_handlers[event_handler](args);
}
static rse_err_t quit_event(struct events_context_t* context)
{
context->should_stop = EVENT_SHOULD_STOP_ENGINE_TRUE;
return run_custom_event_handler(context, EVENT_HANDLER_QUIT, NULL);
}
static rse_err_t mouse_event(struct events_context_t* context)
{
struct mouse_event_data_t mouse_data = {0};
mouse_data.mouse_buttons = SDL_GetMouseState(&mouse_data.x_pos, &mouse_data.y_pos);
return run_custom_event_handler(context, EVENT_HANDLER_MOUSE, (void*)&mouse_data);
}
rse_err_t rse_events_init(struct events_context_t* events_context)
{
size_t i = 0U;
for (i = 0U; i < EVENT_HANDLER_LAST; ++i) {
events_context->event_handlers[i] = default_event;
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t rse_events_set_event_handler(struct events_context_t* events_context,
enum EVENT_HANDLER event_type,
rse_event_handler_t event_handler)
{
if (event_type >= EVENT_HANDLER_LAST) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Invalid event type selected");
return RSE_ERROR_INVALID_PARAM;
}
if (event_handler == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Provided NULL event handler");
return RSE_ERROR_INVALID_PARAM;
}
events_context->event_handlers[event_type] = event_handler;
return RSE_ERROR_NO_ERROR;
}
rse_err_t rse_events_main_loop(struct events_context_t* context)
{
SDL_Event event;
rse_err_t status = RSE_ERROR_NO_ERROR;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_EVENT_QUIT:
STATUS_CHECK(quit_event(context));
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
STATUS_CHECK(mouse_event(context));
break;
}
}
return status;
}
+30 -21
View File
@@ -9,41 +9,50 @@ rse_graphics_sources = [
'src/vulkan_base.c',
'src/vulkan_buffers.c',
'src/vulkan_commands.c',
'src/vulkan_commons.c',
'src/vulkan_image.c',
'src/vulkan_render_pass.c',
'src/vulkan_swapchain.c',
'src/window.c',
]
]
vulkan_dep = dependency('vulkan',
version : '>=1.4.335')
vulkan_headers_dep = dependency('VulkanHeaders')
sdl3_dep = dependency('sdl3',
version : '>=3.4.0')
vulkan_dep = dependency('Vulkan')
freetype_dep = dependency('freetype2',
version : '>=22.1.16')
glm_dep = dependency('glm',
version : '>=1.0.3',
method : 'cmake')
vma_dep = dependency('VulkanMemoryAllocator',
version : '>=3.3.0',
modules : ['GPUOpen::VulkanMemoryAllocator'])
sdl3_dep = dependency('sdl3', version: '>=3.4.0')
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,
include_directories : [
'../'
],
dependencies : [vulkan_dep,
include_directories: ['../'],
dependencies: [
vulkan_headers_dep,
vulkan_dep,
vma_dep,
sdl3_dep,
freetype_dep,
glm_dep,
stb_dep,
stb
],
link_with: rse_utilities_lib,
install : true
)
install: true,
)
rse_graphics_lib_dep = declare_dependency(
link_with: rse_graphics_lib,
dependencies: [
vulkan_headers_dep,
vulkan_dep,
vma_dep,
glm_dep
],
include_directories: include_directories('.')
)
+8 -5
View File
@@ -1,10 +1,9 @@
#ifndef RSE_GRAPHICS_H
#define RSE_GRAPHICS_H
#include "src/graphics_context.h"
#include "utilities/commons.h"
struct rse_graphics_context_t;
/**
* @brief Initializes graphics backend. This has to be called before any new mesh, texture or any other object is
* added
@@ -12,20 +11,24 @@ struct rse_graphics_context_t;
* @param context Graphics context handle. Must be NULL!
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
rse_err_t rse_graphics_init(struct rse_graphics_context_t** context);
rse_err_t rse_graphics_init(struct graphics_context_t* context);
/**
* @brief Runs graphics engine. This has to be called after all objects are added to the scene
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
int rse_graphics_run(void* arg);
rse_err_t rse_graphics_run(struct graphics_context_t* context);
rse_err_t rse_graphics_main_loop(struct graphics_context_t* context);
/**
* @brief Custom function for testing engine. TODO: Remove it whe releasing
*
*/
rse_err_t rse_graphics_test_function(struct rse_graphics_context_t* context);
rse_err_t rse_graphics_test_function(struct graphics_context_t* context);
void rse_graphics_deinit(struct graphics_context_t* context);
#endif /* RSE_GRAPHICS_H */
+3 -5
View File
@@ -1,13 +1,11 @@
#version 450
layout(binding = 0) uniform UniformBufferObject {
layout(push_constant) uniform pc {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
#define MAX_INSTANCES 1024
struct InstanceData
{
vec3 pos;
@@ -16,8 +14,8 @@ struct InstanceData
uint texture_id;
};
layout(binding = 1) uniform InstanceUniformBuffer {
InstanceData instances[MAX_INSTANCES];
layout(set=0, binding = 0, std430) readonly buffer InstanceBuffer {
InstanceData instances[];
};
layout(location = 0) in vec3 inPosition;
+1 -1
View File
@@ -1,7 +1,7 @@
#version 450
#extension GL_EXT_nonuniform_qualifier : enable
layout(set = 0, binding = 2) uniform sampler2D texSampler[];
layout(set = 0, binding = 1) uniform sampler2D texSampler[];
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
+10 -1
View File
@@ -21,7 +21,7 @@
#include "utilities/entity.h"
#include "utilities/vector.h"
#include "vma/vk_mem_alloc.h"
#include "vk_mem_alloc.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_commons.h"
@@ -46,6 +46,7 @@
#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)
@@ -126,6 +127,12 @@ struct required_dynamic_states_t {
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;
@@ -159,6 +166,7 @@ struct pipeline_t
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;
};
@@ -273,6 +281,7 @@ struct graphics_context_t
uint32_t current_frame;
uint32_t swapchain_images_count;
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;
+150
View File
@@ -10,14 +10,18 @@
*/
#include <SDL3/SDL_log.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "src/graphics_context.h"
#include "src/vulkan_commons.h"
#include "utilities/cgltf.h"
#include "utilities/commons.h"
#include "utilities/entity.h"
#include "utilities/errors_common.h"
#include "utilities/file_utils.h"
#include "vulkan_buffers.h"
#include "vulkan_image.h"
rse_err_t mesh_create(struct graphics_context_t* context,
struct vertex_t* vertices,
@@ -46,6 +50,152 @@ rse_err_t mesh_create(struct graphics_context_t* context,
return status;
}
rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char* file_name, rse_id_t* mesh_id, rse_id_t* texture_id)
{
struct vertex_t* vertices = NULL;
uint16_t* indices = NULL;
size_t indices_count = 0U;
size_t mesh_index = 0U;
size_t primitive_index = 0U;
size_t attribute_index = 0U;
cgltf_mesh* mesh = NULL;
cgltf_primitive* primitive = NULL;
cgltf_attribute* attribute = NULL;
size_t* position_data_offset = NULL;
size_t* color_data_offset = NULL;
size_t* texture_coord_data_offset = NULL;
size_t* indices_data_offset = NULL;
size_t* texture_data_offset = NULL;
int* texture_data_size = NULL;
unsigned char *texture_pixel_buffer = NULL;
size_t data_count = 0U;
size_t data_index = 0U;
size_t tmp_offset = 0U;
uint8_t* bin = NULL;
(void)context;
(void)mesh_id;
cgltf_data* data = NULL;
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(file_load_gltf(file_name, &data));
for (mesh_index = 0U; mesh_index < data->meshes_count; ++mesh_index) {
mesh = &data->meshes[mesh_index];
for (primitive_index = 0U; primitive_index < mesh->primitives_count; ++primitive_index) {
primitive = &mesh->primitives[primitive_index];
if (primitive->indices == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU,
"No indices for mesh found. Currently only indexed drawing is supported");
return RSE_ERROR_INVALID_PARAM;
} else {
rse_malloc(indices_data_offset, sizeof(size_t));
*indices_data_offset = primitive->indices->buffer_view->offset;
indices_count = primitive->indices->count;
rse_malloc(indices, (sizeof(uint16_t) * indices_count));
}
if (primitive->material != NULL) {
if (primitive->material->has_pbr_metallic_roughness) {
rse_malloc(texture_data_offset, sizeof(size_t));
rse_malloc(texture_data_size, sizeof(int));
*texture_data_offset = primitive->material->pbr_metallic_roughness.base_color_texture.texture->image->buffer_view->offset;
*texture_data_size = primitive->material->pbr_metallic_roughness.base_color_texture.texture->image->buffer_view->size;
}
}
for (attribute_index = 0; attribute_index < primitive->attributes_count; ++attribute_index) {
attribute = &primitive->attributes[attribute_index];
tmp_offset = attribute->data->buffer_view->offset;
switch (attribute->type) {
case cgltf_attribute_type_position:
rse_malloc(position_data_offset, sizeof(size_t));
*position_data_offset = tmp_offset;
data_count = attribute->data->count;
break;
case cgltf_attribute_type_color:
rse_malloc(color_data_offset, sizeof(size_t));
*color_data_offset = tmp_offset;
break;
case cgltf_attribute_type_texcoord:
rse_malloc(texture_coord_data_offset, sizeof(size_t));
*texture_coord_data_offset = tmp_offset;
break;
default:
break;
}
}
}
if (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)
+1
View File
@@ -41,6 +41,7 @@ rse_err_t mesh_create(struct graphics_context_t* context, struct vertex_t* verti
rse_err_t mesh_create_instance(struct graphics_context_t* context, entity_t entity,
struct instance_data_t instance_data);
rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char* file_name, rse_id_t* mesh_id, rse_id_t* texture_id);
/**
* @brief Get the vertex count for mesh with mesh_id ID
*
+6
View File
@@ -0,0 +1,6 @@
#include "utilities/commons.h"
rse_err_t object_load_gltf(const char* file_path)
{
}
+22 -5
View File
@@ -29,7 +29,7 @@ rse_err_t shader_create_module(struct graphics_context_t* context,
const char* shader_path,
const VkShaderStageFlagBits shader_stage)
{
char* buffer;
unsigned char* buffer;
size_t buffer_size;
VkShaderModuleCreateInfo create_info = {0};
@@ -44,9 +44,9 @@ rse_err_t shader_create_module(struct graphics_context_t* context,
}
/* Read shader file */
file_read(shader_path, &buffer_size, NULL);
file_read_bytes(shader_path, &buffer_size, NULL);
rse_malloc(buffer, buffer_size);
file_read(shader_path, &buffer_size, buffer);
file_read_bytes(shader_path, &buffer_size, buffer);
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.pNext = NULL;
@@ -205,8 +205,8 @@ rse_err_t pipeline_add_descriptor_sets(struct graphics_context_t* context,
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 = 0U;
create_info.pPushConstantRanges = NULL;
create_info.pushConstantRangeCount = pipeline->push_constant_ranges.push_constant_ranges_count;
create_info.pPushConstantRanges = pipeline->push_constant_ranges.push_constant_ranges;
if (VK_SUCCESS !=
vkCreatePipelineLayout(context->vulkan_handles.device, &create_info, NULL, &pipeline->pipeline_layout)) {
@@ -341,6 +341,23 @@ rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline,
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)
{
assert(NULL != context);
+12
View File
@@ -92,6 +92,18 @@ rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline, const VkDynami
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.
+146 -105
View File
@@ -5,9 +5,7 @@
#include <SDL3/SDL_render.h>
#include <cglm/util.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vulkan/vulkan_core.h>
#include "descriptor_builder.h"
@@ -19,6 +17,7 @@
#include "src/vulkan_buffers.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/time_utils.h"
#include "vulkan_base.h"
#include "vulkan_commons.h"
#include "vulkan_image.h"
@@ -29,15 +28,65 @@ 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);
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,
@@ -57,100 +106,62 @@ static rse_err_t render_static_mesh(struct graphics_context_t* context, uint32_t
return RSE_ERROR_NO_ERROR;
}
static void model_view_projection_update(struct graphics_context_t* context, const struct vulkan_buffer_t* buffer)
{
struct uniform_buffer_object_t ubo = {0};
vec3 eye = {0};
vec3 center = {0};
vec3 up = {0};
vec3 rotation_vec = {1.0f, 0.0f, 0.0f};
float fovy;
float z_near;
float z_far;
eye[2] = -5.0f;
up[1] = 1.0f;
fovy = glm_rad(45.0f);
z_near = 0.1f;
z_far = 20.0f;
glm_mat4_identity(ubo.model);
glm_rotate(ubo.model, glm_rad(0.0f), rotation_vec);
glm_lookat(eye, center, up, ubo.view);
glm_perspective(
fovy,
context->swapchain_data.swapchain_extent.width / (float)context->swapchain_data.swapchain_extent.height,
z_near,
z_far,
ubo.proj);
memcpy(buffer->allocation_info.pMappedData, &ubo, sizeof(ubo));
}
rse_err_t rse_graphics_init(struct rse_graphics_context_t** context)
rse_err_t rse_graphics_init(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct rse_graphics_context_t* ctx_ptr = NULL;
if (*context != NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Graphics already initialized!");
return RSE_ERROR_ALREADY_INITIALIZED;
}
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(vulkan_init(ctx_ptr->context));
STATUS_CHECK(window_init(&context->window_handle, &context->is_framebuffer_resized));
STATUS_CHECK(vulkan_init(context));
return status;
}
rse_err_t rse_graphics_test_function(struct rse_graphics_context_t* rse_context)
#define TEXTURES_COUNT (3U)
rse_err_t rse_graphics_test_function(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct graphics_context_t* context = rse_context->context;
rse_id_t textures[2] = {0};
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};
struct vulkan_buffer_t model_view_projection_buffer = {0};
rse_id_t mesh_id_1, mesh_id_2 = 0;
rse_id_t font_id = 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;
texture_load_from_file(context, "../../test_image.jpg", &textures[0]);
texture_load_from_file(context, "../../test_image_2.jpg", &textures[1]);
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));
// STATUS_CHECK(fonts_init());
// STATUS_CHECK(fonts_load_from_file(context, "../../NotoSansMono-Regular.ttf", &font_id));
// STATUS_CHECK(fonts_set_font_size(context, font_id, 16));
// FIXME: Temporary array of vertices, for testing purposes
struct vertex_t vertices[] = {{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
struct vertex_t vertices[] = {
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}};
};
struct vertex_t vertices2[] = {{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}};
struct vertex_t vertices_2[] = {
{{-0.7f, -0.7f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{-0.7f, 0.7f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.1f, 0.7f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{0.1f, -0.7f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
};
// FIXME: Temporary array of vertices, for testing purposes
uint16_t indices[] = {0, 1, 2, 2, 3, 0};
uint16_t indices[] = {0, 1, 2, 2, 3, 0, 0, 3, 2, 2, 1, 0};
mesh_create(context, vertices, indices, 4, 6, &mesh_id_1);
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, 0.0f, 2.4f}, {0.0f, 0.0f, 0.0}, 1.0f, textures[1]})) {
(struct instance_data_t){{0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0}, 0.5f, textures[0]})) {
exit(1);
}
@@ -158,50 +169,60 @@ rse_err_t rse_graphics_test_function(struct rse_graphics_context_t* rse_context)
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)}, 1.0f, textures[0]})) {
(struct instance_data_t){{-0.5f, 0.0f, -1.0f}, {0.0f, 0.0f, glm_rad(10.0f)}, 0.5f, textures[1]})) {
exit(1);
}
mesh_create(context, vertices2, indices, 4, 6, &mesh_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){{1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, glm_rad(0.0f)}, 1.0f, textures[0]})) {
(struct instance_data_t){{0.5f, 0.0f, 1.0f}, {0.0f, 0.0f, glm_rad(0.0f)}, 0.5f, textures[1]})) {
exit(1);
}
buffer_create_uniform(context, &model_view_projection_buffer, sizeof(struct uniform_buffer_object_t));
model_view_projection_update(context, &model_view_projection_buffer);
buffers_update(context);
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);
}
sampler_create(context, &context->vulkan_handles.sampler);
STATUS_CHECK(buffers_update(context));
descriptor_pool_add_type(context, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2);
descriptor_pool_add_type(context, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2);
descriptor_pool_initialize(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 */
descriptor_create_new_set(context, &descriptor_set_handle);
descriptor_add_layout(&layout_bindings, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT);
descriptor_add_layout(&layout_bindings, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT);
descriptor_add_layout(&layout_bindings, 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, VK_SHADER_STAGE_FRAGMENT_BIT);
descriptor_set_finish(context, &layout_bindings, descriptor_set_handle);
descriptor_build_sets(context);
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));
descriptor_attach_buffer(context,
descriptor_set_handle,
&model_view_projection_buffer,
0,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
descriptor_attach_buffer(context,
STATUS_CHECK(descriptor_attach_buffer(context,
descriptor_set_handle,
&context->render_targets[RENDER_TARGET_3D].instance_buffer,
1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
0,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
descriptor_attach_images(context, descriptor_set_handle, textures, 2, context->vulkan_handles.sampler, 2);
STATUS_CHECK(descriptor_attach_images(context,
descriptor_set_handle,
textures,
TEXTURES_COUNT,
context->vulkan_handles.sampler,
1));
/* Initialize pipeline */
pipeline_builder_init(&pipeline);
@@ -215,12 +236,18 @@ rse_err_t rse_graphics_test_function(struct rse_graphics_context_t* rse_context)
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,
2,
VK_FORMAT_R32G32_SFLOAT,
offsetof(struct vertex_t, tex_coords));
pipeline_add_dynamic_state(&pipeline, VK_DYNAMIC_STATE_VIEWPORT);
pipeline_add_dynamic_state(&pipeline, VK_DYNAMIC_STATE_SCISSOR);
pipeline_add_push_constants(&pipeline, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(struct mvp_data_t));
pipeline_add_descriptor_sets(context, &pipeline, descriptor_set_handle);
pipeline_add(context, &pipeline, &pipeline_infos);
pipelines_build(context, &pipeline_infos);
@@ -232,21 +259,35 @@ rse_err_t rse_graphics_test_function(struct rse_graphics_context_t* rse_context)
return RSE_ERROR_NO_ERROR;
}
int rse_graphics_run(void* arg)
rse_err_t rse_graphics_run(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct rse_graphics_context_t* rse_context = (struct rse_graphics_context_t*)arg;
struct graphics_context_t* context = rse_context->context;
STATUS_CHECK(vulkan_run(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);
window_terminate(context->window_handle);
rse_free(context->debug_overlay.pixels);
rse_free(context);
rse_free(rse_context);
return status;
}
+1 -1
View File
@@ -23,7 +23,7 @@
#pragma GCC diagnostic ignored "-Wswitch"
#pragma GCC diagnostic ignored "-Wparentheses"
#endif
#include "vma/vk_mem_alloc.h"
#include "vk_mem_alloc.h"
#ifdef __unix__
#pragma GCC diagnostic pop
#endif
+83 -51
View File
@@ -15,25 +15,23 @@
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_vulkan.h>
#include <vma/vk_mem_alloc.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <vk_mem_alloc.h>
#include "pipeline_builder.h"
#include "src/descriptor_builder.h"
#include "src/graphics_context.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_commons.h"
#include "vulkan_commands.h"
#include "vulkan_buffers.h"
#include "vulkan_commands.h"
#include "vulkan_commons.h"
#include "vulkan_image.h"
#include "vulkan_render_pass.h"
#include "vulkan_swapchain.h"
#include "pipeline_builder.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define APPLICATION_NAME "RedScarfEngine PoC"
#define ENGINE_NAME "RedScarf Engine"
@@ -49,7 +47,8 @@
*/
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT message_type,
const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data)
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
void* user_data)
{
(void)message_severity;
(void)message_type;
@@ -80,7 +79,8 @@ 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,
static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* create_info,
const VkAllocationCallbacks* allocator,
VkDebugUtilsMessengerEXT* debug_messenger)
{
@@ -108,10 +108,9 @@ static rse_err_t setup_debug_messenger(VkInstance instance, VkDebugUtilsMessenge
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
create_info.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;
@@ -134,7 +133,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,
static void DestroyDebugUtilsMessengerEXT(VkInstance instance,
VkDebugUtilsMessengerEXT debug_messenger,
const VkAllocationCallbacks* allocator)
{
PFN_vkDestroyDebugUtilsMessengerEXT func =
@@ -211,14 +211,14 @@ static rse_err_t create_instance(VkInstance* instance)
/* 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; j < 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);
if (supported == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Selected layer is not supported: %s", enabled_instance_layers_names[i]);
return RSE_ERROR_INTERNAL_ERROR;
}
supported = 0;
@@ -226,14 +226,16 @@ static rse_err_t create_instance(VkInstance* instance)
/* Check if selected extensions are supported*/
for (i = 0; i < enabled_instance_extensions_count; ++i) {
for(j = 0; j < extensionCount; ++j) {
if(strcmp(extensions_properties[j].extensionName, enabled_instance_extensions_names[i]) == 0) {
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);
if (supported == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU,
"Selected extension is not supported: %s",
extensions_properties[j].extensionName);
return RSE_ERROR_INTERNAL_ERROR;
}
supported = 0;
@@ -293,7 +295,8 @@ 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 */
@@ -378,9 +381,12 @@ static rse_err_t create_device(struct graphics_context_t* context)
}
/* Get information about supported queue families */
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device, &queue_families_property_count, NULL);
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device,
&queue_families_property_count,
NULL);
rse_malloc(queue_family_properties, sizeof(VkQueueFamilyProperties) * queue_families_property_count);
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device, &queue_families_property_count,
vkGetPhysicalDeviceQueueFamilyProperties(context->vulkan_handles.physical_device,
&queue_families_property_count,
queue_family_properties);
/* Get queue families that support graphics operations AND have most
@@ -401,7 +407,10 @@ 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->vulkan_handles.physical_device,
familyIdx,
context->vulkan_handles.surface,
&presentSupport);
if (presentSupport) {
presentation_familiy_idx = familyIdx;
presentation_familiy_found = true;
@@ -426,16 +435,17 @@ static rse_err_t create_device(struct graphics_context_t* context)
context->queue_family_indices[QUEUE_FAMILY_INDEX_GRAPHICS] = graphics_family_idx;
context->queue_family_indices[QUEUE_FAMILY_INDEX_PRESENTATION] = presentation_familiy_idx;
rse_malloc(graphics_familiy_queue_priorities, sizeof(float) * queue_family_properties[graphics_family_idx].queueCount);
rse_malloc(graphics_familiy_queue_priorities,
sizeof(float) * queue_family_properties[graphics_family_idx].queueCount);
for (size_t i = 0; i < queue_family_properties[graphics_family_idx].queueCount; i++) {
graphics_familiy_queue_priorities[i] = 1.0f;
}
rse_malloc(presentation_familiy_queue_priorities, sizeof(float) * queue_family_properties[presentation_familiy_idx].queueCount);
rse_malloc(presentation_familiy_queue_priorities,
sizeof(float) * queue_family_properties[presentation_familiy_idx].queueCount);
for (size_t i = 0; i < queue_family_properties[presentation_familiy_idx].queueCount; i++) {
presentation_familiy_queue_priorities[i] =
1.0f;
presentation_familiy_queue_priorities[i] = 1.0f;
}
device_queue_createinfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
@@ -463,13 +473,19 @@ static rse_err_t create_device(struct graphics_context_t* context)
device_create_info.ppEnabledExtensionNames = enabled_device_extensions_names;
device_create_info.pEnabledFeatures = NULL;
if (VK_SUCCESS != vkCreateDevice(context->vulkan_handles.physical_device, &device_create_info, NULL, &context->vulkan_handles.device)) {
if (VK_SUCCESS != vkCreateDevice(context->vulkan_handles.physical_device,
&device_create_info,
NULL,
&context->vulkan_handles.device)) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create logical device");
return RSE_ERROR_INTERNAL_ERROR;
}
vkGetDeviceQueue(context->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->vulkan_handles.device,
presentation_familiy_idx,
0,
&context->vulkan_handles.present_queue);
rse_free(presentation_familiy_queue_priorities);
rse_free(graphics_familiy_queue_priorities);
@@ -505,7 +521,6 @@ static rse_err_t create_memory_allocator(struct graphics_context_t* context)
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create a Sync Objects
*
@@ -522,9 +537,18 @@ static rse_err_t create_sync_objects(struct graphics_context_t* context)
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]) ||
VK_SUCCESS != vkCreateFence(context->vulkan_handles.device, &fenceInfo, NULL, &context->vulkan_handles.in_flight_fences[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]) ||
VK_SUCCESS != vkCreateFence(context->vulkan_handles.device,
&fenceInfo,
NULL,
&context->vulkan_handles.in_flight_fences[i])) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create sync objects.");
return RSE_ERROR_INTERNAL_ERROR;
}
@@ -533,14 +557,14 @@ static rse_err_t create_sync_objects(struct graphics_context_t* context)
return RSE_ERROR_NO_ERROR;
}
rse_err_t vulkan_init(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(
create_surface(context->vulkan_handles.instance, context->window_handle, &context->vulkan_handles.surface));
STATUS_CHECK(pick_physical_device(context->vulkan_handles.instance, &context->vulkan_handles.physical_device));
STATUS_CHECK(create_device(context));
STATUS_CHECK(create_memory_allocator(context));
@@ -559,12 +583,10 @@ rse_err_t vulkan_run(struct graphics_context_t* context)
STATUS_CHECK(create_sync_objects(context));
init_time();
return status;
}
rse_err_t draw_frame(struct graphics_context_t* context)
rse_err_t vulkan_draw_frame(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
uint32_t image_index = 0U;
@@ -576,14 +598,20 @@ rse_err_t draw_frame(struct graphics_context_t* context)
VkPresentInfoKHR present_info = {0};
VkSwapchainKHR swap_chains[1];
vkWaitForFences(context->vulkan_handles.device, 1, &context->vulkan_handles.in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
vkWaitForFences(context->vulkan_handles.device,
1,
&context->vulkan_handles.in_flight_fences[context->current_frame],
VK_TRUE,
UINT64_MAX);
STATUS_CHECK(buffers_update(context));
update_time();
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->vulkan_handles.device,
context->swapchain_data.swapchain,
UINT64_MAX,
context->vulkan_handles.image_available_semaphores[context->current_frame],
VK_NULL_HANDLE,
&image_index);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreate_swapchain(context);
@@ -612,11 +640,14 @@ rse_err_t draw_frame(struct graphics_context_t* context)
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &context->vulkan_handles.command_buffers[context->current_frame];
signal_semaphores[0] = context->vulkan_handles.render_finished_semaphores[context->current_frame];
signal_semaphores[0] = context->vulkan_handles.render_finished_semaphores[image_index];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores;
if (vkQueueSubmit(context->vulkan_handles.graphics_queue, 1, &submit_info, context->vulkan_handles.in_flight_fences[context->current_frame]) != VK_SUCCESS) {
if (vkQueueSubmit(context->vulkan_handles.graphics_queue,
1,
&submit_info,
context->vulkan_handles.in_flight_fences[context->current_frame]) != VK_SUCCESS) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to submit draw command buffer!");
return RSE_ERROR_INTERNAL_ERROR;
}
@@ -624,7 +655,7 @@ rse_err_t draw_frame(struct graphics_context_t* context)
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &context->vulkan_handles.render_finished_semaphores[context->current_frame];
present_info.pWaitSemaphores = &context->vulkan_handles.render_finished_semaphores[image_index];
swap_chains[0] = context->swapchain_data.swapchain;
present_info.swapchainCount = 1;
@@ -649,6 +680,7 @@ rse_err_t draw_frame(struct graphics_context_t* context)
void vulkan_deinit(struct graphics_context_t* context)
{
vkDeviceWaitIdle(context->vulkan_handles.device);
destroy_buffers(context);
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
vkDestroySemaphore(context->vulkan_handles.device, context->vulkan_handles.image_available_semaphores[i], NULL);
+1 -1
View File
@@ -46,6 +46,6 @@ void vulkan_deinit(struct graphics_context_t* context);
* @param context Graphics context
* @return rse_err_t
*/
rse_err_t draw_frame(struct graphics_context_t* context);
rse_err_t vulkan_draw_frame(struct graphics_context_t* context);
#endif /* GRAPHICS_VULKANBASE_H */
+20 -7
View File
@@ -119,7 +119,12 @@ static rse_err_t create_instance_buffers(struct graphics_context_t* context)
buffer_size = sizeof(struct instance_data_t) * MAX_INSTANCE_NUMBER;
STATUS_CHECK(
buffer_create_uniform(context, &context->render_targets[RENDER_TARGET_3D].instance_buffer, buffer_size));
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,
@@ -205,8 +210,11 @@ rse_err_t add_vertices(struct graphics_context_t* context,
size_t vertices_count,
const struct vertex_t* vertices,
size_t indices_count,
const uint16_t* indices)
uint16_t* indices)
{
// static uint16_t last_index = 0U;
// uint16_t max_index = 0U;
// size_t idx = 0U;
rse_err_t status = RSE_ERROR_NO_ERROR;
struct vulkan_buffer_t staging_buffer;
size_t vertices_size = sizeof(vertices[0]) * vertices_count;
@@ -230,6 +238,7 @@ rse_err_t add_vertices(struct graphics_context_t* context,
copy_buffer(context, staging_buffer.buffer, vertex_buffer->buffer, vertices_size, vertex_buffer->allocated_size);
vertex_buffer->allocated_size += vertices_size;
/* Fill staging buffer with index data */
memcpy(staging_buffer.allocation_info.pMappedData, indices, indices_size);
copy_buffer(context, staging_buffer.buffer, index_buffer->buffer, indices_size, index_buffer->allocated_size);
@@ -269,6 +278,7 @@ rse_err_t buffers_update(struct graphics_context_t* context)
sizeof(VkDrawIndexedIndirectCommand) * context->mesh_data.mesh_count;
struct vulkan_buffer_t staging_buffer;
struct mesh_t* mesh = NULL;
uint32_t indices_count = 0U;
rse_malloc(draw_indirect_commands_buffer, draw_indirect_commands_buffer_size);
for (iter = 0U; iter < context->mesh_data.mesh_count; ++iter) {
@@ -290,13 +300,14 @@ rse_err_t buffers_update(struct graphics_context_t* context)
draw_indirect_command->firstInstance = 0;
draw_indirect_command->vertexOffset = 0;
} else {
draw_indirect_command->firstIndex = context->mesh_data.mesh_bucket[iter - 1].index_count;
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_command->vertexOffset = (context->mesh_data.mesh_bucket[iter - 1].vertex_count + draw_indirect_commands_buffer[iter-1].vertexOffset);
}
draw_indirect_command->indexCount = mesh->index_count;
draw_indirect_command->instanceCount = mesh->instances_count;
indices_count += mesh->index_count;
}
/* Copy Draw indirect commands into buffer */
@@ -435,10 +446,12 @@ void destroy_buffers(struct graphics_context_t* context)
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].vertex_buffer);
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].index_buffer);
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].draw_indirect_command_buffer);
destroy_buffer(context, &context->render_targets[RENDER_TARGET_3D].instance_buffer);
for (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 (iter = 0U; iter < context->uniform_buffers.vulkan_buffers_count; ++iter) {
vmaDestroyBuffer(context->vulkan_handles.allocator,
context->uniform_buffers.vulkan_buffers[iter].buffer,
context->uniform_buffers.vulkan_buffers[iter].allocation);
}
context->uniform_buffers.vulkan_buffers_count = 0;
+2 -2
View File
@@ -2,7 +2,7 @@
#define VULKAN_BUFFERS_H
#include <stdint.h>
#include <vma/vk_mem_alloc.h>
#include <vk_mem_alloc.h>
#include "graphics_context.h"
#include "utilities/commons.h"
@@ -36,7 +36,7 @@ rse_err_t buffer_create_uniform(struct graphics_context_t* context, struct vulka
* @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, const uint16_t* indices);
const struct vertex_t* vertices, size_t indices_count, uint16_t* indices);
/**
* @brief Add instance data to vulkan buffers
+1 -1
View File
@@ -12,7 +12,7 @@
#ifndef VULKAN_COMMANDS_H
#define VULKAN_COMMANDS_H
#include <vma/vk_mem_alloc.h>
#include <vk_mem_alloc.h>
#include <vulkan/vulkan_core.h>
#include "utilities/commons.h"
-24
View File
@@ -1,24 +0,0 @@
#include "vulkan_commons.h"
#include <time.h>
time_t g_last_frame_time;
time_t g_this_frame_time;
float g_float_delta_time;
void init_time(void)
{
time(&g_last_frame_time);
}
void update_time(void)
{
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(void)
{
return g_float_delta_time;
}
+2 -6
View File
@@ -14,7 +14,7 @@
#include <cglm/types.h>
#include <vulkan/vulkan.h>
#include <vma/vk_mem_alloc.h>
#include <vk_mem_alloc.h>
#include <cglm/cglm.h>
#include <stdalign.h>
@@ -44,7 +44,7 @@ struct vulkan_buffer_t
VmaAllocationInfo allocation_info;
};
struct uniform_buffer_object_t
struct mvp_data_t
{
alignas(16) mat4 model;
alignas(16) mat4 view;
@@ -58,8 +58,4 @@ struct instance_data_t {
alignas(4) uint32_t texture_id;
};
void init_time(void);
void update_time(void);
float get_time_diff(void);
#endif /* VULKAN_GLOBAL_H */
+66 -21
View File
@@ -3,6 +3,7 @@
#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"
@@ -15,6 +16,9 @@
#include <stdlib.h>
#include <string.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define IMAGE_TAKEN 1U
#define IMAGE_FREE 0U
@@ -168,16 +172,14 @@ rse_err_t create_color_resource(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkFormat color_format = IMAGE_FORMAT;
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
VkExtent2D extent = {0};
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->vulkan_handles.physical_device,
context->vulkan_handles.surface,
&physical_device_surface_capabilities);
STATUS_CHECK(image_get_extent(context, &extent));
STATUS_CHECK(create_image(context,
&context->color_image,
physical_device_surface_capabilities.currentExtent.width,
physical_device_surface_capabilities.currentExtent.height,
extent.width,
extent.height,
color_format,
VK_SAMPLE_COUNT_8_BIT,
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
@@ -191,17 +193,16 @@ rse_err_t create_depth_resources(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
VkFormat depth_format = {0};
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
VkExtent2D extent = {0};
STATUS_CHECK(image_get_extent(context, &extent));
STATUS_CHECK(find_depth_format(context, &depth_format));
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->vulkan_handles.physical_device,
context->vulkan_handles.surface,
&physical_device_surface_capabilities);
STATUS_CHECK(create_image(context,
&context->depth_image,
physical_device_surface_capabilities.currentExtent.width,
physical_device_surface_capabilities.currentExtent.height,
extent.width,
extent.height,
depth_format,
VK_SAMPLE_COUNT_8_BIT,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
@@ -407,6 +408,33 @@ 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,
@@ -490,20 +518,19 @@ rse_err_t load_texture_from_bitmat(struct graphics_context_t* context,
return status;
}
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_load_from_buffer(struct graphics_context_t* context, const unsigned char* data_buffer, size_t buffer_size, rse_id_t* texture_id)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
int width = 0;
int height = 0;
size_t buffer_size = 0U;
int channels = 0;
unsigned char* pixel_buffer = NULL;
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(file_load_pixels(file_path, NULL, &buffer_size, &width, &height));
rse_malloc(pixel_buffer, buffer_size);
if (file_load_pixels(file_path, pixel_buffer, &buffer_size, &width, &height) != RSE_ERROR_NO_ERROR) {
status = RSE_ERROR_INTERNAL_ERROR;
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");
goto mem_free;
return RSE_ERROR_INTERNAL_ERROR;
}
if (load_texture_from_bitmat(context, pixel_buffer, width, height, texture_id, VK_FORMAT_R8G8B8A8_SRGB) != RSE_ERROR_NO_ERROR) {
@@ -513,8 +540,26 @@ rse_err_t texture_load_from_file(struct graphics_context_t* context, const char*
}
mem_free:
stbi_image_free(pixel_buffer);
rse_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;
}
+3
View File
@@ -29,6 +29,8 @@
*/
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,6 +42,7 @@ rse_err_t init_vulkan_images(struct graphics_context_t* context);
rse_err_t load_texture_from_bitmat(struct graphics_context_t* context, const unsigned char* pixel_buffer, int width,
int height, rse_id_t* texture_id, VkFormat color_format);
rse_err_t texture_load_from_buffer(struct graphics_context_t* context, const unsigned char* data_buffer, size_t buffer_size, rse_id_t* texture_id);
/**
* @brief Loads image from file, for later to be used as a texture
*
+6 -2
View File
@@ -21,14 +21,18 @@ 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));
/* Store extent in global variable, for later use */
context->swapchain_data.swapchain_extent = physical_device_surface_capabilities.currentExtent;
context->swapchain_data.swapchain_extent = extent;
/* Check if device supports surface for presentation */
vkGetPhysicalDeviceSurfaceSupportKHR(context->vulkan_handles.physical_device,
@@ -52,7 +56,7 @@ static rse_err_t create_swapchain(struct graphics_context_t* context)
create_info.pQueueFamilyIndices = context->queue_family_indices;
create_info.preTransform = physical_device_surface_capabilities.currentTransform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
create_info.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
create_info.clipped = VK_TRUE;
create_info.oldSwapchain = VK_NULL_HANDLE;
+4 -25
View File
@@ -18,46 +18,25 @@
#include <stdint.h>
#include <stdio.h>
#include "SDL3/SDL_log.h"
#include "utilities/commons.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)
{
SDL_Init(SDL_INIT_VIDEO); // TODO: Move to initial phase of RSE initialization
(void)is_framebuffer_resized;; // TODO: Actually use this parameter?
(void)is_framebuffer_resized;
// TODO: Actually use this parameter?
*window_handle = SDL_CreateWindow("RedScarfEngine", 1024, 768, SDL_WINDOW_VULKAN);
if (NULL == *window_handle) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Window context creation failed");
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Window context creation failed. Reason: %s", SDL_GetError());
return RSE_ERROR_INTERNAL_ERROR;
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t window_loop(struct graphics_context_t* context)
{
uint8_t done = 0;
SDL_Event event;
while (0 == done) {
while (SDL_PollEvent(&event)) {
if (SDL_EVENT_QUIT == event.type) {
done = 1;
}
}
draw_frame(context);
}
vkDeviceWaitIdle(context->vulkan_handles.device);
return RSE_ERROR_NO_ERROR;
}
void window_terminate(SDL_Window* window_handle)
{
SDL_DestroyWindow(window_handle);
-8
View File
@@ -32,14 +32,6 @@
*/
rse_err_t window_init(SDL_Window **window_handle, bool *is_framebuffer_resized);
/**
* @brief Main window loop
*
* @param context Graphics context
* @return rse_err_t WINDOW_SUCCESS or error code
*/
rse_err_t window_loop(struct graphics_context_t* context);
/**
* @brief Closes window and terminates GLFW
*
+4
View File
@@ -6,7 +6,11 @@ project('RedScarfEngine',
'werror=true'
])
add_project_arguments('-DWAYLAND=1', language : 'c')
subdir('utilities')
subdir('graphics')
subdir('events')
subdir('red_scarf_engine')
-10
View File
@@ -1,10 +0,0 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror")
add_executable(${PROJECT_NAME} src/main.c)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE
graphics
utilities)
+6 -1
View File
@@ -1,4 +1,5 @@
red_scarf_engine_srcs = [
'src/red_scarf_engine.c',
'src/main.c'
]
@@ -13,6 +14,10 @@ executable(
],
dependencies : [
sdl3_dep,
rse_graphics_lib_dep,
],
link_with: [
rse_events_lib,
rse_utilities_lib,
],
link_with: rse_graphics_lib,
)
+22
View File
@@ -0,0 +1,22 @@
/**
* @file red_scarf_engine.h
* @author Piotr Krygier (piotrkrygier@everyonencancode.xyz)
* @brief Red Scarf Engine main entry point
* @version 0.1
* @date 2026-03-03
*
* @copyright Copyright (c) 2026
*
*/
#ifndef RED_SCARF_ENGINE_H_
#define RED_SCARF_ENGINE_H_
#include "utilities/commons.h"
typedef struct rse_context_t* rse_context;
rse_err_t rse_init(rse_context* context);
rse_err_t rse_run(rse_context context);
#endif /* RED_SCARF_ENGINE_H_ */
+5 -20
View File
@@ -1,31 +1,16 @@
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_log.h>
#include "graphics/rse_graphics.h"
#include "red_scarf_engine.h"
#include "utilities/errors_common.h"
#include "SDL3/SDL_thread.h"
int main(int argc, char** argv)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct rse_graphics_context_t* context = NULL;
SDL_Thread* graphics_task = NULL;
rse_context context;
(void)argc;
(void)argv;
SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
STATUS_CHECK(rse_graphics_init(&context));
STATUS_CHECK(rse_graphics_test_function(context));
graphics_task = SDL_CreateThread(rse_graphics_run, "graphics_task", (void*) context);
if (NULL == graphics_task) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to create graphics task");
exit(1);
}
SDL_WaitThread(graphics_task, NULL);
STATUS_CHECK(rse_init(&context));
STATUS_CHECK(rse_run(context));
return 0;
}
+71
View File
@@ -0,0 +1,71 @@
#include "red_scarf_engine.h"
#include "graphics/rse_graphics.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/time_utils.h"
#include "events/rse_events_manager.h"
struct rse_context_t {
struct graphics_context_t graphics_context;
struct events_context_t events_context;
};
static void fps_print(double* start_time)
{
static uint32_t frame_counts = 0U;
double now = time_get_sec_from_start();
frame_counts++;
if ((now - *start_time) > 1.0) {
printf("\rFPS: %u", frame_counts);
fflush(stdout);
frame_counts = 0;
*start_time += 1.0;
}
}
rse_err_t rse_init(rse_context* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct rse_context_t* rse_context = NULL;
rse_malloc(*context, sizeof(struct rse_context_t));
rse_context = *context;
SDL_Init(SDL_INIT_VIDEO);
SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
STATUS_CHECK(time_init());
STATUS_CHECK(rse_events_init(&rse_context->events_context));
STATUS_CHECK(rse_graphics_init(&rse_context->graphics_context));
return RSE_ERROR_NO_ERROR;
}
rse_err_t rse_run(rse_context context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
double start_time;
start_time = time_get_sec_from_start();
STATUS_CHECK(rse_graphics_test_function(&context->graphics_context));
STATUS_CHECK(rse_graphics_run(&context->graphics_context));
while(true) {
time_update();
rse_events_main_loop(&context->events_context);
if (context->events_context.should_stop == EVENT_SHOULD_STOP_ENGINE_TRUE) {
break;
}
rse_graphics_main_loop(&context->graphics_context);
fps_print(&start_time);
}
rse_graphics_deinit(&context->graphics_context);
rse_free(context);
return status;
}
+10
View File
@@ -0,0 +1,10 @@
[wrap-file]
directory = cglm-0.9.6
source_url = https://github.com/recp/cglm/archive/refs/tags/v0.9.6.tar.gz
source_filename = cglm-0.9.6.tar.gz
source_hash = be5e7d384561eb0fca59724a92b7fb44bf03e588a7eae5123a7d796002928184
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/cglm_0.9.6-1/cglm-0.9.6.tar.gz
wrapdb_version = 0.9.6-1
[provide]
cglm = cglm_dep
+7
View File
@@ -0,0 +1,7 @@
project('stb', 'c')
stb_inc = include_directories('.')
stb_dep = declare_dependency(
include_directories: stb_inc,
)
@@ -0,0 +1,15 @@
project(
'vulkan-headers',
'c',
license: 'Apache-2.0',
version: '1.4.353',
meson_version: '>=0.56.0',
)
vulkan_api_xml = files('registry/vk.xml')
vulkan_headers_dep = declare_dependency(
include_directories: include_directories('include'),
)
meson.override_dependency('VulkanHeaders', vulkan_headers_dep)
+5
View File
@@ -0,0 +1,5 @@
[wrap-git]
url = https://github.com/nothings/stb.git
revision = HEAD
depth = 1
patch_directory = stb/
+9
View File
@@ -0,0 +1,9 @@
[wrap-file]
directory = Vulkan-Headers-1.4.353
source_url = https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.4.353.tar.gz
source_filename = vulkan-headers-1.4.353.tar.gz
source_hash = aead572dd86def896eddac84cb31acb4ede28e3ac1ab97439d6ddd1b5a14d38e
patch_directory = vulkan_headers/
[provide]
dependency_names = VulkanHeaders
+14
View File
@@ -0,0 +1,14 @@
[wrap-file]
directory = VulkanMemoryAllocator-3.3.0
source_url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/archive/refs/tags/v3.3.0.tar.gz
source_filename = VulkanMemoryAllocator-3.3.0.tar.gz
source_hash = c4f6bbe6b5a45c2eb610ca9d231158e313086d5b1a40c9922cb42b597419b14e
source_fallback_url = https://wrapdb.mesonbuild.com/v2/vulkan-memory-allocator_3.3.0-1/get_source/VulkanMemoryAllocator-3.3.0.tar.gz
patch_filename = vulkan-memory-allocator_3.3.0-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/vulkan-memory-allocator_3.3.0-1/get_patch
patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/vulkan-memory-allocator_3.3.0-1/vulkan-memory-allocator_3.3.0-1_patch.zip
patch_hash = 85f3c1c82f3d1938fa295c80b06fc1c5c001f939f009b566c336a941d5d2d8b5
wrapdb_version = 3.3.0-1
[provide]
dependency_names = VulkanMemoryAllocator
+7240
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -22,13 +22,13 @@
*/
#ifndef RSE_TEST
#define rse_malloc(ptr, size) \
ptr = malloc(size); \
ptr = calloc(1, size); \
if (ptr == NULL) { \
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Memory allocation failure"); \
exit(1); \
}
#define rse_free(ptr) free(ptr);
#define rse_free(ptr) if (ptr != NULL) free(ptr);
#define rse_memcpy(dst, src, size) memcpy(dst, src, size);
#define rse_memset(ptr, value, size) memset(ptr, value, size);
+4 -3
View File
@@ -3,6 +3,7 @@
#include <stddef.h>
#include "commons.h"
#include "cgltf.h"
#define MAX_PATH_LENGTH (4096U)
@@ -14,13 +15,13 @@
* @param bytes_count Returned bytes count
* @param buffer Returned filled buffer
*/
rse_err_t file_read(const char* file_path, size_t* bytes_count, char* buffer);
rse_err_t file_read_bytes(const char* file_path, size_t* bytes_count, unsigned char* buffer);
rse_err_t file_write(const char* file_name, size_t bytes_count, char* buffer);
rse_err_t file_load_pixels(const char* file_name, unsigned char* buffer, size_t* buffer_size, int* width, int* height);
rse_err_t file_append_full_path(char* file_path, size_t max_buffer_size);
rse_err_t file_load_gltf(const char* file_name, cgltf_data** data);
#endif /* FILE_UTILS_H */
+6 -4
View File
@@ -1,13 +1,15 @@
rse_utilities_srcs = [
'src/file_utils.c'
'src/file_utils.c',
'src/time_utils.c'
]
cc = meson.get_compiler('c')
sdl3_dep = dependency('sdl3',
version : '>=3.4.0')
stb_dep = dependency('stb',
version : '>=2.30.0')
stb = dependency('stb',
fallback: ['stb', 'stb_dep'])
m_dep = cc.find_library('m')
rse_utilities_lib = shared_library(
@@ -16,7 +18,7 @@ rse_utilities_lib = shared_library(
include_directories : ['.'],
dependencies : [
sdl3_dep,
stb_dep,
stb,
m_dep,
],
install : true
+28 -69
View File
@@ -1,5 +1,10 @@
#include "file_utils.h"
#define CGLTF_IMPLEMENTATION
#include "cgltf.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -8,9 +13,6 @@
#include "commons.h"
#include "errors_common.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#ifdef __linux__
#include <limits.h>
#include <unistd.h>
@@ -113,70 +115,7 @@ rse_err_t file_append_full_path(char* file_path, size_t max_buffer_size)
return ret;
}
rse_err_t file_load_pixels(const char* file_name, unsigned char* buffer, size_t* buffer_size, int* width, int* height)
{
int channels = 0;
stbi_uc* pixel_buffer = NULL;
size_t image_buffer_size = 0U;
rse_err_t ret = RSE_ERROR_NO_ERROR;
char real_path[MAX_PATH_LENGTH] = {0};
if (buffer_size == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Provided NULL buffer_size");
return RSE_ERROR_INVALID_PARAM;
}
if (width == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Provided NULL width");
return RSE_ERROR_INVALID_PARAM;
}
if (height == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Provided NULL height");
return RSE_ERROR_INVALID_PARAM;
}
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;
}
pixel_buffer = stbi_load(real_path, width, height, &channels, STBI_rgb_alpha);
if (pixel_buffer == NULL) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to load texture from file: %s", file_name);
return RSE_ERROR_INTERNAL_ERROR;
}
image_buffer_size = *width * *height * 4; // TODO: "4" is very hardcoded value. Change this
if ((buffer != NULL) && (image_buffer_size > *buffer_size)) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION,
"Provided buffer is to small to hold pixel data. %ld vs %ld",
*buffer_size,
image_buffer_size);
ret = RSE_ERROR_INTERNAL_ERROR;
goto image_free;
}
*buffer_size = image_buffer_size;
if (buffer != NULL) {
memset(buffer, 0, *buffer_size);
memcpy(buffer, pixel_buffer, *buffer_size);
}
image_free:
stbi_image_free(pixel_buffer);
return ret;
}
rse_err_t file_read(const char* file_name, size_t* bytes_count, char* buffer)
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;
@@ -193,7 +132,7 @@ rse_err_t file_read(const char* file_name, size_t* bytes_count, char* buffer)
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to open file for read: %s", real_path);
ret = RSE_ERROR_INTERNAL_ERROR;
goto file_close;
return ret;
}
fseek(file, 0, SEEK_END);
@@ -238,5 +177,25 @@ rse_err_t file_write(const char* file_name, size_t bytes_count, char* buffer)
fwrite(buffer, bytes_count, 1, file);
fclose(file);
return RSE_ERROR_NO_ERROR;
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;
}
+33
View File
@@ -0,0 +1,33 @@
#include "time_utils.h"
#include <stdint.h>
#include "SDL3/SDL_timer.h"
#include "commons.h"
#include "errors_common.h"
#define NS_TO_S(time) (time/1000000000)
double g_last_time = 0.0;
rse_err_t time_init(void)
{
g_last_time = NS_TO_S((double)SDL_GetTicksNS());
return RSE_ERROR_NO_ERROR;
}
double time_get_sec_from_start(void)
{
return NS_TO_S((double)SDL_GetTicksNS());
}
void time_update(void)
{
g_last_time = NS_TO_S((double)SDL_GetTicksNS());
}
double time_get_dt(void)
{
float time = NS_TO_S((double)SDL_GetTicksNS());
float dt = time - g_last_time;
return dt;
}
+24
View File
@@ -0,0 +1,24 @@
/**
* @file file_utils.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Common functionality for all modules
* @version 0.1
* @date 2026-02-11
*
* @copyright Copyright (c) 2023
*
*/
#ifndef TIME_UTILS_H_
#define TIME_UTILS_H_
#include "commons.h"
rse_err_t time_init(void);
void time_update(void);
double time_get_sec_from_start(void);
double time_get_dt(void);
#endif // !TIME_UTILS_H_