Implement resource compression
This commit is contained in:
		
							parent
							
								
									3a9f9d4986
								
							
						
					
					
						commit
						cfec746545
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -2,9 +2,7 @@
 | 
			
		||||
/subprojects/*
 | 
			
		||||
!/subprojects/*.wrap
 | 
			
		||||
 | 
			
		||||
# assetc directories
 | 
			
		||||
/actemp/*
 | 
			
		||||
/data/*
 | 
			
		||||
/res/*
 | 
			
		||||
 | 
			
		||||
/.cache/*
 | 
			
		||||
/.vs/*
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,17 @@ VSOutput VsMain(VSInput input, uint vertexIndex : SV_VertexID) {
 | 
			
		||||
 | 
			
		||||
fragment {
 | 
			
		||||
	vk BEGIN
 | 
			
		||||
struct PSOutput {
 | 
			
		||||
	float4 Color : SV_TARGET0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PSOutput PsMain(void) {
 | 
			
		||||
	PSOutput output = (PSOutput)0;
 | 
			
		||||
	output.Color[0] = 0;
 | 
			
		||||
	output.Color[1] = 0;
 | 
			
		||||
	output.Color[2] = 0;
 | 
			
		||||
	output.Color[3] = 0;
 | 
			
		||||
	return output;
 | 
			
		||||
}
 | 
			
		||||
	END
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								meson.build
									
									
									
									
									
								
							@ -2,9 +2,13 @@ project('voyage', ['c', 'cpp'],
 | 
			
		||||
  default_options: ['buildtype=debug', 
 | 
			
		||||
    'b_sanitize=address', 
 | 
			
		||||
    'c_std=c17', 
 | 
			
		||||
    'cpp_std=c++14',
 | 
			
		||||
    'warning_level=3',
 | 
			
		||||
    'werror=true',
 | 
			
		||||
    'b_vscrt=static_from_buildtype'])
 | 
			
		||||
    'b_vscrt=static_from_buildtype',
 | 
			
		||||
    'default_library=static',
 | 
			
		||||
    'b_rtti=false',
 | 
			
		||||
    'cpp_eh=none'])
 | 
			
		||||
 | 
			
		||||
compiler = meson.get_compiler('c')
 | 
			
		||||
buildtype = get_option('buildtype')
 | 
			
		||||
@ -16,13 +20,11 @@ if compiler.get_argument_syntax() == 'gcc'
 | 
			
		||||
      '-Wdouble-promotion', '-Wno-unused-function', '-Wno-unused-parameter'],
 | 
			
		||||
    language : ['c', 'cpp']
 | 
			
		||||
    )
 | 
			
		||||
  add_project_arguments(['-fno-exceptions', '-fno-rtti'], language : 'cpp')
 | 
			
		||||
elif compiler.get_argument_syntax() == 'msvc'
 | 
			
		||||
  add_project_arguments(
 | 
			
		||||
    ['/wd4146', '/wd4245', '/wd4100', '/D_CRT_SECURE_NO_WARNINGS'],
 | 
			
		||||
    language:  ['c', 'cpp']
 | 
			
		||||
    )
 | 
			
		||||
  add_project_arguments(['/EHsc', '/GR-'], language : 'cpp')
 | 
			
		||||
  if buildtype == 'debug'
 | 
			
		||||
    add_project_arguments(['/RTCsu'], language : ['c', 'cpp'])
 | 
			
		||||
  endif
 | 
			
		||||
@ -66,26 +68,6 @@ runtime_deps = [thread_dep, m_dep, windowing_dep]
 | 
			
		||||
if get_option('build_asset_compiler')
 | 
			
		||||
  runtime_cargs += ['-DRT_BUILD_ASSET_COMPILER']
 | 
			
		||||
 | 
			
		||||
  # Shaderc for shaders
 | 
			
		||||
  #if get_option('enable_vulkan_shader_compiler')
 | 
			
		||||
  #  shaderc_include = include_directories('contrib\\shaderc\\libshaderc\\include')
 | 
			
		||||
  #  shaderc_libdir = 'NONE'
 | 
			
		||||
  #  if host_machine.system() == 'windows'
 | 
			
		||||
  #    if buildtype == 'debug' or buildtype == 'debugoptimized'
 | 
			
		||||
  #      shaderc_libdir = meson.project_source_root() / 'contrib\\shaderc\\build-win\\libshaderc\\Debug'
 | 
			
		||||
  #    else
 | 
			
		||||
  #      shaderc_libdir = meson.project_source_root() / 'contrib\\shaderc\\build-win\\libshaderc\\Release'
 | 
			
		||||
  #    endif
 | 
			
		||||
  #  endif
 | 
			
		||||
  #  shaderc_dep = declare_dependency(link_args : ['-L'+shaderc_libdir, '-lshaderc_combined'],
 | 
			
		||||
  #    include_directories : shaderc_include)
 | 
			
		||||
  #  runtime_deps += shaderc_dep
 | 
			
		||||
  #  runtime_additional_sources += [
 | 
			
		||||
  #    'src/runtime/vulkan_shader_compiler.c'
 | 
			
		||||
  #  ]
 | 
			
		||||
  #  runtime_cargs += ['-DRT_BUILD_VULKAN_SHADER_COMPILER']
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  # DXC for vulkan & directx shaders
 | 
			
		||||
  if get_option('enable_dxc_shader_compiler')
 | 
			
		||||
    # We package dxc binaries under contrib/dxc
 | 
			
		||||
@ -125,9 +107,10 @@ runtime_lib = library('rt',
 | 
			
		||||
  # Project Sources
 | 
			
		||||
  'src/runtime/aio.h',
 | 
			
		||||
  'src/runtime/app.h',
 | 
			
		||||
  'src/runtime/assets.h',
 | 
			
		||||
  'src/runtime/buffer_manager.h',
 | 
			
		||||
  'src/runtime/compression.h',
 | 
			
		||||
  'src/runtime/config.h',
 | 
			
		||||
  'src/runtime/ds.h',
 | 
			
		||||
  'src/runtime/dynamic_libs.h',
 | 
			
		||||
  'src/runtime/file_tab.h',
 | 
			
		||||
  'src/runtime/fsutils.h',
 | 
			
		||||
@ -138,14 +121,17 @@ runtime_lib = library('rt',
 | 
			
		||||
  'src/runtime/mem_arena.h',
 | 
			
		||||
  'src/runtime/packages.h',
 | 
			
		||||
  'src/runtime/renderer_api.h',
 | 
			
		||||
  'src/runtime/resources.h',
 | 
			
		||||
  'src/runtime/runtime.h',
 | 
			
		||||
  'src/runtime/threading.h',
 | 
			
		||||
  
 | 
			
		||||
  'src/runtime/aio.c',
 | 
			
		||||
  'src/runtime/app.c',
 | 
			
		||||
  'src/runtime/asset_manager.c',
 | 
			
		||||
  'src/runtime/assert.c',
 | 
			
		||||
  'src/runtime/buffer_manager.c',
 | 
			
		||||
  'src/runtime/compression.c',
 | 
			
		||||
  'src/runtime/config.c',
 | 
			
		||||
  'src/runtime/ds_minheap.c',
 | 
			
		||||
  'src/runtime/dynamic_libs.c',
 | 
			
		||||
  'src/runtime/error_report.c',
 | 
			
		||||
  'src/runtime/file_tab.c',
 | 
			
		||||
@ -156,6 +142,8 @@ runtime_lib = library('rt',
 | 
			
		||||
  'src/runtime/jobs.c',
 | 
			
		||||
  'src/runtime/mem_arena.c',
 | 
			
		||||
  'src/runtime/packages.c',
 | 
			
		||||
  'src/runtime/resource_manager.c',
 | 
			
		||||
  'src/runtime/sprint.c',
 | 
			
		||||
  'src/runtime/text.c',
 | 
			
		||||
  'src/runtime/threading_cond.c',
 | 
			
		||||
  'src/runtime/threading_mutex.c',
 | 
			
		||||
@ -217,40 +205,6 @@ else
 | 
			
		||||
  engine_link_libs = [runtime_lib]
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Asset Compiler Tool
 | 
			
		||||
#executable('assetc',
 | 
			
		||||
#  'src/tools/assetc/assetmeta.h',
 | 
			
		||||
#  'src/tools/assetc/assetsettings.h',
 | 
			
		||||
#  'src/tools/assetc/dependency_tracking.h',
 | 
			
		||||
#  'src/tools/assetc/description_parser.h',
 | 
			
		||||
#  'src/tools/assetc/options.h',
 | 
			
		||||
#  'src/tools/assetc/packages.h',
 | 
			
		||||
#  'src/tools/assetc/processing.h',
 | 
			
		||||
#  'src/tools/assetc/processing_flags.h',
 | 
			
		||||
#  'src/tools/assetc/utils.h',
 | 
			
		||||
#
 | 
			
		||||
#  'src/tools/assetc/assetc.c',
 | 
			
		||||
#  'src/tools/assetc/assetmeta.c',
 | 
			
		||||
#  'src/tools/assetc/assetsettings.c',
 | 
			
		||||
#  'src/tools/assetc/dependency_tracking.c',
 | 
			
		||||
#  'src/tools/assetc/description_parser.c',
 | 
			
		||||
#  'src/tools/assetc/discovery.c',
 | 
			
		||||
#  'src/tools/assetc/packages.c',
 | 
			
		||||
#  'src/tools/assetc/pipeline_processor.c',
 | 
			
		||||
#  'src/tools/assetc/processor.c',
 | 
			
		||||
#  'src/tools/assetc/shader_processor.c',
 | 
			
		||||
#  'src/tools/assetc/uidtable.c',
 | 
			
		||||
#  'src/tools/assetc/utils.c',
 | 
			
		||||
#
 | 
			
		||||
#  # Contrib sources
 | 
			
		||||
#  'contrib/xxhash/xxhash.c',
 | 
			
		||||
#  'contrib/lz4/lz4.c',
 | 
			
		||||
#  include_directories : [incdir, shaderc_include],
 | 
			
		||||
#  dependencies : [],
 | 
			
		||||
#  link_with : engine_link_libs,
 | 
			
		||||
#  link_args : ['-L'+shaderc_libdir, '-lshaderc_combined'],
 | 
			
		||||
#  win_subsystem : 'console')
 | 
			
		||||
 | 
			
		||||
# Game
 | 
			
		||||
executable('voyage',
 | 
			
		||||
  'src/game/voyage.c',
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,8 @@
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#define NOMINMAX
 | 
			
		||||
#include <Windows.h>
 | 
			
		||||
#include <unknwn.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* C standard library*/
 | 
			
		||||
@ -8,3 +11,6 @@
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
/* Runtime */
 | 
			
		||||
#include "runtime/runtime.h"
 | 
			
		||||
@ -50,8 +50,8 @@ static rt_mutex *_guard;
 | 
			
		||||
static size_t _block_count;
 | 
			
		||||
 | 
			
		||||
RT_CVAR_I(rt_BufferMemoryBudget,
 | 
			
		||||
          "The amount of memory to allocate for the buffer manager. Default: 512MB",
 | 
			
		||||
          RT_MB(512));
 | 
			
		||||
          "The amount of memory to allocate for the buffer manager. Default: 1GB",
 | 
			
		||||
          RT_GB(1));
 | 
			
		||||
 | 
			
		||||
extern rt_result InitBufferManager(void) {
 | 
			
		||||
    _guard = rtCreateMutex();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								src/runtime/compression.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/runtime/compression.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
#include "compression.h"
 | 
			
		||||
 | 
			
		||||
#include <lz4/lz4.h>
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT size_t rtGetCompressionBound(size_t uncompressed_size) {
 | 
			
		||||
    return (size_t)LZ4_compressBound((int)uncompressed_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT size_t rtCompressData(const void *in,
 | 
			
		||||
                                   size_t uncompressed_size,
 | 
			
		||||
                                   void *out,
 | 
			
		||||
                                   size_t out_capacity) {
 | 
			
		||||
    return (size_t)LZ4_compress_default((const char *)in,
 | 
			
		||||
                                        (char *)out,
 | 
			
		||||
                                        (int)uncompressed_size,
 | 
			
		||||
                                        (int)out_capacity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT size_t rtDecompressData(const void *in,
 | 
			
		||||
                                     size_t compressed_size,
 | 
			
		||||
                                     void *out,
 | 
			
		||||
                                     size_t out_capacity) {
 | 
			
		||||
    int res =
 | 
			
		||||
        LZ4_decompress_safe((const char *)in, (char *)out, (int)compressed_size, (int)out_capacity);
 | 
			
		||||
    return (res > 0) ? (size_t)res : 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/runtime/compression.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/runtime/compression.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
#ifndef RT_COMPRESSION_H
 | 
			
		||||
#define RT_COMPRESSION_H
 | 
			
		||||
 | 
			
		||||
#include "runtime.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Returns the worst-case size of the compression result of a buffer with the given uncompressed
 | 
			
		||||
 * size. */
 | 
			
		||||
RT_DLLEXPORT size_t rtGetCompressionBound(size_t uncompressed_size);
 | 
			
		||||
 | 
			
		||||
/* Compresses a buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the number of bytes written to the output buffer, or 0 if compression failed
 | 
			
		||||
 * due to insufficient capacity.
 | 
			
		||||
 */
 | 
			
		||||
RT_DLLEXPORT size_t rtCompressData(const void *in,
 | 
			
		||||
                                   size_t uncompressed_size,
 | 
			
		||||
                                   void *out,
 | 
			
		||||
                                   size_t out_capacity);
 | 
			
		||||
 | 
			
		||||
/* Decompresses a buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the number of bytes written to the output buffer, or 0 if decompression failed
 | 
			
		||||
 * due to insufficient capacity.
 | 
			
		||||
 */
 | 
			
		||||
RT_DLLEXPORT size_t rtDecompressData(const void *in,
 | 
			
		||||
                                     size_t compressed_size,
 | 
			
		||||
                                     void *out,
 | 
			
		||||
                                     size_t out_capacity);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@ -47,6 +47,8 @@ typedef enum {
 | 
			
		||||
 | 
			
		||||
    RT_ATTRIBUTE_VALUE_MATERIAL_ALBEDO,
 | 
			
		||||
    RT_ATTRIBUTE_VALUE_MATERIAL_NORMAL,
 | 
			
		||||
 | 
			
		||||
    RT_ATTRIBUTE_VALUE_count
 | 
			
		||||
} rt_attribute_value;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,10 @@
 | 
			
		||||
#include "mem_arena.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
RT_CVAR_I(rt_TemporaryArenaSize, "Size of temporary arenas in bytes. Default: 32 MB", RT_MB(32));
 | 
			
		||||
 | 
			
		||||
#define ALIGNMENT 0xf
 | 
			
		||||
#define ALIGN(n)  (((n) + ALIGNMENT) & ~ALIGNMENT)
 | 
			
		||||
 | 
			
		||||
@ -59,3 +62,45 @@ RT_DLLEXPORT void rtReleaseArena(rt_arena *arena) {
 | 
			
		||||
    arena->size       = 0;
 | 
			
		||||
    arena->needs_free = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Temporary arena pool */
 | 
			
		||||
 | 
			
		||||
typedef uint32_t rt_thread_id;
 | 
			
		||||
extern RT_DLLEXPORT rt_thread_id rtGetCurrentThreadId(void);
 | 
			
		||||
 | 
			
		||||
#define NUM_TEMP_ARENAS_PER_THREAD 2
 | 
			
		||||
typedef struct {
 | 
			
		||||
    rt_arena arenas[NUM_TEMP_ARENAS_PER_THREAD];
 | 
			
		||||
} rt_thread_temp_arenas;
 | 
			
		||||
 | 
			
		||||
static RT_THREAD_LOCAL rt_thread_temp_arenas t_arenas;
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT rt_temp_arena rtGetTemporaryArena(const rt_arena **permanent_arenas, int count) {
 | 
			
		||||
    if (!t_arenas.arenas[0].base) {
 | 
			
		||||
        /* Initialize */
 | 
			
		||||
        for (int i = 0; i < NUM_TEMP_ARENAS_PER_THREAD; ++i) {
 | 
			
		||||
            rt_create_arena_result res = rtCreateArena(NULL, (size_t)rt_TemporaryArenaSize.i);
 | 
			
		||||
            if (!res.ok) {
 | 
			
		||||
                rtLog("CORE",
 | 
			
		||||
                      "Failed to initialize thread-local temporary arenas for thread: %u",
 | 
			
		||||
                      rtGetCurrentThreadId());
 | 
			
		||||
                return (rt_temp_arena){.arena = NULL, .at = 0};
 | 
			
		||||
            }
 | 
			
		||||
            t_arenas.arenas[i] = res.arena;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    for (int i = 0; i < NUM_TEMP_ARENAS_PER_THREAD; ++i) {
 | 
			
		||||
        int conflict_found = 0;
 | 
			
		||||
        for (int j = 0; j < count; ++j) {
 | 
			
		||||
            if (permanent_arenas[j] == &t_arenas.arenas[i])
 | 
			
		||||
                conflict_found = 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (!conflict_found) {
 | 
			
		||||
            return rtBeginTempArena(&t_arenas.arenas[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    rtLog("CORE",
 | 
			
		||||
          "Failed to find a usable thread-local temporary arena for thread: %u",
 | 
			
		||||
          rtGetCurrentThreadId());
 | 
			
		||||
    return (rt_temp_arena){.arena = NULL, .at = 0};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,8 @@ typedef struct {
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    size_t at;
 | 
			
		||||
} rt_arena_rewindpoint;
 | 
			
		||||
    rt_arena *arena;
 | 
			
		||||
} rt_temp_arena;
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT rt_create_arena_result rtCreateArena(void *memory, size_t size);
 | 
			
		||||
 | 
			
		||||
@ -37,22 +38,34 @@ RT_INLINE void rtArenaClear(rt_arena *arena) {
 | 
			
		||||
    arena->at = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_INLINE rt_arena_rewindpoint rtGetArenaRewindPoint(rt_arena *arena) {
 | 
			
		||||
RT_INLINE rt_temp_arena rtBeginTempArena(rt_arena *arena) {
 | 
			
		||||
#ifndef __cplusplus
 | 
			
		||||
    return (rt_arena_rewindpoint){.at = arena->at};
 | 
			
		||||
    return (rt_temp_arena){.at = arena->at, .arena = arena};
 | 
			
		||||
#else
 | 
			
		||||
    rt_arena_rewindpoint rp = {arena->at};
 | 
			
		||||
    rt_temp_arena rp = {arena->at, arena};
 | 
			
		||||
    return rp;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_INLINE void rtArenaRewind(rt_arena *arena, rt_arena_rewindpoint rewind) {
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
    if (rewind.at > arena->at)
 | 
			
		||||
        rtReportError("CORE",
 | 
			
		||||
                      "Tried to rewind an arena to a point that lies beyond the last allocation.");
 | 
			
		||||
#endif
 | 
			
		||||
    arena->at = rewind.at;
 | 
			
		||||
RT_INLINE void rtEndTempArena(rt_temp_arena tmp) {
 | 
			
		||||
    RT_ASSERT(tmp.at <= tmp.arena->at,
 | 
			
		||||
              "Tried to rewind an arena to a point that lies beyond the last allocation.");
 | 
			
		||||
    tmp.arena->at = tmp.at;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a thread-local arena for temporary allocations.
 | 
			
		||||
 *
 | 
			
		||||
 * Since it's possible that such an arena was passed into a function via parameters, which might 
 | 
			
		||||
 * lead to conflicts - such as a called function inadvertently freeing its own "permanent" allocations,
 | 
			
		||||
 * we pass in arenas that are used for allocations lasting beyond the usage of the temporary arena.
 | 
			
		||||
 *
 | 
			
		||||
 * DO NOT call rtReleaseArena on a arena returned by this function. Instead return them via
 | 
			
		||||
 * rtReturnTemporaryArena()!
 | 
			
		||||
 */
 | 
			
		||||
RT_DLLEXPORT rt_temp_arena rtGetTemporaryArena(const rt_arena **permanent_arenas, int count);
 | 
			
		||||
 | 
			
		||||
RT_INLINE void rtReturnTemporaryArena(rt_temp_arena tmp) {
 | 
			
		||||
    rtEndTempArena(tmp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Helper macros */
 | 
			
		||||
 | 
			
		||||
@ -39,31 +39,6 @@ enum {
 | 
			
		||||
 | 
			
		||||
extern rt_cvar rt_Renderer;
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static void
 | 
			
		||||
DbgPrintShaderFile(const rt_parse_state *state, unsigned int list_index, unsigned int indent) {
 | 
			
		||||
    assert(list_index < state->statement_list_count);
 | 
			
		||||
    const rt_parsed_stmt_list *list = &state->statement_lists[list_index];
 | 
			
		||||
 | 
			
		||||
    unsigned int stmt_index = list->first;
 | 
			
		||||
    for (unsigned int i = 0; i < list->count; ++i) {
 | 
			
		||||
        const rt_parsed_stmt *stmt = &state->statements[stmt_index];
 | 
			
		||||
        for (unsigned int j = 0; j < indent; ++j)
 | 
			
		||||
            printf(" ");
 | 
			
		||||
        printf("%.*s: ", stmt->attribute.length, stmt->attribute.start);
 | 
			
		||||
        if (stmt->form == RT_STMT_FORM_VALUE) {
 | 
			
		||||
            printf("%.*s\n", stmt->value.length, stmt->value.start);
 | 
			
		||||
        } else {
 | 
			
		||||
            printf("{\n");
 | 
			
		||||
            DbgPrintShaderFile(state, stmt->list_index, indent + 2);
 | 
			
		||||
            printf("}\n");
 | 
			
		||||
        }
 | 
			
		||||
        stmt_index = stmt->next;
 | 
			
		||||
    }
 | 
			
		||||
    assert(stmt_index == UINT_MAX || stmt_index == 0);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static bool ParseBindingIndex(rt_text_span span, unsigned int *index) {
 | 
			
		||||
    if (span.length == 0)
 | 
			
		||||
        return false;
 | 
			
		||||
@ -469,10 +444,8 @@ RT_ASSET_PROCESSOR_FN(PipelineProcessor) {
 | 
			
		||||
        goto out;
 | 
			
		||||
 | 
			
		||||
    rt_resource_id shader_resources[3] = {0};
 | 
			
		||||
    result                             = rtCreateResources(pipeline.shader_count,
 | 
			
		||||
                               pipeline.shader_names,
 | 
			
		||||
                               pipeline.shaders,
 | 
			
		||||
                               shader_resources);
 | 
			
		||||
    result = rtCreateResources(pipeline.shader_count, pipeline.shader_names, pipeline.shaders,
 | 
			
		||||
                                shader_resources);
 | 
			
		||||
    if (result != RT_SUCCESS)
 | 
			
		||||
        goto out;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -48,13 +48,15 @@ typedef enum {
 | 
			
		||||
    RT_SHADER_TYPE_INVALID,
 | 
			
		||||
    RT_SHADER_TYPE_VULKAN,
 | 
			
		||||
 | 
			
		||||
    RT_SHADER_TYPE_COUNT,
 | 
			
		||||
    RT_SHADER_TYPE_count,
 | 
			
		||||
} rt_shader_type;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    RT_SHADER_STAGE_VERTEX,
 | 
			
		||||
    RT_SHADER_STAGE_FRAGMENT,
 | 
			
		||||
    RT_SHADER_STAGE_COMPUTE,
 | 
			
		||||
 | 
			
		||||
    RT_SHADER_STAGE_count,
 | 
			
		||||
} rt_shader_stage;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,12 @@
 | 
			
		||||
#include "aio.h"
 | 
			
		||||
#include "buffer_manager.h"
 | 
			
		||||
#include "compression.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "ds.h"
 | 
			
		||||
#include "file_tab.h"
 | 
			
		||||
#include "fsutils.h"
 | 
			
		||||
#include "hashing.h"
 | 
			
		||||
#include "mem_arena.h"
 | 
			
		||||
#include "renderer_api.h"
 | 
			
		||||
#include "resources.h"
 | 
			
		||||
#include "threading.h"
 | 
			
		||||
@ -29,7 +31,8 @@ RT_CVAR_I(rt_ResourceNamespaceSize,
 | 
			
		||||
typedef struct {
 | 
			
		||||
    void *buffer;
 | 
			
		||||
    size_t size;
 | 
			
		||||
    int next_free;
 | 
			
		||||
    rt_aio_handle load_aio;
 | 
			
		||||
    unsigned int next_free;
 | 
			
		||||
    int usage_counter;
 | 
			
		||||
} rt_cached_resource;
 | 
			
		||||
 | 
			
		||||
@ -52,13 +55,15 @@ typedef struct {
 | 
			
		||||
 | 
			
		||||
    size_t current_size;
 | 
			
		||||
 | 
			
		||||
    rt_rwlock lock;
 | 
			
		||||
    rt_rwlock resource_lock;
 | 
			
		||||
    rt_mutex *heap_lock;
 | 
			
		||||
} rt_resource_cache;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    rt_file_id file;
 | 
			
		||||
    size_t offset;
 | 
			
		||||
    size_t size;
 | 
			
		||||
    size_t decompressed_size;
 | 
			
		||||
    size_t compressed_size;
 | 
			
		||||
} rt_resource_ref;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@ -102,6 +107,53 @@ static void CopyResourceData(const rt_resource *resource, void *dest) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static rt_resource_ref *GetResourceRefPtr(rt_resource_id id) {
 | 
			
		||||
    rt_resource_ref *ref = NULL;
 | 
			
		||||
    rtLockRead(&_namespace.lock);
 | 
			
		||||
    size_t ns_size = (size_t)rt_ResourceNamespaceSize.i;
 | 
			
		||||
    for (size_t j = 0; j < ns_size; ++j) {
 | 
			
		||||
        size_t at = (id + j) % ns_size;
 | 
			
		||||
        if (_namespace.ids[at] == RT_INVALID_RESOURCE_ID) {
 | 
			
		||||
            break;
 | 
			
		||||
        } else if (_namespace.ids[at] == id) {
 | 
			
		||||
            ref = &_namespace.refs[at];
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    rtUnlockRead(&_namespace.lock);
 | 
			
		||||
    return ref;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Fills the passed write struct with the necessary information to save the resource to a file */
 | 
			
		||||
static bool PrepareResourceFlushToFile(rt_resource_id id,
 | 
			
		||||
                                       const rt_resource *resource,
 | 
			
		||||
                                       rt_file_write *write,
 | 
			
		||||
                                       rt_arena *arena) {
 | 
			
		||||
 | 
			
		||||
    RT_ASSERT((uintptr_t)resource->data == (uintptr_t)(resource + 1),
 | 
			
		||||
              "The resource and data must be laid out in a continous buffer.");
 | 
			
		||||
 | 
			
		||||
    char file_path[260];
 | 
			
		||||
    rtSPrint(file_path, 260, "%s/%llx.bin", rt_ResourceDirectory.s, id);
 | 
			
		||||
 | 
			
		||||
    size_t total_size        = sizeof(rt_resource) + GetResourceDataSize(resource);
 | 
			
		||||
    size_t compression_bound = rtGetCompressionBound(total_size);
 | 
			
		||||
 | 
			
		||||
    void *compressed_resource = rtArenaPush(arena, compression_bound);
 | 
			
		||||
    if (!compressed_resource)
 | 
			
		||||
        return false;
 | 
			
		||||
    size_t compressed_bytes =
 | 
			
		||||
        rtCompressData(resource, total_size, compressed_resource, compression_bound);
 | 
			
		||||
 | 
			
		||||
    write->file      = rtAddFile(file_path);
 | 
			
		||||
    write->buffer    = compressed_resource;
 | 
			
		||||
    write->offset    = 0;
 | 
			
		||||
    write->num_bytes = compressed_bytes;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ~~~ Cache ~~~ */
 | 
			
		||||
 | 
			
		||||
static rt_resource_cache _cache;
 | 
			
		||||
@ -120,11 +172,17 @@ static rt_result InitResourceCache(void) {
 | 
			
		||||
    void *mem = malloc(required_mem);
 | 
			
		||||
    if (!mem)
 | 
			
		||||
        return RT_OUT_OF_MEMORY;
 | 
			
		||||
    rt_create_rwlock_result lock_create = rtCreateRWLock();
 | 
			
		||||
    if (!lock_create.ok) {
 | 
			
		||||
    rt_create_rwlock_result resource_lock_create = rtCreateRWLock();
 | 
			
		||||
    if (!resource_lock_create.ok) {
 | 
			
		||||
        free(mem);
 | 
			
		||||
        return RT_UNKNOWN_ERROR;
 | 
			
		||||
    }
 | 
			
		||||
    _cache.heap_lock = rtCreateMutex();
 | 
			
		||||
    if (!_cache.heap_lock) {
 | 
			
		||||
        free(mem);
 | 
			
		||||
        rtDestroyRWLock(&_cache.resource_lock);
 | 
			
		||||
        return RT_UNKNOWN_ERROR;
 | 
			
		||||
    }
 | 
			
		||||
    memset(mem, 0, required_mem);
 | 
			
		||||
    _cache.mem = mem;
 | 
			
		||||
 | 
			
		||||
@ -136,9 +194,9 @@ static rt_result InitResourceCache(void) {
 | 
			
		||||
                                          (size_t)count,
 | 
			
		||||
                                          0);
 | 
			
		||||
 | 
			
		||||
    _cache.current_size = 0;
 | 
			
		||||
    _cache.resources    = (rt_cached_resource *)(reclaim_keys + count);
 | 
			
		||||
    _cache.lock         = lock_create.lock;
 | 
			
		||||
    _cache.current_size  = 0;
 | 
			
		||||
    _cache.resources     = (rt_cached_resource *)(reclaim_keys + count);
 | 
			
		||||
    _cache.resource_lock = resource_lock_create.lock;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < count; ++i) {
 | 
			
		||||
        _cache.resources[i].next_free = (i < count - 1) ? i + 1 : UINT_MAX;
 | 
			
		||||
@ -153,19 +211,30 @@ static rt_result InitResourceCache(void) {
 | 
			
		||||
 | 
			
		||||
static void ShutdownResourceCache(void) {
 | 
			
		||||
    free(_cache.mem);
 | 
			
		||||
    rtDestroyRWLock(&_cache.lock);
 | 
			
		||||
    rtDestroyRWLock(&_cache.resource_lock);
 | 
			
		||||
    rtDestroyMutex(_cache.heap_lock);
 | 
			
		||||
    memset(&_cache, 0, sizeof(_cache));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* NOTE(Kevin): Only call this while holding a write-lock on the cache.
 | 
			
		||||
 * The function locks the reclaim heap lock itself. */
 | 
			
		||||
static bool FreeCacheSpace(size_t space) {
 | 
			
		||||
    size_t total_freed = 0;
 | 
			
		||||
    while (total_freed < space && !rtMinheapIsEmpty(&_cache.reclaim_heap)) {
 | 
			
		||||
    size_t free_space = (size_t)rt_ResourceCacheSize.i - _cache.current_size;
 | 
			
		||||
    rtLockMutex(_cache.heap_lock);
 | 
			
		||||
    while (free_space < space && !rtMinheapIsEmpty(&_cache.reclaim_heap)) {
 | 
			
		||||
        rt_cached_resource_ref ref;
 | 
			
		||||
        rtMinheapPop(&_cache.reclaim_heap, &ref);
 | 
			
		||||
 | 
			
		||||
        rt_cached_resource *res = &_cache.resources[ref.index];
 | 
			
		||||
 | 
			
		||||
        if (res->load_aio != RT_AIO_INVALID_HANDLE) {
 | 
			
		||||
            rtWaitForAIOCompletion(res->load_aio);
 | 
			
		||||
            rtReleaseAIO(res->load_aio);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rtReleaseBuffer(res->buffer, res->size);
 | 
			
		||||
        total_freed += res->size;
 | 
			
		||||
        free_space += res->size;
 | 
			
		||||
        _cache.current_size -= res->size;
 | 
			
		||||
 | 
			
		||||
        res->next_free     = _cache.first_free;
 | 
			
		||||
        _cache.first_free  = ref.index;
 | 
			
		||||
@ -185,7 +254,8 @@ static bool FreeCacheSpace(size_t space) {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return total_freed >= space;
 | 
			
		||||
    rtUnlockMutex(_cache.heap_lock);
 | 
			
		||||
    return free_space >= space;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned int FindCachedResource(rt_resource_id id) {
 | 
			
		||||
@ -202,13 +272,15 @@ static unsigned int FindCachedResource(rt_resource_id id) {
 | 
			
		||||
 | 
			
		||||
static rt_resource *CacheResource(rt_resource_id id, const rt_resource *res) {
 | 
			
		||||
    rt_resource *cached = NULL;
 | 
			
		||||
    rtLockWrite(&_cache.lock);
 | 
			
		||||
    rtLockWrite(&_cache.resource_lock);
 | 
			
		||||
    unsigned int index = FindCachedResource(id);
 | 
			
		||||
    if (index != UINT_MAX) {
 | 
			
		||||
        rt_cached_resource_ref ref      = {.id = id, .index = index};
 | 
			
		||||
        rt_cached_resource *cache_entry = &_cache.resources[index];
 | 
			
		||||
        ++cache_entry->usage_counter;
 | 
			
		||||
        rtLockMutex(_cache.heap_lock);
 | 
			
		||||
        rtMinheapUpdate(&_cache.reclaim_heap, &ref, cache_entry->usage_counter, NULL);
 | 
			
		||||
        rtUnlockMutex(_cache.heap_lock);
 | 
			
		||||
        cached = cache_entry->buffer;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Insert into cache */
 | 
			
		||||
@ -218,8 +290,8 @@ static rt_resource *CacheResource(rt_resource_id id, const rt_resource *res) {
 | 
			
		||||
                rtLog("RESMGR",
 | 
			
		||||
                      "Unable to reclaim %zu kB from the resource cache.",
 | 
			
		||||
                      total_size / 1024);
 | 
			
		||||
                rtUnlockWrite(&_cache.lock);
 | 
			
		||||
                return NULL;
 | 
			
		||||
                rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
                return cached;
 | 
			
		||||
            }
 | 
			
		||||
            RT_ASSERT(_cache.first_free != UINT_MAX,
 | 
			
		||||
                      "There must be a free cache entry after space was freed.");
 | 
			
		||||
@ -228,11 +300,11 @@ static rt_resource *CacheResource(rt_resource_id id, const rt_resource *res) {
 | 
			
		||||
        void *buffer = rtAllocBuffer(total_size);
 | 
			
		||||
        if (!buffer) {
 | 
			
		||||
            rtLog("RESMG", "Unable to allocate %zu kB for the new resource.", total_size / 1024);
 | 
			
		||||
            rtUnlockWrite(&_cache.lock);
 | 
			
		||||
            return NULL;
 | 
			
		||||
            rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
            return cached;
 | 
			
		||||
        }
 | 
			
		||||
        memcpy(buffer, res, sizeof(rt_resource));
 | 
			
		||||
        cached       = buffer;
 | 
			
		||||
        cached = buffer;
 | 
			
		||||
        memcpy(cached, res, sizeof(rt_resource));
 | 
			
		||||
        cached->data = (void *)(cached + 1);
 | 
			
		||||
        CopyResourceData(res, cached->data);
 | 
			
		||||
 | 
			
		||||
@ -242,12 +314,17 @@ static rt_resource *CacheResource(rt_resource_id id, const rt_resource *res) {
 | 
			
		||||
        _cache.resources[index].usage_counter = 1;
 | 
			
		||||
        _cache.resources[index].size          = total_size;
 | 
			
		||||
        _cache.resources[index].next_free     = UINT_MAX;
 | 
			
		||||
        _cache.resources[index].load_aio      = RT_AIO_INVALID_HANDLE;
 | 
			
		||||
 | 
			
		||||
        _cache.current_size += total_size;
 | 
			
		||||
 | 
			
		||||
        rt_cached_resource_ref reclaim_ref = {
 | 
			
		||||
            .id    = id,
 | 
			
		||||
            .index = index,
 | 
			
		||||
        };
 | 
			
		||||
        rtLockMutex(_cache.heap_lock);
 | 
			
		||||
        rtMinheapPush(&_cache.reclaim_heap, 1, &reclaim_ref);
 | 
			
		||||
        rtUnlockMutex(_cache.heap_lock);
 | 
			
		||||
 | 
			
		||||
        /* Insert into lookup table */
 | 
			
		||||
        bool inserted  = false;
 | 
			
		||||
@ -256,6 +333,7 @@ static rt_resource *CacheResource(rt_resource_id id, const rt_resource *res) {
 | 
			
		||||
            size_t slot = (id + off) % ht_size;
 | 
			
		||||
            if (_cache.resource_ids[slot] == RT_INVALID_RESOURCE_ID ||
 | 
			
		||||
                _cache.resource_ids[slot] == RT_TOMBSTONE_ID || _cache.resource_ids[slot] == id) {
 | 
			
		||||
                _cache.resource_ids[slot]     = id;
 | 
			
		||||
                _cache.resource_indices[slot] = index;
 | 
			
		||||
                inserted                      = true;
 | 
			
		||||
                break;
 | 
			
		||||
@ -266,10 +344,72 @@ static rt_resource *CacheResource(rt_resource_id id, const rt_resource *res) {
 | 
			
		||||
                          "Failed to insert created resource into the resource lookup table.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    rtUnlockWrite(&_cache.lock);
 | 
			
		||||
    rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
    return cached;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void InsertPrefetchResourceIntoCache(rt_resource_id id,
 | 
			
		||||
                                            rt_aio_handle load_aio,
 | 
			
		||||
                                            void *load_buffer,
 | 
			
		||||
                                            size_t load_buffer_size) {
 | 
			
		||||
    rtLockWrite(&_cache.resource_lock);
 | 
			
		||||
    unsigned int index = FindCachedResource(id);
 | 
			
		||||
    if (index != UINT_MAX) {
 | 
			
		||||
        rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (_cache.current_size + load_buffer_size >= (size_t)rt_ResourceCacheSize.i) {
 | 
			
		||||
        if (!FreeCacheSpace(load_buffer_size)) {
 | 
			
		||||
            rtLog("RESMGR",
 | 
			
		||||
                  "Unable to reclaim %zu kB from the resource cache.",
 | 
			
		||||
                  load_buffer_size / 1024);
 | 
			
		||||
            rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        RT_ASSERT(_cache.first_free != UINT_MAX,
 | 
			
		||||
                  "There must be a free cache entry after space was freed.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    index                                 = _cache.first_free;
 | 
			
		||||
    _cache.first_free                     = _cache.resources[index].next_free;
 | 
			
		||||
    _cache.resources[index].buffer        = load_buffer;
 | 
			
		||||
    _cache.resources[index].usage_counter = 1;
 | 
			
		||||
    _cache.resources[index].size          = load_buffer_size;
 | 
			
		||||
    _cache.resources[index].next_free     = UINT_MAX;
 | 
			
		||||
    _cache.resources[index].load_aio      = load_aio;
 | 
			
		||||
 | 
			
		||||
    _cache.current_size += load_buffer_size;
 | 
			
		||||
 | 
			
		||||
    rt_cached_resource_ref reclaim_ref = {
 | 
			
		||||
        .id    = id,
 | 
			
		||||
        .index = index,
 | 
			
		||||
    };
 | 
			
		||||
    rtLockMutex(_cache.heap_lock);
 | 
			
		||||
    rtMinheapPush(&_cache.reclaim_heap, 1, &reclaim_ref);
 | 
			
		||||
    rtUnlockMutex(_cache.heap_lock);
 | 
			
		||||
 | 
			
		||||
    /* Insert into lookup table */
 | 
			
		||||
    bool inserted  = false;
 | 
			
		||||
    size_t ht_size = (size_t)rt_MaxCachedResources.i * 2;
 | 
			
		||||
    for (size_t off = 0; off < ht_size; ++off) {
 | 
			
		||||
        size_t slot = (id + off) % ht_size;
 | 
			
		||||
        if (_cache.resource_ids[slot] == RT_INVALID_RESOURCE_ID ||
 | 
			
		||||
            _cache.resource_ids[slot] == RT_TOMBSTONE_ID || _cache.resource_ids[slot] == id) {
 | 
			
		||||
            _cache.resource_ids[slot]     = id;
 | 
			
		||||
            _cache.resource_indices[slot] = index;
 | 
			
		||||
            inserted                      = true;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (!inserted) {
 | 
			
		||||
        rtReportError("RESMGR",
 | 
			
		||||
                      "Failed to insert created resource into the resource lookup table.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ~~~ Resource Namespace ~~~ */
 | 
			
		||||
 | 
			
		||||
static rt_resource_namespace _namespace;
 | 
			
		||||
@ -301,40 +441,23 @@ static void ShutdownNamespace(void) {
 | 
			
		||||
    memset(&_namespace, 0, sizeof(_namespace));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static rt_resource_ref *GetResourceRefPtr(rt_resource_id id) {
 | 
			
		||||
    rt_resource_ref *ref = NULL;
 | 
			
		||||
static rt_resource_ref GetResourceRef(rt_resource_id id) {
 | 
			
		||||
    rt_resource_ref ref = {.file = RT_INVALID_FILE_ID};
 | 
			
		||||
    rtLockRead(&_namespace.lock);
 | 
			
		||||
    size_t ns_size = (size_t)rt_ResourceNamespaceSize.i;
 | 
			
		||||
    for (size_t j = 0; j < ns_size; ++j) {
 | 
			
		||||
        size_t at = (id + j) % ns_size;
 | 
			
		||||
        if (_namespace.ids[at] == RT_INVALID_RESOURCE_ID) {
 | 
			
		||||
    for (size_t off = 0; off < ns_size; ++off) {
 | 
			
		||||
        size_t at = (id + off) % ns_size;
 | 
			
		||||
        if (_namespace.ids[at] == id) {
 | 
			
		||||
            ref = _namespace.refs[at];
 | 
			
		||||
            break;
 | 
			
		||||
        } else if (_namespace.ids[at] == id) {
 | 
			
		||||
            ref = &_namespace.refs[at];
 | 
			
		||||
        } else if (_namespace.ids[at] == RT_INVALID_RESOURCE_ID) {
 | 
			
		||||
            rtLog("RESMGR", "Tried to load unknown resource %llx", id);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    rtUnlockRead(&_namespace.lock);
 | 
			
		||||
    return ref;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Fills the passed write struct with the necessary information to save the resource to a file */
 | 
			
		||||
static void
 | 
			
		||||
PrepareResourceFlushToFile(rt_resource_id id, const rt_resource *resource, rt_file_write *write) {
 | 
			
		||||
    /* A file write needs one contiguous buffer */
 | 
			
		||||
    RT_ASSERT(((uintptr_t)resource->data == (uintptr_t)resource + sizeof(*resource)),
 | 
			
		||||
              "The resource must reside in the cache, to ensure the correct memory layout");
 | 
			
		||||
 | 
			
		||||
    char file_path[260];
 | 
			
		||||
    rtSPrint(file_path, 260, "%s/%llx.bin", rt_ResourceDirectory.s, id);
 | 
			
		||||
 | 
			
		||||
    write->file      = rtAddFile(file_path);
 | 
			
		||||
    write->buffer    = resource;
 | 
			
		||||
    write->offset    = 0;
 | 
			
		||||
    write->num_bytes = sizeof(rt_resource) + GetResourceDataSize(resource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ~~~ Public API ~~~ */
 | 
			
		||||
 | 
			
		||||
@ -357,6 +480,203 @@ void ShutdownResourceManager(void) {
 | 
			
		||||
    ShutdownNamespace();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT rt_result rtGetResource(rt_resource_id id, void *dest) {
 | 
			
		||||
    rtLockRead(&_cache.resource_lock);
 | 
			
		||||
    unsigned int cache_index = FindCachedResource(id);
 | 
			
		||||
    if (cache_index != UINT_MAX) {
 | 
			
		||||
        rt_cached_resource *cached = &_cache.resources[cache_index];
 | 
			
		||||
        /* TODO(Kevin): It's possible that the load is not finished. */
 | 
			
		||||
        if (cached->load_aio != RT_AIO_INVALID_HANDLE) {
 | 
			
		||||
            rtUnlockRead(&_cache.resource_lock);
 | 
			
		||||
            rtLockWrite(&_cache.resource_lock);
 | 
			
		||||
            if (cached->load_aio != RT_AIO_INVALID_HANDLE) {
 | 
			
		||||
                rt_aio_state state = rtWaitForAIOCompletion(cached->load_aio);
 | 
			
		||||
                rtReleaseAIO(cached->load_aio);
 | 
			
		||||
                cached->load_aio = RT_AIO_INVALID_HANDLE;
 | 
			
		||||
                if (state != RT_AIO_STATE_FINISHED) {
 | 
			
		||||
                    rtLog("RESMGR", "Failed to load resource %llx: %u", id, state);
 | 
			
		||||
                    rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
                    return RT_UNKNOWN_ERROR;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                /* Need to decompress the resource */
 | 
			
		||||
                rt_resource_ref ref = GetResourceRef(id);
 | 
			
		||||
                if (ref.file == RT_INVALID_FILE_ID) {
 | 
			
		||||
                    rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
                    return RT_INVALID_VALUE;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                void *decompressed = rtAllocBuffer(ref.decompressed_size);
 | 
			
		||||
                if (!decompressed) {
 | 
			
		||||
                    rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
                    return RT_OUT_OF_MEMORY;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                size_t written_bytes = rtDecompressData(cached->buffer,
 | 
			
		||||
                                                        cached->size,
 | 
			
		||||
                                                        decompressed,
 | 
			
		||||
                                                        ref.decompressed_size);
 | 
			
		||||
                if (written_bytes != ref.decompressed_size) {
 | 
			
		||||
                    rtLog("RESMGR",
 | 
			
		||||
                          "Corrupted resource data %llx: Result of decompression does not match "
 | 
			
		||||
                          "saved "
 | 
			
		||||
                          "metadata.",
 | 
			
		||||
                          id);
 | 
			
		||||
                    rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
                    return RT_UNKNOWN_ERROR;
 | 
			
		||||
                }
 | 
			
		||||
                rt_resource *resource = decompressed;
 | 
			
		||||
                /* Patch the data pointer */
 | 
			
		||||
                resource->data = (resource + 1);
 | 
			
		||||
 | 
			
		||||
                /* Note that we allow the cache to grow beyond its configured maximum here. */
 | 
			
		||||
                rtReleaseBuffer(cached->buffer, cached->size);
 | 
			
		||||
                _cache.current_size -= cached->size;
 | 
			
		||||
                cached->size = ref.decompressed_size;
 | 
			
		||||
                _cache.current_size += ref.decompressed_size;
 | 
			
		||||
                cached->buffer = decompressed;
 | 
			
		||||
 | 
			
		||||
                rtUnlockWrite(&_cache.resource_lock);
 | 
			
		||||
                rtLockRead(&_cache.resource_lock);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RT_ASSERT(cached->size == rtGetResourceSize(id), "Inconsistent resource size");
 | 
			
		||||
        memcpy(dest, cached->buffer, cached->size);
 | 
			
		||||
 | 
			
		||||
        rtLockMutex(_cache.heap_lock);
 | 
			
		||||
        ++cached->usage_counter;
 | 
			
		||||
        rt_cached_resource_ref ref = {.id = id, .index = cache_index};
 | 
			
		||||
        rtMinheapUpdate(&_cache.reclaim_heap, &ref, cached->usage_counter, NULL);
 | 
			
		||||
        rtUnlockMutex(_cache.heap_lock);
 | 
			
		||||
        rtUnlockRead(&_cache.resource_lock);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Load the resource file */
 | 
			
		||||
        rtUnlockRead(&_cache.resource_lock);
 | 
			
		||||
        rt_resource_ref ref = GetResourceRef(id);
 | 
			
		||||
        if (ref.file == RT_INVALID_FILE_ID) {
 | 
			
		||||
            return RT_INVALID_VALUE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rt_temp_arena temp_arena = rtGetTemporaryArena(NULL, 0);
 | 
			
		||||
        void *compressed_buffer  = rtArenaPush(temp_arena.arena, ref.compressed_size);
 | 
			
		||||
        if (!compressed_buffer) {
 | 
			
		||||
            rtReturnTemporaryArena(temp_arena);
 | 
			
		||||
            return RT_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rt_aio_state state = rtSubmitSingleLoadSync((rt_file_load){
 | 
			
		||||
            .file      = ref.file,
 | 
			
		||||
            .dest      = compressed_buffer,
 | 
			
		||||
            .num_bytes = ref.compressed_size,
 | 
			
		||||
            .offset    = ref.offset,
 | 
			
		||||
        });
 | 
			
		||||
        if (state != RT_AIO_STATE_FINISHED) {
 | 
			
		||||
            rtLog("RESMGR", "Failed to load resource %llx: %u", id, state);
 | 
			
		||||
            rtReturnTemporaryArena(temp_arena);
 | 
			
		||||
            return RT_UNKNOWN_ERROR;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Decompress */
 | 
			
		||||
        size_t written_bytes =
 | 
			
		||||
            rtDecompressData(compressed_buffer, ref.compressed_size, dest, ref.decompressed_size);
 | 
			
		||||
        rtReturnTemporaryArena(temp_arena);
 | 
			
		||||
        if (written_bytes != ref.decompressed_size) {
 | 
			
		||||
            rtLog("RESMGR",
 | 
			
		||||
                  "Corrupted resource data %llx: Result of decompression does not match saved "
 | 
			
		||||
                  "metadata.",
 | 
			
		||||
                  id);
 | 
			
		||||
            return RT_UNKNOWN_ERROR;
 | 
			
		||||
        }
 | 
			
		||||
        rt_resource *resource = dest;
 | 
			
		||||
        /* Patch the data pointer */
 | 
			
		||||
        resource->data = (resource + 1);
 | 
			
		||||
 | 
			
		||||
        CacheResource(id, resource);
 | 
			
		||||
 | 
			
		||||
        rtPrefetchResources(resource->dependencies, resource->dependency_count);
 | 
			
		||||
        rtPrefetchResources(resource->subresources, resource->subresource_count);
 | 
			
		||||
    }
 | 
			
		||||
    return RT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT size_t rtGetResourceSize(rt_resource_id id) {
 | 
			
		||||
    size_t size = 0;
 | 
			
		||||
    rtLockRead(&_namespace.lock);
 | 
			
		||||
    size_t ns_size = (size_t)rt_ResourceNamespaceSize.i;
 | 
			
		||||
    for (size_t off = 0; off < ns_size; ++off) {
 | 
			
		||||
        size_t at = (id + off) % ns_size;
 | 
			
		||||
        if (_namespace.ids[at] == id) {
 | 
			
		||||
            size = _namespace.refs[at].decompressed_size;
 | 
			
		||||
            break;
 | 
			
		||||
        } else if (_namespace.ids[at] == RT_INVALID_RESOURCE_ID) {
 | 
			
		||||
            rtLog("RESMGR", "Tried to get size of unknown resource %llx", id);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    rtUnlockRead(&_namespace.lock);
 | 
			
		||||
    return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT void rtPrefetchResources(const rt_resource_id *ids, uint32_t count) {
 | 
			
		||||
    rt_load_batch loads = {.num_loads = 0};
 | 
			
		||||
    rt_aio_handle handles[RT_LOAD_BATCH_MAX_SIZE];
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0; i < count; ++i) {
 | 
			
		||||
        rt_resource_ref ref = GetResourceRef(ids[i]);
 | 
			
		||||
        if (ref.file == RT_INVALID_FILE_ID) {
 | 
			
		||||
            rtLog("RESMGR", "Attempted to prefetch unknown resource %llx", ids[i]);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Check if the resource is already cached */
 | 
			
		||||
        if (FindCachedResource(ids[i]) != UINT_MAX)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        void *buffer = rtAllocBuffer(ref.compressed_size);
 | 
			
		||||
        if (!buffer) {
 | 
			
		||||
            rtLog("RESMGR",
 | 
			
		||||
                  "Could not prefetch resource %llx because a buffer allocation failed.",
 | 
			
		||||
                  ids[i]);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        loads.loads[loads.num_loads] = (rt_file_load){
 | 
			
		||||
            .file      = ref.file,
 | 
			
		||||
            .num_bytes = ref.compressed_size,
 | 
			
		||||
            .offset    = ref.offset,
 | 
			
		||||
            .dest      = buffer,
 | 
			
		||||
        };
 | 
			
		||||
        ++loads.num_loads;
 | 
			
		||||
        if (loads.num_loads == RT_LOAD_BATCH_MAX_SIZE || i == count - 1) {
 | 
			
		||||
            if (rtSubmitLoadBatch(&loads, handles) != RT_SUCCESS) {
 | 
			
		||||
                rtLog("RESMGR", "Prefetch failed because the file loads could not be submitted.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (uint32_t j = 0; j < loads.num_loads; ++j) {
 | 
			
		||||
                InsertPrefetchResourceIntoCache(ids[i - loads.num_loads + j],
 | 
			
		||||
                                                handles[j],
 | 
			
		||||
                                                loads.loads[j].dest,
 | 
			
		||||
                                                loads.loads[j].num_bytes);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            loads.num_loads = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (loads.num_loads > 0) {
 | 
			
		||||
        if (rtSubmitLoadBatch(&loads, handles) != RT_SUCCESS) {
 | 
			
		||||
            rtLog("RESMGR", "Prefetch failed because the file loads could not be submitted.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint32_t j = 0; j < loads.num_loads; ++j) {
 | 
			
		||||
            InsertPrefetchResourceIntoCache(ids[count - 1 - loads.num_loads + j],
 | 
			
		||||
                                            handles[j],
 | 
			
		||||
                                            loads.loads[j].dest,
 | 
			
		||||
                                            loads.loads[j].num_bytes);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
 | 
			
		||||
                                         const char **names,
 | 
			
		||||
                                         const rt_resource *resources,
 | 
			
		||||
@ -368,6 +688,8 @@ RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
 | 
			
		||||
    rt_aio_handle write_handles[RT_WRITE_BATCH_MAX_SIZE];
 | 
			
		||||
    uint32_t outstanding_writes = 0;
 | 
			
		||||
 | 
			
		||||
    rt_temp_arena temp_arena = rtGetTemporaryArena(NULL, 0);
 | 
			
		||||
 | 
			
		||||
    rtLockWrite(&_namespace.lock);
 | 
			
		||||
    for (uint32_t i = 0; i < count; ++i) {
 | 
			
		||||
        size_t name_len   = strlen(names[i]);
 | 
			
		||||
@ -383,14 +705,31 @@ RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
 | 
			
		||||
 | 
			
		||||
                ids[i] = id;
 | 
			
		||||
 | 
			
		||||
                const rt_resource *cached_resource = CacheResource(id, &resources[i]);
 | 
			
		||||
                rt_resource *cached = CacheResource(id, &resources[i]);
 | 
			
		||||
 | 
			
		||||
                PrepareResourceFlushToFile(id, cached_resource, &writes.writes[writes.num_writes]);
 | 
			
		||||
                _namespace.ids[at]         = id;
 | 
			
		||||
                _namespace.refs[at].offset = writes.writes[writes.num_writes].offset;
 | 
			
		||||
                _namespace.refs[at].size   = writes.writes[writes.num_writes].num_bytes;
 | 
			
		||||
                _namespace.refs[at].file   = writes.writes[writes.num_writes].file;
 | 
			
		||||
                if (!PrepareResourceFlushToFile(id,
 | 
			
		||||
                                                cached,
 | 
			
		||||
                                                &writes.writes[writes.num_writes],
 | 
			
		||||
                                                temp_arena.arena)) {
 | 
			
		||||
 | 
			
		||||
                    rtReportError("RESMGR", "Failed to prepare resource %llx for writing.", id);
 | 
			
		||||
                    inserted = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                _namespace.ids[at]                  = id;
 | 
			
		||||
                _namespace.refs[at].offset          = writes.writes[writes.num_writes].offset;
 | 
			
		||||
                _namespace.refs[at].compressed_size = writes.writes[writes.num_writes].num_bytes;
 | 
			
		||||
                _namespace.refs[at].file            = writes.writes[writes.num_writes].file;
 | 
			
		||||
                _namespace.refs[at].decompressed_size =
 | 
			
		||||
                    sizeof(rt_resource) + GetResourceDataSize(&resources[i]);
 | 
			
		||||
                ++writes.num_writes;
 | 
			
		||||
 | 
			
		||||
                rtLog("RESMGR",
 | 
			
		||||
                      "Created resource %llx: Uncompressed size: %zu bytes, compressed size: %zu "
 | 
			
		||||
                      "bytes.",
 | 
			
		||||
                      id,
 | 
			
		||||
                      _namespace.refs[at].decompressed_size,
 | 
			
		||||
                      _namespace.refs[at].compressed_size);
 | 
			
		||||
                break;
 | 
			
		||||
            } else if (_namespace.ids[at] == id) {
 | 
			
		||||
                rtReportError("RESMGR",
 | 
			
		||||
@ -439,6 +778,83 @@ RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
out:
 | 
			
		||||
    rtReturnTemporaryArena(temp_arena);
 | 
			
		||||
    rtUnlockWrite(&_namespace.lock);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resource) {
 | 
			
		||||
    static const char *type_str[RT_RESOURCE_TYPE_count] = {"Shader", "Pipeline"};
 | 
			
		||||
    rtLog("RESMGR", "Resource %llx:", id);
 | 
			
		||||
    rtLog("RESMGR",
 | 
			
		||||
          "  type: %s",
 | 
			
		||||
          (resource->type < RT_RESOURCE_TYPE_count) ? type_str[resource->type] : "<INVALID>");
 | 
			
		||||
    rtLog("RESMGR", "  subresources:");
 | 
			
		||||
    for (uint32_t i = 0; i < resource->subresource_count; ++i) {
 | 
			
		||||
        rtLog("RESMGR", "    - %llx", resource->subresources[i]);
 | 
			
		||||
    }
 | 
			
		||||
    rtLog("RESMGR", "  dependencies:");
 | 
			
		||||
    for (uint32_t i = 0; i < resource->dependency_count; ++i) {
 | 
			
		||||
        rtLog("RESMGR", "    - %llx", resource->dependencies[i]);
 | 
			
		||||
    }
 | 
			
		||||
    switch (resource->type) {
 | 
			
		||||
    case RT_RESOURCE_PIPELINE: {
 | 
			
		||||
        static const char *binding_str[RT_ATTRIBUTE_VALUE_count] = {"<UNDEFINED>",
 | 
			
		||||
                                                                    "MaterialAlbedo",
 | 
			
		||||
                                                                    "MaterialNormal"};
 | 
			
		||||
        const rt_pipeline_info *pipeline                         = resource->data;
 | 
			
		||||
        rtLog("RESMGR", "  pipeline data:");
 | 
			
		||||
        rtLog("RESMGR", "    vertex shader:   %llx", pipeline->vertex_shader);
 | 
			
		||||
        rtLog("RESMGR", "    fragment shader: %llx", pipeline->fragment_shader);
 | 
			
		||||
        rtLog("RESMGR", "    compute shader:  %llx", pipeline->compute_shader);
 | 
			
		||||
        rtLog("RESMGR", "    uniform bindings:");
 | 
			
		||||
        const rt_attribute_binding *uniform_bindings =
 | 
			
		||||
            rtResolveConstRelptr(&pipeline->uniform_bindings);
 | 
			
		||||
        for (uint32_t i = 0; i < pipeline->uniform_binding_count; ++i) {
 | 
			
		||||
            rtLog("RESMGR",
 | 
			
		||||
                  "      - %u : %s",
 | 
			
		||||
                  uniform_bindings[i].index,
 | 
			
		||||
                  (uniform_bindings[i].value < RT_ATTRIBUTE_VALUE_count)
 | 
			
		||||
                      ? binding_str[uniform_bindings[i].value]
 | 
			
		||||
                      : "<INVALID>");
 | 
			
		||||
        }
 | 
			
		||||
        rtLog("RESMGR", "    texture bindings:");
 | 
			
		||||
        const rt_attribute_binding *texture_bindings =
 | 
			
		||||
            rtResolveConstRelptr(&pipeline->texture_bindings);
 | 
			
		||||
        for (uint32_t i = 0; i < pipeline->texture_binding_count; ++i) {
 | 
			
		||||
            rtLog("RESMGR",
 | 
			
		||||
                  "      - %u : %s",
 | 
			
		||||
                  texture_bindings[i].index,
 | 
			
		||||
                  (texture_bindings[i].value < RT_ATTRIBUTE_VALUE_count)
 | 
			
		||||
                      ? binding_str[texture_bindings[i].value]
 | 
			
		||||
                      : "<INVALID>");
 | 
			
		||||
        }
 | 
			
		||||
        rtLog("RESMGR", "    storage bindings:");
 | 
			
		||||
        const rt_attribute_binding *storage_bindings =
 | 
			
		||||
            rtResolveConstRelptr(&pipeline->storage_bindings);
 | 
			
		||||
        for (uint32_t i = 0; i < pipeline->storage_binding_count; ++i) {
 | 
			
		||||
            rtLog("RESMGR",
 | 
			
		||||
                  "      - %u : %s",
 | 
			
		||||
                  storage_bindings[i].index,
 | 
			
		||||
                  (storage_bindings[i].value < RT_ATTRIBUTE_VALUE_count)
 | 
			
		||||
                      ? binding_str[storage_bindings[i].value]
 | 
			
		||||
                      : "<INVALID>");
 | 
			
		||||
        }
 | 
			
		||||
    } break;
 | 
			
		||||
    case RT_RESOURCE_SHADER: {
 | 
			
		||||
        static const char *stype_str[RT_SHADER_TYPE_count]  = {"<INVALID>", "Vulkan"};
 | 
			
		||||
        static const char *stage_str[RT_SHADER_STAGE_count] = {"Vertex", "Fragment", "Compute"};
 | 
			
		||||
        const rt_shader_info *shader                        = resource->data;
 | 
			
		||||
        rtLog("RESMGR", "  shader data:");
 | 
			
		||||
        rtLog("RESMGR",
 | 
			
		||||
              "    type: %s",
 | 
			
		||||
              (shader->type < RT_SHADER_TYPE_count) ? stype_str[shader->type] : "<INVALID>");
 | 
			
		||||
        rtLog("RESMGR",
 | 
			
		||||
              "    stage: %s",
 | 
			
		||||
              (shader->stage < RT_SHADER_STAGE_count) ? stage_str[shader->stage] : "<INVALID>");
 | 
			
		||||
        rtLog("RESMGR", "    bytecode: %zu bytes", shader->bytecode_length);
 | 
			
		||||
    } break;
 | 
			
		||||
    default:
 | 
			
		||||
        rtLog("RESMGR", "  unknown data at: %llx", (uintptr_t)resource->data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -34,6 +34,8 @@ typedef enum {
 | 
			
		||||
 | 
			
		||||
    /* A pipeline state object */
 | 
			
		||||
    RT_RESOURCE_PIPELINE,
 | 
			
		||||
 | 
			
		||||
    RT_RESOURCE_TYPE_count,
 | 
			
		||||
} rt_resource_type;
 | 
			
		||||
 | 
			
		||||
#define RT_MAX_SUBRESOURCES          32
 | 
			
		||||
@ -55,6 +57,26 @@ typedef struct {
 | 
			
		||||
    rt_resource_id dependencies[RT_MAX_RESOURCE_DEPENDENCIES];
 | 
			
		||||
} rt_resource;
 | 
			
		||||
 | 
			
		||||
/* Retrieves a resource.
 | 
			
		||||
 *
 | 
			
		||||
 * This function will also attempt to pre-cache resources that are likely to be accessed
 | 
			
		||||
 * in the future. Namely subresources and dependencies of the accessed resource. 
 | 
			
		||||
 *
 | 
			
		||||
 * The passed destination must point to a buffer large enough to hold the resource.
 | 
			
		||||
 * You can query the resources size with rtGetResourceSize().
 | 
			
		||||
 */
 | 
			
		||||
RT_DLLEXPORT rt_result rtGetResource(rt_resource_id id, void *dest);
 | 
			
		||||
 | 
			
		||||
/* Notifies the system that a resource might be accessed soon.
 | 
			
		||||
 * Starts a asynchronous prefetch of that resource. */
 | 
			
		||||
RT_DLLEXPORT void rtPrefetchResources(const rt_resource_id *ids, uint32_t count);
 | 
			
		||||
 | 
			
		||||
/* Returns the size of a resource in bytes, or 0 if the resource id is invalid. */
 | 
			
		||||
RT_DLLEXPORT size_t rtGetResourceSize(rt_resource_id id);
 | 
			
		||||
 | 
			
		||||
/* Logs information about a resource. Useful for debugging */
 | 
			
		||||
RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resource);
 | 
			
		||||
 | 
			
		||||
/* Registers resources with the resource manager, making them available to the system.
 | 
			
		||||
 * 
 | 
			
		||||
 * The runtime will create a standalone file for each resource in the resource directory.
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,21 @@ extern "C" {
 | 
			
		||||
#define RT_MB(n) ((n)*1024U * 1024U)
 | 
			
		||||
#define RT_GB(n) ((n)*1024U * 1024U * 1024U)
 | 
			
		||||
 | 
			
		||||
#if defined(_MSC_VER)
 | 
			
		||||
/* For some reason _Thread_local does not work with vs2022,
 | 
			
		||||
 * despite MS documentation claiming it should. */
 | 
			
		||||
#define RT_THREAD_LOCAL __declspec(thread)
 | 
			
		||||
#elif defined(__GNUC__) || defined(__clang__)
 | 
			
		||||
/* _Thread_local in C11-C17, thread_local in C23 */
 | 
			
		||||
#if __STDC_VERSION__ > 201710L
 | 
			
		||||
#define RT_THREAD_LOCAL thread_local
 | 
			
		||||
#elif __STDC_VERSION__ >= 201112L
 | 
			
		||||
#define RT_THREAD_LOCAL _Thread_local
 | 
			
		||||
#else
 | 
			
		||||
#pragma error Pre-C11 not supported.
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef unsigned int rt_result;
 | 
			
		||||
 | 
			
		||||
/* Default result codes */
 | 
			
		||||
@ -80,11 +95,11 @@ RT_DLLEXPORT void rtLog(const char *subsystem, const char *fmt, ...);
 | 
			
		||||
RT_DLLEXPORT int rtAssertHandler(const char *expr, const char *msg, const char *file, int line);
 | 
			
		||||
#define RT_ASSERT(x, msg)                                                                          \
 | 
			
		||||
    do {                                                                                           \
 | 
			
		||||
        if (!(x)) {                                                                               \
 | 
			
		||||
            if (rtAssertHandler(#x, (msg), __FILE__, __LINE__) == 0) {                            \
 | 
			
		||||
        if (!(x)) {                                                                                \
 | 
			
		||||
            if (rtAssertHandler(#x, (msg), __FILE__, __LINE__) == 0) {                             \
 | 
			
		||||
                RT_DEBUGBREAK;                                                                     \
 | 
			
		||||
            }                                                                                      \
 | 
			
		||||
       }                                                                                           \
 | 
			
		||||
        }                                                                                          \
 | 
			
		||||
    } while (0)
 | 
			
		||||
#else
 | 
			
		||||
#define RT_ASSERT(x, msg) RT_UNUSED(x)
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ static rt_shader_bytecode CompileNullShader(rt_shader_stage stage,
 | 
			
		||||
typedef rt_shader_bytecode
 | 
			
		||||
shader_compiler_fn(rt_shader_stage, rt_shader_optimization_level, rt_text_span, const char *, rt_arena *);
 | 
			
		||||
 | 
			
		||||
static shader_compiler_fn *_compiler_funcs[RT_SHADER_TYPE_COUNT] = {
 | 
			
		||||
static shader_compiler_fn *_compiler_funcs[RT_SHADER_TYPE_count] = {
 | 
			
		||||
    CompileNullShader,
 | 
			
		||||
 | 
			
		||||
#ifdef RT_BUILD_DXC_SHADER_COMPILER
 | 
			
		||||
@ -44,7 +44,7 @@ rt_shader_bytecode CompileShader(rt_shader_type type,
 | 
			
		||||
                                 rt_text_span code,
 | 
			
		||||
                                 const char *file_path,
 | 
			
		||||
                                 rt_arena *arena) {
 | 
			
		||||
    if (type >= RT_SHADER_TYPE_COUNT) {
 | 
			
		||||
    if (type >= RT_SHADER_TYPE_count) {
 | 
			
		||||
        rtLog("AC", "Invalid shader type %u", type);
 | 
			
		||||
        return (rt_shader_bytecode){.bytes = NULL, .len = 0};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user