Write the first compiled resources to files
This commit is contained in:
		
							parent
							
								
									9670844bb2
								
							
						
					
					
						commit
						3a9f9d4986
					
				
							
								
								
									
										1910
									
								
								contrib/stb_sprintf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1910
									
								
								contrib/stb_sprintf.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,6 +1,6 @@
 | 
				
			|||||||
#include "aio.h"
 | 
					#include "aio.h"
 | 
				
			||||||
#include "threading.h"
 | 
					 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "threading.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef _WIN32
 | 
					#ifdef _WIN32
 | 
				
			||||||
#define WIN32_LEAN_AND_MEAN
 | 
					#define WIN32_LEAN_AND_MEAN
 | 
				
			||||||
@ -223,6 +223,96 @@ RT_DLLEXPORT rt_result rtSubmitLoadBatch(const rt_load_batch *batch, rt_aio_hand
 | 
				
			|||||||
    return RT_SUCCESS;
 | 
					    return RT_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT rt_result rtSubmitWriteBatch(const rt_write_batch *batch, rt_aio_handle *handles) {
 | 
				
			||||||
 | 
					    if (batch->num_writes > RT_LOAD_BATCH_MAX_SIZE) {
 | 
				
			||||||
 | 
					        return RT_AIO_WRITE_TOO_LARGE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt_ringbuffer_space rbspace = ReserveRingbufferSpace(batch->num_writes);
 | 
				
			||||||
 | 
					    if (!rbspace.a) {
 | 
				
			||||||
 | 
					        rtReportError("aio", "Too many pending file operations");
 | 
				
			||||||
 | 
					        return RT_AIO_TOO_MANY_OPERATIONS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned int i = 0; i < batch->num_writes; ++i) {
 | 
				
			||||||
 | 
					        rt_aio *op = (i < rbspace.a_count) ? &rbspace.a[i] : &rbspace.b[i - rbspace.a_count];
 | 
				
			||||||
 | 
					        op->state  = RT_AIO_STATE_PENDING;
 | 
				
			||||||
 | 
					        const char *file_path = rtGetFilePath(batch->writes[i].file);
 | 
				
			||||||
 | 
					        if (!file_path) {
 | 
				
			||||||
 | 
					            rtReportError("aio", "Failed to resolve file path for a batched write");
 | 
				
			||||||
 | 
					            op->state  = RT_AIO_STATE_INVALID;
 | 
				
			||||||
 | 
					            handles[i] = RT_AIO_INVALID_HANDLE;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					        op->overlapped = (OVERLAPPED){
 | 
				
			||||||
 | 
					            /* ReadFileEx does not use hEvent and we are free to use it for our own purposes. */
 | 
				
			||||||
 | 
					            .hEvent       = (HANDLE)(op),
 | 
				
			||||||
 | 
					            .Internal     = 0,
 | 
				
			||||||
 | 
					            .InternalHigh = 0,
 | 
				
			||||||
 | 
					            .Offset       = (DWORD)(batch->writes[i].offset & MAXDWORD),
 | 
				
			||||||
 | 
					            .OffsetHigh   = (DWORD)(batch->writes[i].offset >> 32),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        WCHAR wpath[MAX_PATH];
 | 
				
			||||||
 | 
					        if (MultiByteToWideChar(CP_UTF8,
 | 
				
			||||||
 | 
					                                MB_PRECOMPOSED,
 | 
				
			||||||
 | 
					                                file_path,
 | 
				
			||||||
 | 
					                                -1,
 | 
				
			||||||
 | 
					                                wpath,
 | 
				
			||||||
 | 
					                                RT_ARRAY_COUNT(wpath)) == 0) {
 | 
				
			||||||
 | 
					            rtReportError("aio", "MultiByteToWideChar failed with error code: %u", GetLastError());
 | 
				
			||||||
 | 
					            op->state  = RT_AIO_STATE_FINISHED;
 | 
				
			||||||
 | 
					            handles[i] = RT_AIO_INVALID_HANDLE;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HANDLE file_handle = CreateFileW(wpath,
 | 
				
			||||||
 | 
					                                         GENERIC_WRITE,
 | 
				
			||||||
 | 
					                                         0,
 | 
				
			||||||
 | 
					                                         NULL,
 | 
				
			||||||
 | 
					                                         OPEN_ALWAYS,
 | 
				
			||||||
 | 
					                                         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
 | 
				
			||||||
 | 
					                                         NULL);
 | 
				
			||||||
 | 
					        if (file_handle == INVALID_HANDLE_VALUE) {
 | 
				
			||||||
 | 
					            DWORD err = GetLastError();
 | 
				
			||||||
 | 
					            char error_msg[256];
 | 
				
			||||||
 | 
					            Win32ErrorToString(err, error_msg, 256);
 | 
				
			||||||
 | 
					            rtReportError("aio",
 | 
				
			||||||
 | 
					                          "CreateFileW failed for file: %s with error code: %u  (%s)",
 | 
				
			||||||
 | 
					                          file_path,
 | 
				
			||||||
 | 
					                          err,
 | 
				
			||||||
 | 
					                          error_msg);
 | 
				
			||||||
 | 
					            op->state  = RT_AIO_STATE_INVALID;
 | 
				
			||||||
 | 
					            handles[i] = RT_AIO_INVALID_HANDLE;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        op->file_handle = file_handle;
 | 
				
			||||||
 | 
					        BOOL result     = WriteFileEx(file_handle,
 | 
				
			||||||
 | 
					                                  batch->writes[i].buffer,
 | 
				
			||||||
 | 
					                                  (DWORD)batch->writes[i].num_bytes,
 | 
				
			||||||
 | 
					                                  &op->overlapped,
 | 
				
			||||||
 | 
					                                  win32CompletionRoutine);
 | 
				
			||||||
 | 
					        DWORD err       = GetLastError();
 | 
				
			||||||
 | 
					        if (!result || (err != ERROR_SUCCESS && err != ERROR_ALREADY_EXISTS)) {
 | 
				
			||||||
 | 
					            char error_msg[256];
 | 
				
			||||||
 | 
					            Win32ErrorToString(err, error_msg, 256);
 | 
				
			||||||
 | 
					            rtReportError("aio", "WriteFileEx failed with error code: %u  (%s)", err, error_msg);
 | 
				
			||||||
 | 
					            op->state  = RT_AIO_STATE_FINISHED;
 | 
				
			||||||
 | 
					            handles[i] = RT_AIO_INVALID_HANDLE;
 | 
				
			||||||
 | 
					            CloseHandle(file_handle);
 | 
				
			||||||
 | 
					            op->file_handle = NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Handle is the index into the ringbuffer + 1 */
 | 
				
			||||||
 | 
					        ptrdiff_t op_idx = op - _ringbuffer.storage;
 | 
				
			||||||
 | 
					        handles[i]       = (uint32_t)op_idx + 1;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return RT_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT volatile rt_aio_state rtGetAIOState(rt_aio_handle handle) {
 | 
					RT_DLLEXPORT volatile rt_aio_state rtGetAIOState(rt_aio_handle handle) {
 | 
				
			||||||
    if (handle == RT_AIO_INVALID_HANDLE || handle > _ringbuffer.capacity)
 | 
					    if (handle == RT_AIO_INVALID_HANDLE || handle > _ringbuffer.capacity)
 | 
				
			||||||
        return RT_AIO_STATE_INVALID;
 | 
					        return RT_AIO_STATE_INVALID;
 | 
				
			||||||
@ -270,7 +360,6 @@ RT_DLLEXPORT rt_aio_state rtWaitForAIOCompletion(rt_aio_handle handle) {
 | 
				
			|||||||
    return state;
 | 
					    return state;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_DLLEXPORT rt_result rtSubmitSingleLoad(rt_file_load load, rt_aio_handle *handle) {
 | 
					RT_DLLEXPORT rt_result rtSubmitSingleLoad(rt_file_load load, rt_aio_handle *handle) {
 | 
				
			||||||
    rt_load_batch batch;
 | 
					    rt_load_batch batch;
 | 
				
			||||||
    batch.loads[0]  = load;
 | 
					    batch.loads[0]  = load;
 | 
				
			||||||
 | 
				
			|||||||
@ -33,13 +33,35 @@ typedef struct {
 | 
				
			|||||||
    unsigned int num_loads;
 | 
					    unsigned int num_loads;
 | 
				
			||||||
} rt_load_batch;
 | 
					} rt_load_batch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    size_t num_bytes; /** Number of bytes to write */
 | 
				
			||||||
 | 
					    size_t offset;    /** Offset at which to start writing */
 | 
				
			||||||
 | 
					    /* Source buffer with at least num_bytes bytes.
 | 
				
			||||||
 | 
					     * Must be valid until the write is finished.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    const void *buffer;
 | 
				
			||||||
 | 
					    rt_file_id file;
 | 
				
			||||||
 | 
					} rt_file_write;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RT_WRITE_OFFSET_APPEND ((size_t)-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RT_WRITE_BATCH_MAX_SIZE 64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* A batch of writes that will be started together.
 | 
				
			||||||
 | 
					 * The aio system will hand these to the OS. */
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    rt_file_write writes[RT_WRITE_BATCH_MAX_SIZE];
 | 
				
			||||||
 | 
					    unsigned int num_writes;
 | 
				
			||||||
 | 
					} rt_write_batch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define RT_AIO_INVALID_HANDLE 0
 | 
					#define RT_AIO_INVALID_HANDLE 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Handle for an async io operation. Can be used to query the state and result. */
 | 
					/** Handle for an async io operation. Can be used to query the state and result. */
 | 
				
			||||||
typedef uint32_t rt_aio_handle;
 | 
					typedef uint32_t rt_aio_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
    RT_AIO_LOAD_TOO_LARGE = (RT_SUCCESS + 1),
 | 
					    RT_AIO_LOAD_TOO_LARGE = RT_CUSTOM_ERROR_START,
 | 
				
			||||||
 | 
					    RT_AIO_WRITE_TOO_LARGE,
 | 
				
			||||||
    RT_AIO_TOO_MANY_OPERATIONS,
 | 
					    RT_AIO_TOO_MANY_OPERATIONS,
 | 
				
			||||||
    RT_AIO_OUT_OF_MEMORY,
 | 
					    RT_AIO_OUT_OF_MEMORY,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -51,14 +73,16 @@ typedef enum {
 | 
				
			|||||||
    RT_AIO_STATE_FAILED,
 | 
					    RT_AIO_STATE_FAILED,
 | 
				
			||||||
} rt_aio_state;
 | 
					} rt_aio_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT rt_result rtSubmitLoadBatch(const rt_load_batch *batch, rt_aio_handle *handles);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_DLLEXPORT volatile rt_aio_state rtGetAIOState(rt_aio_handle handle);
 | 
					RT_DLLEXPORT volatile rt_aio_state rtGetAIOState(rt_aio_handle handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Blocks until the given operation is no longer pending.
 | 
					/* Blocks until the given operation is no longer pending.
 | 
				
			||||||
 * Returns the state that caused the wait to end. The handle is still valid after this function returned. */
 | 
					 * Returns the state that caused the wait to end. The handle is still valid after this function
 | 
				
			||||||
 | 
					 * returned. */
 | 
				
			||||||
RT_DLLEXPORT rt_aio_state rtWaitForAIOCompletion(rt_aio_handle handle);
 | 
					RT_DLLEXPORT rt_aio_state rtWaitForAIOCompletion(rt_aio_handle handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT rt_result rtSubmitLoadBatch(const rt_load_batch *batch, rt_aio_handle *handles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Releases the internal storage for the operation.
 | 
					/* Releases the internal storage for the operation.
 | 
				
			||||||
 * The system is allowed to re-use the same handle value for new operations after this was called.
 | 
					 * The system is allowed to re-use the same handle value for new operations after this was called.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@ -70,6 +94,8 @@ RT_DLLEXPORT rt_result rtSubmitSingleLoad(rt_file_load load, rt_aio_handle *hand
 | 
				
			|||||||
 * Returns the state that caused the wait for completion to return. */
 | 
					 * Returns the state that caused the wait for completion to return. */
 | 
				
			||||||
RT_DLLEXPORT rt_aio_state rtSubmitSingleLoadSync(rt_file_load load);
 | 
					RT_DLLEXPORT rt_aio_state rtSubmitSingleLoadSync(rt_file_load load);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT rt_result rtSubmitWriteBatch(const rt_write_batch *batch, rt_aio_handle *handles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										45
									
								
								src/runtime/assert.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/runtime/assert.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					#define WIN32_LEAN_AND_MEAN
 | 
				
			||||||
 | 
					#include <Windows.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_CVAR_I(rt_AssertEnabled, "Enables or disables asserts in non-release builds. Default: 1", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERT_HANDLER_DBGBREAK 0
 | 
				
			||||||
 | 
					#define ASSERT_HANDLER_CONTINUE 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT int rtAssertHandler(const char *expr, const char *msg, const char *file, int line) {
 | 
				
			||||||
 | 
					    if (!rt_AssertEnabled.i)
 | 
				
			||||||
 | 
					        return ASSERT_HANDLER_CONTINUE;
 | 
				
			||||||
 | 
					    rtLog("ASSERT", "[%s:%d] Assertion (%s) failed: %s", file, line, expr, msg);
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					    char outmessage[512];
 | 
				
			||||||
 | 
					    snprintf(outmessage,
 | 
				
			||||||
 | 
					             511,
 | 
				
			||||||
 | 
					             "Assertion failed: %s\nMessage: %s\n%s:%d\nPress \"Yes\" to debug-break, \"No\" to "
 | 
				
			||||||
 | 
					             "continue with asserts enabled or \"Cancel\" to disable asserts.",
 | 
				
			||||||
 | 
					             expr,
 | 
				
			||||||
 | 
					             msg,
 | 
				
			||||||
 | 
					             file,
 | 
				
			||||||
 | 
					             line);
 | 
				
			||||||
 | 
					    outmessage[511] = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DWORD action = MessageBoxA(NULL, outmessage, "Assertion Failed", MB_YESNOCANCEL | MB_ICONERROR);
 | 
				
			||||||
 | 
					    if (action == IDYES) {
 | 
				
			||||||
 | 
					        return ASSERT_HANDLER_DBGBREAK;
 | 
				
			||||||
 | 
					    } else if (action == IDCANCEL) {
 | 
				
			||||||
 | 
					        rt_AssertEnabled.i = 0;
 | 
				
			||||||
 | 
					    } else if (action != IDNO) {
 | 
				
			||||||
 | 
					        rtReportError("CORE", "MessageBoxA for a failed assertion failed.");
 | 
				
			||||||
 | 
					        __debugbreak();
 | 
				
			||||||
 | 
					        ExitProcess(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    return ASSERT_HANDLER_CONTINUE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,15 +1,17 @@
 | 
				
			|||||||
 | 
					#include "asset_compiler.h"
 | 
				
			||||||
 | 
					#include "buffer_manager.h"
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "file_tab.h"
 | 
				
			||||||
 | 
					#include "fsutils.h"
 | 
				
			||||||
 | 
					#include "mem_arena.h"
 | 
				
			||||||
 | 
					#include "resources.h"
 | 
				
			||||||
#include "runtime.h"
 | 
					#include "runtime.h"
 | 
				
			||||||
#include "threading.h"
 | 
					#include "threading.h"
 | 
				
			||||||
#include "config.h"
 | 
					 | 
				
			||||||
#include "fsutils.h"
 | 
					 | 
				
			||||||
#include "file_tab.h"
 | 
					 | 
				
			||||||
#include "mem_arena.h"
 | 
					 | 
				
			||||||
#include "buffer_manager.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef RT_BUILD_ASSET_COMPILER
 | 
					#ifndef RT_BUILD_ASSET_COMPILER
 | 
				
			||||||
#error This should only be built when RT_BUILD_ASSET_COMPILER is defined.
 | 
					#error This should only be built when RT_BUILD_ASSET_COMPILER is defined.
 | 
				
			||||||
@ -17,15 +19,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    uint64_t last_processed;
 | 
					    uint64_t last_processed;
 | 
				
			||||||
 | 
					    rt_resource_id resources[RT_MAX_RESOURCES_PER_ASSET];
 | 
				
			||||||
 | 
					    unsigned int resource_count;
 | 
				
			||||||
 | 
					    bool in_processing;
 | 
				
			||||||
} rt_asset_data;
 | 
					} rt_asset_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    rt_file_id *files;
 | 
					    rt_file_id *files;
 | 
				
			||||||
    rt_asset_data *data;
 | 
					    rt_asset_data *data;
 | 
				
			||||||
 | 
					    rt_rwlock lock;
 | 
				
			||||||
} rt_asset_db;
 | 
					} rt_asset_db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef rt_result rt_asset_processor_fn(rt_file_id file, rt_arena *arena);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    const char *file_ext;
 | 
					    const char *file_ext;
 | 
				
			||||||
    rt_asset_processor_fn *proc;
 | 
					    rt_asset_processor_fn *proc;
 | 
				
			||||||
@ -60,7 +64,7 @@ static rt_asset_db _asset_db;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static rt_processing_queue _processing_queue;
 | 
					static rt_processing_queue _processing_queue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern rt_result PipelineProcessor(rt_file_id file, rt_arena *arena);
 | 
					extern RT_ASSET_PROCESSOR_FN(PipelineProcessor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static rt_asset_processor _processors[] = {
 | 
					static rt_asset_processor _processors[] = {
 | 
				
			||||||
    {.file_ext = ".pipeline", .proc = PipelineProcessor}
 | 
					    {.file_ext = ".pipeline", .proc = PipelineProcessor}
 | 
				
			||||||
@ -78,6 +82,12 @@ rt_result InitAssetCompiler(void) {
 | 
				
			|||||||
    _asset_db.files = mem;
 | 
					    _asset_db.files = mem;
 | 
				
			||||||
    _asset_db.data  = (rt_asset_data *)(_asset_db.files + db_size);
 | 
					    _asset_db.data  = (rt_asset_data *)(_asset_db.files + db_size);
 | 
				
			||||||
    memset(mem, 0, (sizeof(rt_file_id) + sizeof(rt_asset_data)) * db_size);
 | 
					    memset(mem, 0, (sizeof(rt_file_id) + sizeof(rt_asset_data)) * db_size);
 | 
				
			||||||
 | 
					    rt_create_rwlock_result lock_create = rtCreateRWLock();
 | 
				
			||||||
 | 
					    if (!lock_create.ok) {
 | 
				
			||||||
 | 
					        free(mem);
 | 
				
			||||||
 | 
					        return RT_UNKNOWN_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    _asset_db.lock = lock_create.lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _processing_queue.lock = rtCreateConditionVar();
 | 
					    _processing_queue.lock = rtCreateConditionVar();
 | 
				
			||||||
    if (!_processing_queue.lock) {
 | 
					    if (!_processing_queue.lock) {
 | 
				
			||||||
@ -94,7 +104,7 @@ rt_result InitAssetCompiler(void) {
 | 
				
			|||||||
        rt_AssetProcessingThreads.i = MAX_PROCESSING_THREADS;
 | 
					        rt_AssetProcessingThreads.i = MAX_PROCESSING_THREADS;
 | 
				
			||||||
    for (int i = 0; i < rt_AssetProcessingThreads.i; ++i) {
 | 
					    for (int i = 0; i < rt_AssetProcessingThreads.i; ++i) {
 | 
				
			||||||
        char name[64];
 | 
					        char name[64];
 | 
				
			||||||
        snprintf(name, 64, "AssetProcessorThread %d", i);
 | 
					        rtSPrint(name, 64, "AssetProcessorThread %d", i);
 | 
				
			||||||
        _processing_threads[i] = rtSpawnThread(ProcessorThreadEntry, NULL, name);
 | 
					        _processing_threads[i] = rtSpawnThread(ProcessorThreadEntry, NULL, name);
 | 
				
			||||||
        if (!_processing_threads[i]) {
 | 
					        if (!_processing_threads[i]) {
 | 
				
			||||||
            /* Wake the processing threads */
 | 
					            /* Wake the processing threads */
 | 
				
			||||||
@ -120,6 +130,7 @@ void ShutdownAssetCompiler(void) {
 | 
				
			|||||||
        rtJoinThread(_processing_threads[i]);
 | 
					        rtJoinThread(_processing_threads[i]);
 | 
				
			||||||
    free(_asset_db.files);
 | 
					    free(_asset_db.files);
 | 
				
			||||||
    rtDestroyConditionVar(_processing_queue.lock);
 | 
					    rtDestroyConditionVar(_processing_queue.lock);
 | 
				
			||||||
 | 
					    rtDestroyRWLock(&_asset_db.lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int DiscoverAssets(void) {
 | 
					static int DiscoverAssets(void) {
 | 
				
			||||||
@ -187,6 +198,7 @@ static int DiscoverAssets(void) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                rt_file_id fid = rtAddFile(file);
 | 
					                rt_file_id fid = rtAddFile(file);
 | 
				
			||||||
                unsigned int i = 0;
 | 
					                unsigned int i = 0;
 | 
				
			||||||
 | 
					                rtLockWrite(&_asset_db.lock);
 | 
				
			||||||
                while (i < (unsigned int)rt_AssetDBSize.i) {
 | 
					                while (i < (unsigned int)rt_AssetDBSize.i) {
 | 
				
			||||||
                    unsigned int slot = (fid + i) % (unsigned int)rt_AssetDBSize.i;
 | 
					                    unsigned int slot = (fid + i) % (unsigned int)rt_AssetDBSize.i;
 | 
				
			||||||
                    if (_asset_db.files[slot] == fid) {
 | 
					                    if (_asset_db.files[slot] == fid) {
 | 
				
			||||||
@ -194,13 +206,21 @@ static int DiscoverAssets(void) {
 | 
				
			|||||||
                    } else if (_asset_db.files[slot] == 0) {
 | 
					                    } else if (_asset_db.files[slot] == 0) {
 | 
				
			||||||
                        _asset_db.files[slot]               = fid;
 | 
					                        _asset_db.files[slot]               = fid;
 | 
				
			||||||
                        _asset_db.data[slot].last_processed = 0;
 | 
					                        _asset_db.data[slot].last_processed = 0;
 | 
				
			||||||
 | 
					                        memset(&_asset_db.data[slot].resources,
 | 
				
			||||||
 | 
					                               0,
 | 
				
			||||||
 | 
					                               sizeof(_asset_db.data[slot].resources));
 | 
				
			||||||
 | 
					                        _asset_db.data[slot].resource_count = 0;
 | 
				
			||||||
 | 
					                        _asset_db.data[slot].in_processing  = false;
 | 
				
			||||||
                        ++discovery_count;
 | 
					                        ++discovery_count;
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    ++i;
 | 
					                    ++i;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                rtUnlockWrite(&_asset_db.lock);
 | 
				
			||||||
                if (i == (unsigned int)rt_AssetDBSize.i) {
 | 
					                if (i == (unsigned int)rt_AssetDBSize.i) {
 | 
				
			||||||
                    rtLog("AC", "Failed to add %s to AssetDB, because no free slots are left.", file);
 | 
					                    rtLog("AC",
 | 
				
			||||||
 | 
					                          "Failed to add %s to AssetDB, because no free slots are left.",
 | 
				
			||||||
 | 
					                          file);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } while (!entry.is_last);
 | 
					        } while (!entry.is_last);
 | 
				
			||||||
@ -214,17 +234,38 @@ static int DiscoverAssets(void) {
 | 
				
			|||||||
static int CheckUpdatedAssets(void) {
 | 
					static int CheckUpdatedAssets(void) {
 | 
				
			||||||
    int updated_count = 0;
 | 
					    int updated_count = 0;
 | 
				
			||||||
    for (int i = 0; i < rt_AssetDBSize.i; ++i) {
 | 
					    for (int i = 0; i < rt_AssetDBSize.i; ++i) {
 | 
				
			||||||
        if (_asset_db.files[i] == 0)
 | 
					        rtLockRead(&_asset_db.lock);
 | 
				
			||||||
 | 
					        if (_asset_db.files[i] == 0) {
 | 
				
			||||||
 | 
					            rtUnlockRead(&_asset_db.lock);
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        const char *path      = rtGetFilePath(_asset_db.files[i]);
 | 
					        const char *path      = rtGetFilePath(_asset_db.files[i]);
 | 
				
			||||||
        uint64_t last_changed = rtGetFileModificationTimestamp(path);
 | 
					        uint64_t last_changed = rtGetFileModificationTimestamp(path);
 | 
				
			||||||
        if (_asset_db.data[i].last_processed < last_changed) {
 | 
					        if (!_asset_db.data[i].in_processing && _asset_db.data[i].last_processed < last_changed) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Check that we have not already added this file */
 | 
				
			||||||
 | 
					            rtLockConditionVar(_processing_queue.lock);
 | 
				
			||||||
 | 
					            bool already_in_queue = false;
 | 
				
			||||||
 | 
					            for (size_t entry_idx = _processing_queue.head; entry_idx != _processing_queue.tail;
 | 
				
			||||||
 | 
					                 entry_idx        = (entry_idx + 1) % RT_ARRAY_COUNT(_processing_queue.entries)) {
 | 
				
			||||||
 | 
					                if (_processing_queue.entries[entry_idx].fid == _asset_db.files[i]) {
 | 
				
			||||||
 | 
					                    already_in_queue = true;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            rtUnlockConditionVar(_processing_queue.lock, false);
 | 
				
			||||||
 | 
					            if (already_in_queue) {
 | 
				
			||||||
 | 
					                rtUnlockRead(&_asset_db.lock);
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const char *ext = path + strlen(path);
 | 
					            const char *ext = path + strlen(path);
 | 
				
			||||||
            while (*ext != '.' && ext != path)
 | 
					            while (*ext != '.' && ext != path)
 | 
				
			||||||
                --ext;
 | 
					                --ext;
 | 
				
			||||||
            if (*ext != '.')
 | 
					            if (*ext != '.')
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bool found_processor = false;
 | 
				
			||||||
            for (unsigned int j = 0; j < RT_ARRAY_COUNT(_processors); ++j) {
 | 
					            for (unsigned int j = 0; j < RT_ARRAY_COUNT(_processors); ++j) {
 | 
				
			||||||
                if (strcmp(ext, _processors[j].file_ext) == 0) {
 | 
					                if (strcmp(ext, _processors[j].file_ext) == 0) {
 | 
				
			||||||
                    rt_processing_queue_entry entry;
 | 
					                    rt_processing_queue_entry entry;
 | 
				
			||||||
@ -232,6 +273,8 @@ static int CheckUpdatedAssets(void) {
 | 
				
			|||||||
                    entry.processor_index = j;
 | 
					                    entry.processor_index = j;
 | 
				
			||||||
                    entry.db_index        = i;
 | 
					                    entry.db_index        = i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    found_processor = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    while (true) {
 | 
					                    while (true) {
 | 
				
			||||||
                        bool inserted = false;
 | 
					                        bool inserted = false;
 | 
				
			||||||
                        rtLockConditionVar(_processing_queue.lock);
 | 
					                        rtLockConditionVar(_processing_queue.lock);
 | 
				
			||||||
@ -248,8 +291,10 @@ static int CheckUpdatedAssets(void) {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if (found_processor)
 | 
				
			||||||
                ++updated_count;
 | 
					                ++updated_count;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        rtUnlockRead(&_asset_db.lock);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return updated_count;
 | 
					    return updated_count;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -299,24 +344,47 @@ static void ProcessorThreadEntry(void *param) {
 | 
				
			|||||||
        if (!got_entry)
 | 
					        if (!got_entry)
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rtLockWrite(&_asset_db.lock);
 | 
				
			||||||
 | 
					        _asset_db.data[entry.db_index].in_processing = true;
 | 
				
			||||||
 | 
					        rtUnlockWrite(&_asset_db.lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const char *path = rtGetFilePath(entry.fid);
 | 
					        const char *path = rtGetFilePath(entry.fid);
 | 
				
			||||||
        rtLog("AC", "Processing %s", path);
 | 
					        rtLog("AC", "Processing %s", path);
 | 
				
			||||||
        rtArenaClear(&arena);
 | 
					        rtArenaClear(&arena);
 | 
				
			||||||
        rt_result res = _processors[entry.processor_index].proc(entry.fid, &arena);
 | 
					        rt_resource_id existing_resources[RT_MAX_RESOURCES_PER_ASSET];
 | 
				
			||||||
 | 
					        unsigned int existing_resource_count;
 | 
				
			||||||
 | 
					        rtLockRead(&_asset_db.lock);
 | 
				
			||||||
 | 
					        memcpy(existing_resources,
 | 
				
			||||||
 | 
					               _asset_db.data[entry.db_index].resources,
 | 
				
			||||||
 | 
					               sizeof(existing_resources));
 | 
				
			||||||
 | 
					        existing_resource_count = _asset_db.data[entry.db_index].resource_count;
 | 
				
			||||||
 | 
					        rtUnlockRead(&_asset_db.lock);
 | 
				
			||||||
 | 
					        rt_resource_id new_resources[RT_MAX_RESOURCES_PER_ASSET];
 | 
				
			||||||
 | 
					        memset(&new_resources, 0, sizeof(new_resources));
 | 
				
			||||||
 | 
					        unsigned int new_resource_count = 0;
 | 
				
			||||||
 | 
					        rt_result res                   = _processors[entry.processor_index].proc(entry.fid,
 | 
				
			||||||
 | 
					                                                                existing_resource_count,
 | 
				
			||||||
 | 
					                                                                existing_resources,
 | 
				
			||||||
 | 
					                                                                &new_resource_count,
 | 
				
			||||||
 | 
					                                                                new_resources,
 | 
				
			||||||
 | 
					                                                                &arena);
 | 
				
			||||||
        if (res != RT_SUCCESS) {
 | 
					        if (res != RT_SUCCESS) {
 | 
				
			||||||
            rtLog("AC", "Failed to process %s: %u", path, res);
 | 
					            rtLog("AC", "Failed to process %s: %u", path, res);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        rtLockWrite(&_asset_db.lock);
 | 
				
			||||||
        _asset_db.data[entry.db_index].last_processed = rtGetCurrentTimestamp();
 | 
					        _asset_db.data[entry.db_index].last_processed = rtGetCurrentTimestamp();
 | 
				
			||||||
 | 
					        _asset_db.data[entry.db_index].in_processing  = false;
 | 
				
			||||||
 | 
					        memcpy(_asset_db.data[entry.db_index].resources, new_resources, sizeof(new_resources));
 | 
				
			||||||
 | 
					        _asset_db.data[entry.db_index].resource_count = new_resource_count;
 | 
				
			||||||
 | 
					        rtUnlockWrite(&_asset_db.lock);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Utilities for asset processors*/
 | 
					/* Utilities for asset processors*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "aio.h"
 | 
					#include "aio.h"
 | 
				
			||||||
#include "asset_compiler.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
rt_loaded_asset LoadAsset(rt_file_id file)
 | 
					rt_loaded_asset LoadAsset(rt_file_id file) {
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    const char *path = rtGetFilePath(file);
 | 
					    const char *path = rtGetFilePath(file);
 | 
				
			||||||
    size_t file_size = rtGetFileSize(path);
 | 
					    size_t file_size = rtGetFileSize(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,8 @@
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "file_tab.h"
 | 
					#include "file_tab.h"
 | 
				
			||||||
 | 
					#include "resources.h"
 | 
				
			||||||
 | 
					#include "mem_arena.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
@ -15,6 +17,22 @@ enum {
 | 
				
			|||||||
    RT_ASSET_PROCESSING_FAILED = RT_CUSTOM_ERROR_START,
 | 
					    RT_ASSET_PROCESSING_FAILED = RT_CUSTOM_ERROR_START,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RT_MAX_RESOURCES_PER_ASSET 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Asset processor prototype.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The new resources will replace the associated resources completely.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define RT_ASSET_PROCESSOR_FN(_Name)                                                               \
 | 
				
			||||||
 | 
					    rt_result _Name(rt_file_id file,                                                               \
 | 
				
			||||||
 | 
					                    unsigned int existing_resource_count,                                          \
 | 
				
			||||||
 | 
					                    const rt_resource_id *existing_resources,                                      \
 | 
				
			||||||
 | 
					                    unsigned int *new_resource_count,                                              \
 | 
				
			||||||
 | 
					                    rt_resource_id *new_resources,                                                 \
 | 
				
			||||||
 | 
					                    rt_arena *arena)
 | 
				
			||||||
 | 
					/* A Asset processor function */
 | 
				
			||||||
 | 
					typedef RT_ASSET_PROCESSOR_FN(rt_asset_processor_fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Allocated from the buffer manager */
 | 
					/* Allocated from the buffer manager */
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    void *buffer;
 | 
					    void *buffer;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETS_H
 | 
					 | 
				
			||||||
#define RT_ASSETS_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Asset system interface */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -9,16 +9,6 @@
 | 
				
			|||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
typedef struct rt_buffer_region_s {
 | 
					 | 
				
			||||||
    void *memory;
 | 
					 | 
				
			||||||
    int16_t *refcounts; // One per block
 | 
					 | 
				
			||||||
    uint32_t *bitmap;
 | 
					 | 
				
			||||||
    size_t block_count;
 | 
					 | 
				
			||||||
    rt_mutex *guard;
 | 
					 | 
				
			||||||
} rt_buffer_region;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Count leading zeroes.
 | 
					/* Count leading zeroes.
 | 
				
			||||||
 * Note that the return value of __builtin_clz(0) is undefined. */
 | 
					 * Note that the return value of __builtin_clz(0) is undefined. */
 | 
				
			||||||
#ifdef _MSC_VER
 | 
					#ifdef _MSC_VER
 | 
				
			||||||
@ -51,222 +41,8 @@ static __forceinline bool IsLZCNTSupported(void) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
/* NOTE(Kevin): Keep these sorted! */
 | 
					 | 
				
			||||||
static size_t _block_sizes[] = {RT_KB(512), RT_MB(1), RT_MB(4), RT_MB(8)};
 | 
					 | 
				
			||||||
#define NUM_BLOCK_SIZES (sizeof(_block_sizes) / sizeof(_block_sizes[0]))
 | 
					 | 
				
			||||||
static rt_buffer_region _regions[NUM_BLOCK_SIZES];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_CVAR_SZ(rt_BufferManagerMemory,
 | 
					 | 
				
			||||||
           "Total number of bytes allocated for the buffer manager. Default: 1GB",
 | 
					 | 
				
			||||||
           RT_GB(1));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result InitBufferManager(void) {
 | 
					 | 
				
			||||||
    if ((rt_BufferManagerMemory.sz % NUM_BLOCK_SIZES) != 0)
 | 
					 | 
				
			||||||
        rtLog("BUFFERMGR",
 | 
					 | 
				
			||||||
              "Configured memory amount is not dividable by number of block "
 | 
					 | 
				
			||||||
              "sizes: %u MB/%u",
 | 
					 | 
				
			||||||
              rt_BufferManagerMemory.sz / (1024 * 1024),
 | 
					 | 
				
			||||||
              NUM_BLOCK_SIZES);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t mem_per_size = rt_BufferManagerMemory.sz / NUM_BLOCK_SIZES;
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) {
 | 
					 | 
				
			||||||
        if ((mem_per_size % _block_sizes[i]) != 0)
 | 
					 | 
				
			||||||
            rtLog("BUFFERMGR",
 | 
					 | 
				
			||||||
                  "Memory per block size is not dividable by block size: %u "
 | 
					 | 
				
			||||||
                  "MB/%u KB",
 | 
					 | 
				
			||||||
                  mem_per_size / (1024 * 1024),
 | 
					 | 
				
			||||||
                  _block_sizes[i] / 1024);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        size_t block_count      = mem_per_size / _block_sizes[i];
 | 
					 | 
				
			||||||
        _regions[i].block_count = block_count;
 | 
					 | 
				
			||||||
        _regions[i].guard       = rtCreateMutex();
 | 
					 | 
				
			||||||
        if (!_regions[i].guard) {
 | 
					 | 
				
			||||||
            rtReportError("BUFFERMGR", "Failed to create guard mutex %u", i);
 | 
					 | 
				
			||||||
            return RT_BUFFER_MGR_MUTEX_CREATION_FAILED;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        _regions[i].memory = malloc(mem_per_size);
 | 
					 | 
				
			||||||
        if (!_regions[i].memory) {
 | 
					 | 
				
			||||||
            rtDestroyMutex(_regions[i].guard);
 | 
					 | 
				
			||||||
            rtReportError("BUFFERMGR", "Failed to allocate memory.", i);
 | 
					 | 
				
			||||||
            return RT_BUFFER_MGR_OUT_OF_MEMORY;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        _regions[i].bitmap = calloc((block_count + 31) / 32, sizeof(uint32_t));
 | 
					 | 
				
			||||||
        if (!_regions[i].bitmap) {
 | 
					 | 
				
			||||||
            rtDestroyMutex(_regions[i].guard);
 | 
					 | 
				
			||||||
            free(_regions[i].memory);
 | 
					 | 
				
			||||||
            rtReportError("BUFFERMGR", "Failed to allocate memory.", i);
 | 
					 | 
				
			||||||
            return RT_BUFFER_MGR_OUT_OF_MEMORY;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        _regions[i].refcounts = calloc(block_count, sizeof(uint16_t));
 | 
					 | 
				
			||||||
        if (!_regions[i].refcounts) {
 | 
					 | 
				
			||||||
            rtDestroyMutex(_regions[i].guard);
 | 
					 | 
				
			||||||
            free(_regions[i].memory);
 | 
					 | 
				
			||||||
            free(_regions[i].bitmap);
 | 
					 | 
				
			||||||
            rtReportError("BUFFERMGR", "Failed to allocate memory.", i);
 | 
					 | 
				
			||||||
            return RT_BUFFER_MGR_OUT_OF_MEMORY;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ShutdownBufferManager(void) {
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) {
 | 
					 | 
				
			||||||
        rtDestroyMutex(_regions[i].guard);
 | 
					 | 
				
			||||||
        free(_regions[i].memory);
 | 
					 | 
				
			||||||
        free(_regions[i].bitmap);
 | 
					 | 
				
			||||||
        free(_regions[i].refcounts);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_DLLEXPORT void *rtAllocBuffer(size_t size) {
 | 
					 | 
				
			||||||
    assert(IsLZCNTSupported());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Determine the best block size to use
 | 
					 | 
				
			||||||
    size_t required_blocks = (size + _block_sizes[0] - 1) / _block_sizes[0];
 | 
					 | 
				
			||||||
    size_t best_fit        = 0;
 | 
					 | 
				
			||||||
    for (size_t i = 1; i < NUM_BLOCK_SIZES; ++i) {
 | 
					 | 
				
			||||||
        size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i];
 | 
					 | 
				
			||||||
        if (block_count < required_blocks && size >= _block_sizes[i]) {
 | 
					 | 
				
			||||||
            required_blocks = block_count;
 | 
					 | 
				
			||||||
            best_fit        = i;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void *result = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_buffer_region *region = &_regions[best_fit];
 | 
					 | 
				
			||||||
    rtLockMutex(region->guard);
 | 
					 | 
				
			||||||
    size_t dword_count = (region->block_count + 31) / 32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (required_blocks < 32) {
 | 
					 | 
				
			||||||
        /* Fast path for allocations that potentially fit into one dword */
 | 
					 | 
				
			||||||
        uint32_t in_use_mask = (1ull << required_blocks) - 1;
 | 
					 | 
				
			||||||
        size_t max_occupancy = 32 - required_blocks;
 | 
					 | 
				
			||||||
        for (size_t i = 0; i < dword_count; ++i) {
 | 
					 | 
				
			||||||
            size_t block_index = 0;
 | 
					 | 
				
			||||||
            if (region->bitmap[i] != 0 && popcnt32(region->bitmap[i]) < max_occupancy) {
 | 
					 | 
				
			||||||
                size_t free_high_blocks = lzcnt32(region->bitmap[i]);
 | 
					 | 
				
			||||||
                if (free_high_blocks >= required_blocks) {
 | 
					 | 
				
			||||||
                    /* High blocks are free */
 | 
					 | 
				
			||||||
                    size_t first_free = 32 - free_high_blocks;
 | 
					 | 
				
			||||||
                    region->bitmap[i] |= (in_use_mask << first_free);
 | 
					 | 
				
			||||||
                    block_index = i * 32 + first_free;
 | 
					 | 
				
			||||||
                    result      = (char *)region->memory + block_index * _block_sizes[best_fit];
 | 
					 | 
				
			||||||
                } else if (tzcnt32(region->bitmap[i]) >= required_blocks) {
 | 
					 | 
				
			||||||
                    /* Low blocks are free */
 | 
					 | 
				
			||||||
                    region->bitmap[i] |= in_use_mask;
 | 
					 | 
				
			||||||
                    block_index = i * 32;
 | 
					 | 
				
			||||||
                    result      = (char *)region->memory + block_index * _block_sizes[best_fit];
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    /* Check if we can find a large enough range of free blocks.
 | 
					 | 
				
			||||||
                     * Start after the first set bit.
 | 
					 | 
				
			||||||
                     */
 | 
					 | 
				
			||||||
                    for (uint32_t j = tzcnt32(region->bitmap[i]) + 1; j < 32 - required_blocks;
 | 
					 | 
				
			||||||
                         ++j) {
 | 
					 | 
				
			||||||
                        if ((region->bitmap[i] & in_use_mask << j) == 0) {
 | 
					 | 
				
			||||||
                            region->bitmap[i] |= (in_use_mask << j);
 | 
					 | 
				
			||||||
                            block_index = i * 32 + j;
 | 
					 | 
				
			||||||
                            result = (char *)region->memory + block_index * _block_sizes[best_fit];
 | 
					 | 
				
			||||||
                            break;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else if (region->bitmap[i] == 0) {
 | 
					 | 
				
			||||||
                /* All free */
 | 
					 | 
				
			||||||
                region->bitmap[i] = in_use_mask;
 | 
					 | 
				
			||||||
                block_index       = i * 32;
 | 
					 | 
				
			||||||
                result            = (char *)region->memory + block_index * _block_sizes[best_fit];
 | 
					 | 
				
			||||||
            } else if (i < dword_count - 1) {
 | 
					 | 
				
			||||||
                /* Check if we can use high blocks from this dword and low blocks from the next one
 | 
					 | 
				
			||||||
                 */
 | 
					 | 
				
			||||||
                size_t high_blocks = lzcnt32(region->bitmap[i]);
 | 
					 | 
				
			||||||
                size_t low_blocks =
 | 
					 | 
				
			||||||
                    (region->bitmap[i + 1] != 0) ? tzcnt32(region->bitmap[i + 1]) : 32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (high_blocks + low_blocks >= required_blocks) {
 | 
					 | 
				
			||||||
                    size_t high_mask  = (1u << high_blocks) - 1;
 | 
					 | 
				
			||||||
                    size_t first_free = 32 - high_blocks;
 | 
					 | 
				
			||||||
                    size_t low_mask   = (1u << (required_blocks - high_blocks)) - 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    region->bitmap[i] |= (high_mask << first_free);
 | 
					 | 
				
			||||||
                    region->bitmap[i + 1] |= low_mask;
 | 
					 | 
				
			||||||
                    block_index = i * 32 + first_free;
 | 
					 | 
				
			||||||
                    result      = (char *)region->memory + block_index * _block_sizes[best_fit];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (result) {
 | 
					 | 
				
			||||||
                for (size_t j = 0; j < required_blocks; ++j)
 | 
					 | 
				
			||||||
                    region->refcounts[block_index + j] = 1;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        for (size_t i = 0; i < dword_count; ++i) {
 | 
					 | 
				
			||||||
            if (region->bitmap[i] == UINT32_MAX) {
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            /* Check if we can start the allocation here */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtUnlockMutex(region->guard);
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_DLLEXPORT void rtReleaseBuffer(const void *begin, size_t size) {
 | 
					 | 
				
			||||||
    if (!begin)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    uintptr_t begin_addr = (uintptr_t)begin;
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) {
 | 
					 | 
				
			||||||
        uintptr_t region_addr = (uintptr_t)_regions[i].memory;
 | 
					 | 
				
			||||||
        size_t region_size    = _block_sizes[i] * _regions[i].block_count;
 | 
					 | 
				
			||||||
        if (begin_addr >= region_addr && begin_addr + size <= region_addr + region_size) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i];
 | 
					 | 
				
			||||||
            size_t first_block = (begin_addr - region_addr) / _block_sizes[i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            rtLockMutex(_regions[i].guard);
 | 
					 | 
				
			||||||
            for (size_t j = 0; j < block_count; ++j) {
 | 
					 | 
				
			||||||
                size_t dword = (first_block + j) / 32;
 | 
					 | 
				
			||||||
                size_t bit   = (first_block + j) % 32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (--_regions[i].refcounts[first_block + j] == 0)
 | 
					 | 
				
			||||||
                    _regions[i].bitmap[dword] &= ~(1u << bit);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            rtUnlockMutex(_regions[i].guard);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtLog("BUFFERMGR", "Tried to release an invalid buffer");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_DLLEXPORT void rtIncreaseBufferRefCount(const void *begin, size_t size) {
 | 
					 | 
				
			||||||
    uintptr_t begin_addr = (uintptr_t)begin;
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) {
 | 
					 | 
				
			||||||
        uintptr_t region_addr = (uintptr_t)_regions[i].memory;
 | 
					 | 
				
			||||||
        size_t region_size    = _block_sizes[i] * _regions[i].block_count;
 | 
					 | 
				
			||||||
        if (begin_addr >= region_addr && begin_addr + size <= region_addr + region_size) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i];
 | 
					 | 
				
			||||||
            size_t first_block = (begin_addr - region_addr) / _block_sizes[i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            rtLockMutex(_regions[i].guard);
 | 
					 | 
				
			||||||
            for (size_t j = 0; j < block_count; ++j) {
 | 
					 | 
				
			||||||
                ++_regions[i].refcounts[first_block + j];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            rtUnlockMutex(_regions[i].guard);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtLog("BUFFERMGR", "Tried to increase the refcount of an invalid buffer");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define BLOCK_SIZE 4096u
 | 
					#define BLOCK_SIZE 4096u
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static uint32_t *_refcounts;
 | 
					 | 
				
			||||||
static uint32_t *_bitmap;
 | 
					static uint32_t *_bitmap;
 | 
				
			||||||
static char *_memory;
 | 
					static char *_memory;
 | 
				
			||||||
static rt_mutex *_guard;
 | 
					static rt_mutex *_guard;
 | 
				
			||||||
@ -298,14 +74,12 @@ extern rt_result InitBufferManager(void) {
 | 
				
			|||||||
    size_t dword_count = (block_count + 31) / 32;
 | 
					    size_t dword_count = (block_count + 31) / 32;
 | 
				
			||||||
    _block_count       = block_count;
 | 
					    _block_count       = block_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _memory = malloc(budget + dword_count * sizeof(uint32_t) + block_count * sizeof(uint32_t));
 | 
					    _memory = malloc(budget + dword_count * sizeof(uint32_t));
 | 
				
			||||||
    if (!_memory) {
 | 
					    if (!_memory) {
 | 
				
			||||||
        return RT_OUT_OF_MEMORY;
 | 
					        return RT_OUT_OF_MEMORY;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    _bitmap = (uint32_t*)(_memory + budget);
 | 
					    _bitmap = (uint32_t*)(_memory + budget);
 | 
				
			||||||
    memset(_bitmap, 0, sizeof(uint32_t) * dword_count);
 | 
					    memset(_bitmap, 0, sizeof(uint32_t) * dword_count);
 | 
				
			||||||
    _refcounts = _bitmap + dword_count;
 | 
					 | 
				
			||||||
    memset(_refcounts, 0, sizeof(uint32_t) * block_count);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return RT_SUCCESS;
 | 
					    return RT_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -381,9 +155,6 @@ RT_DLLEXPORT void *rtAllocBuffer(size_t size) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (size_t i = first_block; i < first_block + alloc_blocks; ++i)
 | 
					 | 
				
			||||||
        _refcounts[i] = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					    rtUnlockMutex(_guard);
 | 
				
			||||||
    rtLog("BUFFERMGR", "Result ptr %llx", (uintptr_t)result);
 | 
					    rtLog("BUFFERMGR", "Result ptr %llx", (uintptr_t)result);
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
@ -395,22 +166,9 @@ RT_DLLEXPORT void rtReleaseBuffer(const void *begin, size_t size) {
 | 
				
			|||||||
    uintptr_t first_block = off / BLOCK_SIZE;
 | 
					    uintptr_t first_block = off / BLOCK_SIZE;
 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					    rtLockMutex(_guard);
 | 
				
			||||||
    for (size_t i = first_block; i < first_block + alloc_blocks; ++i) {
 | 
					    for (size_t i = first_block; i < first_block + alloc_blocks; ++i) {
 | 
				
			||||||
        if (--_refcounts[i] == 0) {
 | 
					 | 
				
			||||||
        size_t dword = i / 32;
 | 
					        size_t dword = i / 32;
 | 
				
			||||||
        size_t bit   = i % 32;
 | 
					        size_t bit   = i % 32;
 | 
				
			||||||
        _bitmap[dword] &= ~(1u << bit);
 | 
					        _bitmap[dword] &= ~(1u << bit);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_DLLEXPORT void rtIncreaseBufferRefCount(const void *begin, size_t size) {
 | 
					 | 
				
			||||||
    size_t alloc_blocks   = (size + BLOCK_SIZE - 1) / BLOCK_SIZE;
 | 
					 | 
				
			||||||
    uintptr_t off         = (uintptr_t)begin - (uintptr_t)_memory;
 | 
					 | 
				
			||||||
    uintptr_t first_block = off / BLOCK_SIZE;
 | 
					 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
    for (size_t i = first_block; i < first_block + alloc_blocks; ++i) {
 | 
					 | 
				
			||||||
        ++_refcounts[i];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					    rtUnlockMutex(_guard);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -16,8 +16,6 @@ RT_DLLEXPORT void *rtAllocBuffer(size_t size);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT void rtReleaseBuffer(const void *begin, size_t size);
 | 
					RT_DLLEXPORT void rtReleaseBuffer(const void *begin, size_t size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT void rtIncreaseBufferRefCount(const void *begin, size_t size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										59
									
								
								src/runtime/ds.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/runtime/ds.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					#ifndef RT_DS_H
 | 
				
			||||||
 | 
					#define RT_DS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Datastructure Library */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* A minheap.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The memory pointed to by keys and values is not owned by the minheap
 | 
				
			||||||
 | 
					 * and instead provided by the caller. */
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    int *keys;
 | 
				
			||||||
 | 
					    void *values;
 | 
				
			||||||
 | 
					    size_t value_size;
 | 
				
			||||||
 | 
					    size_t capacity;
 | 
				
			||||||
 | 
					    int size;
 | 
				
			||||||
 | 
					} rt_minheap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Comparison function for rtMinheapUpdate.
 | 
				
			||||||
 | 
					 * The function should return 0 if *a and *b are considered equal,
 | 
				
			||||||
 | 
					 * and a different (non-zero) value if they are non-equal.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note that memcmp fits this requirement.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef int rt_minheap_cmp_fn(const void *a, const void *b, size_t n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Takes the arrays and re-orders the values to create a minheap. */
 | 
				
			||||||
 | 
					RT_DLLEXPORT rt_minheap
 | 
				
			||||||
 | 
					rtCreateMinheap(int *keys, void *values, size_t value_size, size_t capacity, int initial_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Copies the value with the smallest key to min_value */
 | 
				
			||||||
 | 
					RT_DLLEXPORT void rtMinheapPeek(const rt_minheap *minheap, void *min_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Copies the value with the smallest key to min_value and removes it from the heap */
 | 
				
			||||||
 | 
					RT_DLLEXPORT void rtMinheapPop(rt_minheap *minheap, void *min_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Pushes a new value into the minheap */
 | 
				
			||||||
 | 
					RT_DLLEXPORT void rtMinheapPush(rt_minheap *minheap, int key, const void *value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Changes the key of an existing value, or inserts it, if it's not found.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Uses memcmp if cmp is NULL. */
 | 
				
			||||||
 | 
					RT_DLLEXPORT void
 | 
				
			||||||
 | 
					rtMinheapUpdate(rt_minheap *minheap, const void *value, int new_key, rt_minheap_cmp_fn *cmp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static RT_INLINE int rtMinheapIsEmpty(rt_minheap *minheap) {
 | 
				
			||||||
 | 
					    return minheap->size == 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										138
									
								
								src/runtime/ds_minheap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/runtime/ds_minheap.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,138 @@
 | 
				
			|||||||
 | 
					#include "ds.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int memcmp(const void *ptr1, const void *ptr2, size_t num);
 | 
				
			||||||
 | 
					extern void *memcpy(void *destination, const void *source, size_t num);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Utilities for index calculation */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static RT_INLINE int Parent(int i) {
 | 
				
			||||||
 | 
					    return (i - 1) / 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static RT_INLINE int Left(int i) {
 | 
				
			||||||
 | 
					    return 2 * i + 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static RT_INLINE int Right(int i) {
 | 
				
			||||||
 | 
					    return 2 * i + 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void Swap(rt_minheap *minheap, int i, int j, void *tmpv) {
 | 
				
			||||||
 | 
					    void *vi = (char *)minheap->values + (size_t)i * minheap->value_size;
 | 
				
			||||||
 | 
					    void *vj = (char *)minheap->values + (size_t)j * minheap->value_size;
 | 
				
			||||||
 | 
					    int tk   = minheap->keys[i];
 | 
				
			||||||
 | 
					    memcpy(tmpv, vi, minheap->value_size);
 | 
				
			||||||
 | 
					    minheap->keys[i] = minheap->keys[j];
 | 
				
			||||||
 | 
					    memcpy(vi, vj, minheap->value_size);
 | 
				
			||||||
 | 
					    minheap->keys[j] = tk;
 | 
				
			||||||
 | 
					    memcpy(vj, tmpv, minheap->value_size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void Heapify(rt_minheap *minheap, int start) {
 | 
				
			||||||
 | 
					    int i    = start;
 | 
				
			||||||
 | 
					    int size = (int)minheap->capacity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* FIXME: Allocate on temp-arena */
 | 
				
			||||||
 | 
					    char tmpv[256];
 | 
				
			||||||
 | 
					    assert(minheap->value_size < sizeof(tmpv) && "tmpv should be allocated on a temporary arena.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        int min = i;
 | 
				
			||||||
 | 
					        if (Left(i) < size && minheap->keys[Left(i)] < minheap->keys[min])
 | 
				
			||||||
 | 
					            min = Left(i);
 | 
				
			||||||
 | 
					        if (Right(i) < size && minheap->keys[Right(i)] < minheap->keys[min])
 | 
				
			||||||
 | 
					            min = Right(i);
 | 
				
			||||||
 | 
					        if (min == i)
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        Swap(minheap, i, min, tmpv);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void Decrease(rt_minheap *minheap, int i, int newkey) {
 | 
				
			||||||
 | 
					    minheap->keys[i] = newkey;
 | 
				
			||||||
 | 
					    /* FIXME: Allocate on temp-arena */
 | 
				
			||||||
 | 
					    char tmpv[256];
 | 
				
			||||||
 | 
					    assert(minheap->value_size < sizeof(tmpv) && "tmpv should be allocated on a temporary arena.");
 | 
				
			||||||
 | 
					    while (i > 0 && minheap->keys[i] < minheap->keys[Parent(i)]) {
 | 
				
			||||||
 | 
					        Swap(minheap, i, Parent(i), tmpv);
 | 
				
			||||||
 | 
					        i = Parent(i);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void Remove(rt_minheap *minheap, int i) {
 | 
				
			||||||
 | 
					    int last = minheap->size - 1;
 | 
				
			||||||
 | 
					    if (last < 0)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    /* FIXME: Allocate on temp-arena */
 | 
				
			||||||
 | 
					    char tmpv[256];
 | 
				
			||||||
 | 
					    assert(minheap->value_size < sizeof(tmpv) && "tmpv should be allocated on a temporary arena.");
 | 
				
			||||||
 | 
					    Swap(minheap, i, last, tmpv);
 | 
				
			||||||
 | 
					    minheap->size = last;
 | 
				
			||||||
 | 
					    if (i != last) {
 | 
				
			||||||
 | 
					        if (i == 0 || minheap->keys[i] > minheap->keys[Parent(i)])
 | 
				
			||||||
 | 
					            Heapify(minheap, i);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            Decrease(minheap, i, minheap->keys[i]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Takes the arrays and re-orders the values to create a minheap. */
 | 
				
			||||||
 | 
					RT_DLLEXPORT rt_minheap
 | 
				
			||||||
 | 
					rtCreateMinheap(int *keys, void *values, size_t value_size, size_t capacity, int initial_size) {
 | 
				
			||||||
 | 
					    rt_minheap minheap = {
 | 
				
			||||||
 | 
					        .keys       = keys,
 | 
				
			||||||
 | 
					        .values     = values,
 | 
				
			||||||
 | 
					        .value_size = value_size,
 | 
				
			||||||
 | 
					        .capacity   = capacity,
 | 
				
			||||||
 | 
					        .size       = initial_size,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    /* Start at the last non-leaf element */
 | 
				
			||||||
 | 
					    for (int i = initial_size / 2 - 1; i >= 0; --i)
 | 
				
			||||||
 | 
					        Heapify(&minheap, i);
 | 
				
			||||||
 | 
					    return minheap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Copies the value with the smallest key to min_value */
 | 
				
			||||||
 | 
					RT_DLLEXPORT void rtMinheapPeek(const rt_minheap *minheap, void *min_value) {
 | 
				
			||||||
 | 
					    memcpy(min_value, minheap->values, minheap->value_size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Copies the value with the smallest key to min_value and removes it from the heap */
 | 
				
			||||||
 | 
					RT_DLLEXPORT void rtMinheapPop(rt_minheap *minheap, void *min_value) {
 | 
				
			||||||
 | 
					    rtMinheapPeek(minheap, min_value);
 | 
				
			||||||
 | 
					    Remove(minheap, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Pushes a new value into the minheap */
 | 
				
			||||||
 | 
					RT_DLLEXPORT void rtMinheapPush(rt_minheap *minheap, int key, const void *value) {
 | 
				
			||||||
 | 
					    int at            = minheap->size;
 | 
				
			||||||
 | 
					    void *v           = (char *)minheap->values + (size_t)at * minheap->value_size;
 | 
				
			||||||
 | 
					    minheap->keys[at] = key;
 | 
				
			||||||
 | 
					    memcpy(v, value, minheap->value_size);
 | 
				
			||||||
 | 
					    ++minheap->size;
 | 
				
			||||||
 | 
					    Decrease(minheap, at, key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Changes the key of an existing value, or inserts it, if it's not found.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Uses memcmp if cmp is NULL. */
 | 
				
			||||||
 | 
					RT_DLLEXPORT void
 | 
				
			||||||
 | 
					rtMinheapUpdate(rt_minheap *minheap, const void *value, int new_key, rt_minheap_cmp_fn *cmp) {
 | 
				
			||||||
 | 
					    if (!cmp)
 | 
				
			||||||
 | 
					        cmp = memcmp;
 | 
				
			||||||
 | 
					    for (int i = 0; i < minheap->size; ++i) {
 | 
				
			||||||
 | 
					        void *v = (char *)minheap->values + (size_t)i * minheap->value_size;
 | 
				
			||||||
 | 
					        if (cmp(v, value, minheap->value_size) == 0) {
 | 
				
			||||||
 | 
					            if (minheap->keys[i] > new_key) {
 | 
				
			||||||
 | 
					                Decrease(minheap, i, new_key);
 | 
				
			||||||
 | 
					            } else if (minheap->keys[i] < new_key) {
 | 
				
			||||||
 | 
					                Remove(minheap, i);
 | 
				
			||||||
 | 
					                rtMinheapPush(minheap, new_key, value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rtMinheapPush(minheap, new_key, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -22,7 +22,7 @@ extern "C" rt_shader_bytecode CompileVulkanShader(rt_shader_stage stage,
 | 
				
			|||||||
    // Check if this is what we want.
 | 
					    // Check if this is what we want.
 | 
				
			||||||
    // For example: 6_2 is what allows the usage of 16 bit types
 | 
					    // For example: 6_2 is what allows the usage of 16 bit types
 | 
				
			||||||
    LPCWSTR target_profile = nullptr;
 | 
					    LPCWSTR target_profile = nullptr;
 | 
				
			||||||
    LPWSTR entry           = nullptr;
 | 
					    LPCWSTR entry           = nullptr;
 | 
				
			||||||
    switch (stage) {
 | 
					    switch (stage) {
 | 
				
			||||||
    case RT_SHADER_STAGE_VERTEX:
 | 
					    case RT_SHADER_STAGE_VERTEX:
 | 
				
			||||||
        target_profile = L"vs_6_1";
 | 
					        target_profile = L"vs_6_1";
 | 
				
			||||||
@ -41,7 +41,7 @@ extern "C" rt_shader_bytecode CompileVulkanShader(rt_shader_stage stage,
 | 
				
			|||||||
        return bc;
 | 
					        return bc;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    LPWSTR optimization_arg = nullptr;
 | 
					    LPCWSTR optimization_arg = nullptr;
 | 
				
			||||||
    switch (optimization) {
 | 
					    switch (optimization) {
 | 
				
			||||||
    case RT_SHADER_OPTIMIZATION_NONE:
 | 
					    case RT_SHADER_OPTIMIZATION_NONE:
 | 
				
			||||||
        optimization_arg = L"-Od";
 | 
					        optimization_arg = L"-Od";
 | 
				
			||||||
@ -81,7 +81,7 @@ extern "C" rt_shader_bytecode CompileVulkanShader(rt_shader_stage stage,
 | 
				
			|||||||
        utils->Release();
 | 
					        utils->Release();
 | 
				
			||||||
        compiler->Release();
 | 
					        compiler->Release();
 | 
				
			||||||
        library->Release();
 | 
					        library->Release();
 | 
				
			||||||
        rtReportError("AC", "Failed to init the DXC ínclude handler.");
 | 
					        rtReportError("AC", "Failed to init the DXC include handler.");
 | 
				
			||||||
        return bc;
 | 
					        return bc;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -118,8 +118,9 @@ extern "C" rt_shader_bytecode CompileVulkanShader(rt_shader_stage stage,
 | 
				
			|||||||
        // Error occured
 | 
					        // Error occured
 | 
				
			||||||
        IDxcBlobEncoding *error_blob;
 | 
					        IDxcBlobEncoding *error_blob;
 | 
				
			||||||
        hr = result->GetErrorBuffer(&error_blob);
 | 
					        hr = result->GetErrorBuffer(&error_blob);
 | 
				
			||||||
        if (SUCCEEDED(hr) && error_blob) {
 | 
					        if (SUCCEEDED(hr) && error_blob && error_blob->GetBufferSize() > 0) {
 | 
				
			||||||
            rtLog("AC", "Shader %s compilation failed: %s", (const char *)error_blob->GetBufferPointer());
 | 
					            const char *msg = (const char *)error_blob->GetBufferPointer();
 | 
				
			||||||
 | 
					            rtLog("AC", "Shader %s compilation failed: %s", file_path, msg);
 | 
				
			||||||
            error_blob->Release();
 | 
					            error_blob->Release();
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            rtLog("AC", "Shader %s compilation failed. No error information available!");
 | 
					            rtLog("AC", "Shader %s compilation failed. No error information available!");
 | 
				
			||||||
 | 
				
			|||||||
@ -57,12 +57,12 @@ static void LogOut(const char *text) {
 | 
				
			|||||||
RT_DLLEXPORT void rtReportError(const char *subsystem, const char *fmt, ...) {
 | 
					RT_DLLEXPORT void rtReportError(const char *subsystem, const char *fmt, ...) {
 | 
				
			||||||
    char buf[256];
 | 
					    char buf[256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int at = snprintf(buf, RT_ARRAY_COUNT(buf) - 1, "[%s] ", subsystem);
 | 
					    int at = rtSPrint(buf, RT_ARRAY_COUNT(buf) - 1, "[%s] ", subsystem);
 | 
				
			||||||
    va_list ap;
 | 
					    va_list ap;
 | 
				
			||||||
    va_start(ap, fmt);
 | 
					    va_start(ap, fmt);
 | 
				
			||||||
    at += vsnprintf(&buf[at], RT_ARRAY_COUNT(buf) - at - 1, fmt, ap);
 | 
					    at += rtVSPrint(&buf[at], RT_ARRAY_COUNT(buf) - at - 1, fmt, ap);
 | 
				
			||||||
    va_end(ap);
 | 
					    va_end(ap);
 | 
				
			||||||
    at += snprintf(&buf[at], RT_ARRAY_COUNT(buf) - at - 1, "\n");
 | 
					    at += rtSPrint(&buf[at], RT_ARRAY_COUNT(buf) - at - 1, "\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    LogOut(buf);
 | 
					    LogOut(buf);
 | 
				
			||||||
    if (DisplayErrorBox(buf)) {
 | 
					    if (DisplayErrorBox(buf)) {
 | 
				
			||||||
@ -73,12 +73,12 @@ RT_DLLEXPORT void rtReportError(const char *subsystem, const char *fmt, ...) {
 | 
				
			|||||||
RT_DLLEXPORT void rtLog(const char *subsystem, const char *fmt, ...) {
 | 
					RT_DLLEXPORT void rtLog(const char *subsystem, const char *fmt, ...) {
 | 
				
			||||||
    char buf[256];
 | 
					    char buf[256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int at = snprintf(buf, RT_ARRAY_COUNT(buf) - 1, "[%s] ", subsystem);
 | 
					    int at = rtSPrint(buf, RT_ARRAY_COUNT(buf), "[%s] ", subsystem);
 | 
				
			||||||
    va_list ap;
 | 
					    va_list ap;
 | 
				
			||||||
    va_start(ap, fmt);
 | 
					    va_start(ap, fmt);
 | 
				
			||||||
    at += vsnprintf(&buf[at], RT_ARRAY_COUNT(buf) - at - 1, fmt, ap);
 | 
					    at += rtVSPrint(&buf[at], RT_ARRAY_COUNT(buf) - at, fmt, ap);
 | 
				
			||||||
    va_end(ap);
 | 
					    va_end(ap);
 | 
				
			||||||
    at += snprintf(&buf[at], RT_ARRAY_COUNT(buf) - at - 1, "\n");
 | 
					    at += rtSPrint(&buf[at], RT_ARRAY_COUNT(buf) - at, "\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    LogOut(buf);
 | 
					    LogOut(buf);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -84,6 +84,12 @@ RT_DLLEXPORT void rtCloseDirectory(rt_scandir_handle *dir) {
 | 
				
			|||||||
    dir->handle = NULL;
 | 
					    dir->handle = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT bool rtCreateDirectory(const char *path) {
 | 
				
			||||||
 | 
					    WCHAR wpath[MAX_PATH];
 | 
				
			||||||
 | 
					    MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH);
 | 
				
			||||||
 | 
					    return CreateDirectoryW(wpath, NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT size_t rtGetFileSize(const char *path) {
 | 
					RT_DLLEXPORT size_t rtGetFileSize(const char *path) {
 | 
				
			||||||
    WCHAR wpath[MAX_PATH];
 | 
					    WCHAR wpath[MAX_PATH];
 | 
				
			||||||
    MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH);
 | 
					    MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH);
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,8 @@ RT_DLLEXPORT rt_dirent rtNextDirectoryEntry(rt_scandir_handle *dir);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT void rtCloseDirectory(rt_scandir_handle *dir);
 | 
					RT_DLLEXPORT void rtCloseDirectory(rt_scandir_handle *dir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT bool rtCreateDirectory(const char *path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT size_t rtGetFileSize(const char *path);
 | 
					RT_DLLEXPORT size_t rtGetFileSize(const char *path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT uint64_t rtGetFileModificationTimestamp(const char *path);
 | 
					RT_DLLEXPORT uint64_t rtGetFileModificationTimestamp(const char *path);
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,9 @@ extern rt_cvar rt_WindowHeight;
 | 
				
			|||||||
extern rt_cvar rt_BufferMemoryBudget;
 | 
					extern rt_cvar rt_BufferMemoryBudget;
 | 
				
			||||||
extern rt_cvar rt_FileTabCapacity;
 | 
					extern rt_cvar rt_FileTabCapacity;
 | 
				
			||||||
extern rt_cvar rt_MaxConcurrentAsyncIO;
 | 
					extern rt_cvar rt_MaxConcurrentAsyncIO;
 | 
				
			||||||
 | 
					extern rt_cvar rt_ResourceDirectory;
 | 
				
			||||||
 | 
					extern rt_cvar rt_ResourceCacheSize;
 | 
				
			||||||
 | 
					extern rt_cvar rt_ResourceNamespaceSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef RT_BUILD_ASSET_COMPILER
 | 
					#ifdef RT_BUILD_ASSET_COMPILER
 | 
				
			||||||
extern rt_cvar rt_AssetDirectory;
 | 
					extern rt_cvar rt_AssetDirectory;
 | 
				
			||||||
@ -24,6 +27,9 @@ void RegisterRuntimeCVars(void) {
 | 
				
			|||||||
    rtRegisterCVAR(&rt_BufferMemoryBudget);
 | 
					    rtRegisterCVAR(&rt_BufferMemoryBudget);
 | 
				
			||||||
    rtRegisterCVAR(&rt_FileTabCapacity);
 | 
					    rtRegisterCVAR(&rt_FileTabCapacity);
 | 
				
			||||||
    rtRegisterCVAR(&rt_MaxConcurrentAsyncIO);
 | 
					    rtRegisterCVAR(&rt_MaxConcurrentAsyncIO);
 | 
				
			||||||
 | 
					    rtRegisterCVAR(&rt_ResourceDirectory);
 | 
				
			||||||
 | 
					    rtRegisterCVAR(&rt_ResourceCacheSize);
 | 
				
			||||||
 | 
					    rtRegisterCVAR(&rt_ResourceNamespaceSize);
 | 
				
			||||||
#ifdef RT_BUILD_ASSET_COMPILER
 | 
					#ifdef RT_BUILD_ASSET_COMPILER
 | 
				
			||||||
    rtRegisterCVAR(&rt_AssetDirectory);
 | 
					    rtRegisterCVAR(&rt_AssetDirectory);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@ -37,6 +43,8 @@ extern rt_result InitFileTab(void);
 | 
				
			|||||||
extern void ShutdownFileTab(void);
 | 
					extern void ShutdownFileTab(void);
 | 
				
			||||||
extern rt_result InitAIO(void);
 | 
					extern rt_result InitAIO(void);
 | 
				
			||||||
extern void ShutdownAIO(void);
 | 
					extern void ShutdownAIO(void);
 | 
				
			||||||
 | 
					extern rt_result InitResourceManager(void);
 | 
				
			||||||
 | 
					extern void ShutdownResourceManager(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef RT_BUILD_ASSET_COMPILER
 | 
					#ifdef RT_BUILD_ASSET_COMPILER
 | 
				
			||||||
extern rt_result InitAssetCompiler(void);
 | 
					extern rt_result InitAssetCompiler(void);
 | 
				
			||||||
@ -63,6 +71,11 @@ RT_DLLEXPORT rt_result rtInitRuntime(void) {
 | 
				
			|||||||
        return res;
 | 
					        return res;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((res = InitResourceManager()) != RT_SUCCESS) {
 | 
				
			||||||
 | 
					        rtReportError("RESMGR", "Init failed.");
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef RT_BUILD_ASSET_COMPILER
 | 
					#ifdef RT_BUILD_ASSET_COMPILER
 | 
				
			||||||
    if ((res = InitAssetCompiler()) != RT_SUCCESS) {
 | 
					    if ((res = InitAssetCompiler()) != RT_SUCCESS) {
 | 
				
			||||||
        rtReportError("AC", "Init failed.");
 | 
					        rtReportError("AC", "Init failed.");
 | 
				
			||||||
@ -77,6 +90,7 @@ RT_DLLEXPORT void rtShutdownRuntime(void) {
 | 
				
			|||||||
#ifdef RT_BUILD_ASSET_COMPILER
 | 
					#ifdef RT_BUILD_ASSET_COMPILER
 | 
				
			||||||
    ShutdownAssetCompiler();
 | 
					    ShutdownAssetCompiler();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					    ShutdownResourceManager();
 | 
				
			||||||
    ShutdownAIO();
 | 
					    ShutdownAIO();
 | 
				
			||||||
    ShutdownFileTab();
 | 
					    ShutdownFileTab();
 | 
				
			||||||
    ShutdownBufferManager();
 | 
					    ShutdownBufferManager();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,14 @@
 | 
				
			|||||||
#include "runtime.h"
 | 
					 | 
				
			||||||
#include "mem_arena.h"
 | 
					 | 
				
			||||||
#include "description_parser.h"
 | 
					 | 
				
			||||||
#include "buffer_manager.h"
 | 
					 | 
				
			||||||
#include "asset_compiler.h"
 | 
					#include "asset_compiler.h"
 | 
				
			||||||
#include "shader_compiler.h"
 | 
					#include "buffer_manager.h"
 | 
				
			||||||
#include "gfx.h"
 | 
					 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "description_parser.h"
 | 
				
			||||||
 | 
					#include "gfx.h"
 | 
				
			||||||
 | 
					#include "mem_arena.h"
 | 
				
			||||||
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					#include "shader_compiler.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <limits.h>
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <limits.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
@ -16,9 +16,13 @@ typedef struct {
 | 
				
			|||||||
    rt_attribute_binding *storage_bindings;
 | 
					    rt_attribute_binding *storage_bindings;
 | 
				
			||||||
    rt_attribute_binding *texture_bindings;
 | 
					    rt_attribute_binding *texture_bindings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rt_shader_bytecode vertex_shader;
 | 
					    rt_resource shaders[3];
 | 
				
			||||||
    rt_shader_bytecode fragment_shader;
 | 
					    char *shader_names[3];
 | 
				
			||||||
    rt_shader_bytecode compute_shader;
 | 
					    unsigned int shader_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsigned int vertex_shader;
 | 
				
			||||||
 | 
					    unsigned int fragment_shader;
 | 
				
			||||||
 | 
					    unsigned int compute_shader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* TODO Fixed function settings */
 | 
					    /* TODO Fixed function settings */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -144,6 +148,58 @@ static bool ParseBindings(rt_parse_state *state,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static char *GenerateShaderName(rt_shader_type type,
 | 
				
			||||||
 | 
					                                rt_shader_stage stage,
 | 
				
			||||||
 | 
					                                rt_shader_optimization_level optimization,
 | 
				
			||||||
 | 
					                                const char *file_name,
 | 
				
			||||||
 | 
					                                rt_arena *arena) {
 | 
				
			||||||
 | 
					    size_t name_len = strlen(file_name) + 5 /* type */
 | 
				
			||||||
 | 
					                      + 5                   /* stage */
 | 
				
			||||||
 | 
					                      + 3                   /* optimization */
 | 
				
			||||||
 | 
					                      + 1;                  /* '\0' */
 | 
				
			||||||
 | 
					    char *res_name = rtArenaPush(arena, name_len);
 | 
				
			||||||
 | 
					    if (!res_name)
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    const char *type_str = NULL;
 | 
				
			||||||
 | 
					    switch (type) {
 | 
				
			||||||
 | 
					    case RT_SHADER_TYPE_VULKAN:
 | 
				
			||||||
 | 
					        type_str = ":vk";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const char *stage_str = NULL;
 | 
				
			||||||
 | 
					    switch (stage) {
 | 
				
			||||||
 | 
					    case RT_SHADER_STAGE_VERTEX:
 | 
				
			||||||
 | 
					        stage_str = ":vert";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case RT_SHADER_STAGE_FRAGMENT:
 | 
				
			||||||
 | 
					        stage_str = ":frag";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case RT_SHADER_STAGE_COMPUTE:
 | 
				
			||||||
 | 
					        stage_str = ":comp";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const char *optim_str = NULL;
 | 
				
			||||||
 | 
					    switch (optimization) {
 | 
				
			||||||
 | 
					    case RT_SHADER_OPTIMIZATION_NONE:
 | 
				
			||||||
 | 
					        optim_str = ":O0";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case RT_SHADER_OPTIMIZATION_SIZE:
 | 
				
			||||||
 | 
					        optim_str = ":Os";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case RT_SHADER_OPTIMIZATION_SPEED:
 | 
				
			||||||
 | 
					        optim_str = ":Ox";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rtSPrint(res_name, name_len, "%s%s%s%s", file_name, type_str, stage_str, optim_str);
 | 
				
			||||||
 | 
					    return res_name;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static rt_result ParseShader(rt_parse_state *state,
 | 
					static rt_result ParseShader(rt_parse_state *state,
 | 
				
			||||||
                             unsigned int root_list,
 | 
					                             unsigned int root_list,
 | 
				
			||||||
                             const char *name,
 | 
					                             const char *name,
 | 
				
			||||||
@ -151,7 +207,8 @@ static rt_result ParseShader(rt_parse_state *state,
 | 
				
			|||||||
                             rt_shader_type type,
 | 
					                             rt_shader_type type,
 | 
				
			||||||
                             rt_shader_stage stage,
 | 
					                             rt_shader_stage stage,
 | 
				
			||||||
                             rt_shader_optimization_level optimization,
 | 
					                             rt_shader_optimization_level optimization,
 | 
				
			||||||
                             rt_shader_bytecode *p_shader_bytecode,
 | 
					                             rt_resource *p_resource,
 | 
				
			||||||
 | 
					                             char **p_resource_name,
 | 
				
			||||||
                             rt_arena *arena) {
 | 
					                             rt_arena *arena) {
 | 
				
			||||||
    const rt_parsed_stmt *stmt = rtFindStatement(state, root_list, name);
 | 
					    const rt_parsed_stmt *stmt = rtFindStatement(state, root_list, name);
 | 
				
			||||||
    if (stmt) {
 | 
					    if (stmt) {
 | 
				
			||||||
@ -195,10 +252,32 @@ static rt_result ParseShader(rt_parse_state *state,
 | 
				
			|||||||
            if (in_file_type == type) {
 | 
					            if (in_file_type == type) {
 | 
				
			||||||
                if (shader->form == RT_STMT_FORM_BLOCK) {
 | 
					                if (shader->form == RT_STMT_FORM_BLOCK) {
 | 
				
			||||||
                    /* Inline code */
 | 
					                    /* Inline code */
 | 
				
			||||||
                    *p_shader_bytecode =
 | 
					                    rt_shader_bytecode bytecode =
 | 
				
			||||||
                        CompileShader(type, stage, optimization, shader->block, file_path, arena);
 | 
					                        CompileShader(type, stage, optimization, shader->block, file_path, arena);
 | 
				
			||||||
                    if (!p_shader_bytecode->bytes)
 | 
					                    if (!bytecode.bytes)
 | 
				
			||||||
                        return RT_ASSET_PROCESSING_FAILED;
 | 
					                        return RT_ASSET_PROCESSING_FAILED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    *p_resource_name =
 | 
				
			||||||
 | 
					                        GenerateShaderName(type, stage, optimization, file_path, arena);
 | 
				
			||||||
 | 
					                    if (!*p_resource_name)
 | 
				
			||||||
 | 
					                        return RT_ASSET_PROCESSING_FAILED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    rt_resource resource;
 | 
				
			||||||
 | 
					                    resource.type              = RT_RESOURCE_SHADER;
 | 
				
			||||||
 | 
					                    resource.dependency_count  = 0;
 | 
				
			||||||
 | 
					                    resource.subresource_count = 0;
 | 
				
			||||||
 | 
					                    resource.data = rtArenaPush(arena, sizeof(rt_shader_info) + bytecode.len);
 | 
				
			||||||
 | 
					                    if (!resource.data)
 | 
				
			||||||
 | 
					                        return RT_ASSET_PROCESSING_FAILED;
 | 
				
			||||||
 | 
					                    rt_shader_info *shader_info  = resource.data;
 | 
				
			||||||
 | 
					                    uint8_t *shader_bytecode     = (uint8_t *)(shader_info + 1);
 | 
				
			||||||
 | 
					                    shader_info->stage           = stage;
 | 
				
			||||||
 | 
					                    shader_info->type            = type;
 | 
				
			||||||
 | 
					                    shader_info->bytecode_length = bytecode.len;
 | 
				
			||||||
 | 
					                    rtSetRelptr(&shader_info->bytecode, shader_bytecode);
 | 
				
			||||||
 | 
					                    memcpy(shader_bytecode, bytecode.bytes, bytecode.len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    memcpy(p_resource, &resource, sizeof(resource));
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                } else if (shader->form != RT_STMT_FORM_VALUE) {
 | 
					                } else if (shader->form != RT_STMT_FORM_VALUE) {
 | 
				
			||||||
                    /* A filename */
 | 
					                    /* A filename */
 | 
				
			||||||
@ -262,7 +341,8 @@ static rt_result ParsePipelineFile(rt_file_id fid,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* We allow the pipeline file to overwrite the optimization level */
 | 
					    /* We allow the pipeline file to overwrite the optimization level */
 | 
				
			||||||
    rt_shader_optimization_level optimization = ParseOptimizationLevel(&state, root_list, file_path);
 | 
					    rt_shader_optimization_level optimization =
 | 
				
			||||||
 | 
					        ParseOptimizationLevel(&state, root_list, file_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rt_shader_type type = RT_SHADER_TYPE_INVALID;
 | 
					    rt_shader_type type = RT_SHADER_TYPE_INVALID;
 | 
				
			||||||
    if (strcmp(rt_Renderer.s, "vk") == 0)
 | 
					    if (strcmp(rt_Renderer.s, "vk") == 0)
 | 
				
			||||||
@ -275,42 +355,65 @@ static rt_result ParsePipelineFile(rt_file_id fid,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Process shader stages */
 | 
					    /* Process shader stages */
 | 
				
			||||||
    if (ParseShader(&state,
 | 
					    result = ParseShader(&state,
 | 
				
			||||||
                         root_list,
 | 
					                         root_list,
 | 
				
			||||||
                         "vertex",
 | 
					                         "vertex",
 | 
				
			||||||
                         file_path,
 | 
					                         file_path,
 | 
				
			||||||
                         type,
 | 
					                         type,
 | 
				
			||||||
                         RT_SHADER_STAGE_VERTEX,
 | 
					                         RT_SHADER_STAGE_VERTEX,
 | 
				
			||||||
                         optimization,
 | 
					                         optimization,
 | 
				
			||||||
                    &pipeline->vertex_shader,
 | 
					                         &pipeline->shaders[pipeline->shader_count],
 | 
				
			||||||
                    arena) == RT_ASSET_PROCESSING_FAILED) {
 | 
					                         &pipeline->shader_names[pipeline->shader_count],
 | 
				
			||||||
        result = RT_ASSET_PROCESSING_FAILED;
 | 
					                         arena);
 | 
				
			||||||
 | 
					    if (result == RT_SUCCESS) {
 | 
				
			||||||
 | 
					        pipeline->vertex_shader = pipeline->shader_count;
 | 
				
			||||||
 | 
					        ++pipeline->shader_count;
 | 
				
			||||||
 | 
					    } else if (result == RT_SHADER_NOT_PRESENT) {
 | 
				
			||||||
 | 
					        pipeline->vertex_shader = UINT_MAX;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (ParseShader(&state,
 | 
					    result = RT_SUCCESS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result = ParseShader(&state,
 | 
				
			||||||
                         root_list,
 | 
					                         root_list,
 | 
				
			||||||
                         "fragment",
 | 
					                         "fragment",
 | 
				
			||||||
                         file_path,
 | 
					                         file_path,
 | 
				
			||||||
                         type,
 | 
					                         type,
 | 
				
			||||||
                         RT_SHADER_STAGE_FRAGMENT,
 | 
					                         RT_SHADER_STAGE_FRAGMENT,
 | 
				
			||||||
                         optimization,
 | 
					                         optimization,
 | 
				
			||||||
                    &pipeline->fragment_shader,
 | 
					                         &pipeline->shaders[pipeline->shader_count],
 | 
				
			||||||
                    arena) == RT_ASSET_PROCESSING_FAILED) {
 | 
					                         &pipeline->shader_names[pipeline->shader_count],
 | 
				
			||||||
        result = RT_ASSET_PROCESSING_FAILED;
 | 
					                         arena);
 | 
				
			||||||
 | 
					    if (result == RT_SUCCESS) {
 | 
				
			||||||
 | 
					        pipeline->fragment_shader = pipeline->shader_count;
 | 
				
			||||||
 | 
					        ++pipeline->shader_count;
 | 
				
			||||||
 | 
					    } else if (result == RT_SHADER_NOT_PRESENT) {
 | 
				
			||||||
 | 
					        pipeline->fragment_shader = UINT_MAX;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (ParseShader(&state,
 | 
					    result = RT_SUCCESS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result = ParseShader(&state,
 | 
				
			||||||
                         root_list,
 | 
					                         root_list,
 | 
				
			||||||
                         "compute",
 | 
					                         "compute",
 | 
				
			||||||
                         file_path,
 | 
					                         file_path,
 | 
				
			||||||
                         type,
 | 
					                         type,
 | 
				
			||||||
                         RT_SHADER_STAGE_COMPUTE,
 | 
					                         RT_SHADER_STAGE_COMPUTE,
 | 
				
			||||||
                         optimization,
 | 
					                         optimization,
 | 
				
			||||||
                    &pipeline->compute_shader,
 | 
					                         &pipeline->shaders[pipeline->shader_count],
 | 
				
			||||||
                    arena) == RT_ASSET_PROCESSING_FAILED) {
 | 
					                         &pipeline->shader_names[pipeline->shader_count],
 | 
				
			||||||
        result = RT_ASSET_PROCESSING_FAILED;
 | 
					                         arena);
 | 
				
			||||||
 | 
					    if (result == RT_SUCCESS) {
 | 
				
			||||||
 | 
					        pipeline->compute_shader = pipeline->shader_count;
 | 
				
			||||||
 | 
					        ++pipeline->shader_count;
 | 
				
			||||||
 | 
					    } else if (result == RT_SHADER_NOT_PRESENT) {
 | 
				
			||||||
 | 
					        pipeline->compute_shader = UINT_MAX;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    result = RT_SUCCESS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Process bindings */
 | 
					    /* Process bindings */
 | 
				
			||||||
    pipeline->texture_bindings      = NULL;
 | 
					    pipeline->texture_bindings      = NULL;
 | 
				
			||||||
@ -354,18 +457,74 @@ out:
 | 
				
			|||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rt_result PipelineProcessor(rt_file_id file, rt_arena *arena) {
 | 
					RT_ASSET_PROCESSOR_FN(PipelineProcessor) {
 | 
				
			||||||
    rt_loaded_asset asset = LoadAsset(file);
 | 
					    rt_loaded_asset asset = LoadAsset(file);
 | 
				
			||||||
    if (!asset.buffer)
 | 
					    if (!asset.buffer)
 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					        return RT_UNKNOWN_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rt_parsed_pipeline_data pipeline;
 | 
					    rt_parsed_pipeline_data pipeline;
 | 
				
			||||||
 | 
					    memset(&pipeline, 0, sizeof(pipeline));
 | 
				
			||||||
    rt_result result = ParsePipelineFile(file, asset.buffer, asset.size, &pipeline, arena);
 | 
					    rt_result result = ParsePipelineFile(file, asset.buffer, asset.size, &pipeline, arena);
 | 
				
			||||||
    if (result != RT_SUCCESS)
 | 
					    if (result != RT_SUCCESS)
 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt_resource_id shader_resources[3] = {0};
 | 
				
			||||||
 | 
					    result                             = rtCreateResources(pipeline.shader_count,
 | 
				
			||||||
 | 
					                               pipeline.shader_names,
 | 
				
			||||||
 | 
					                               pipeline.shaders,
 | 
				
			||||||
 | 
					                               shader_resources);
 | 
				
			||||||
 | 
					    if (result != RT_SUCCESS)
 | 
				
			||||||
 | 
					        goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt_resource pipeline_resource      = {0};
 | 
				
			||||||
 | 
					    pipeline_resource.type             = RT_RESOURCE_PIPELINE;
 | 
				
			||||||
 | 
					    pipeline_resource.dependency_count = pipeline.shader_count;
 | 
				
			||||||
 | 
					    memcpy(pipeline_resource.dependencies, shader_resources, sizeof(shader_resources));
 | 
				
			||||||
 | 
					    pipeline_resource.subresource_count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_t data_size =
 | 
				
			||||||
 | 
					        sizeof(rt_pipeline_info) + sizeof(rt_attribute_binding) * (pipeline.texture_binding_count +
 | 
				
			||||||
 | 
					                                                                   pipeline.uniform_binding_count +
 | 
				
			||||||
 | 
					                                                                   pipeline.storage_binding_count);
 | 
				
			||||||
 | 
					    pipeline_resource.data = rtArenaPush(arena, data_size);
 | 
				
			||||||
 | 
					    if (!pipeline_resource.data) {
 | 
				
			||||||
 | 
					        result = RT_OUT_OF_MEMORY;
 | 
				
			||||||
 | 
					        goto out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rt_pipeline_info *info = pipeline_resource.data;
 | 
				
			||||||
 | 
					    memset(info, 0, sizeof(*info));
 | 
				
			||||||
 | 
					    info->vertex_shader                    = (pipeline.vertex_shader != UINT_MAX)
 | 
				
			||||||
 | 
					                                                 ? shader_resources[pipeline.vertex_shader]
 | 
				
			||||||
 | 
					                                                 : RT_INVALID_RESOURCE_ID;
 | 
				
			||||||
 | 
					    info->fragment_shader                  = (pipeline.fragment_shader != UINT_MAX)
 | 
				
			||||||
 | 
					                                                 ? shader_resources[pipeline.fragment_shader]
 | 
				
			||||||
 | 
					                                                 : RT_INVALID_RESOURCE_ID;
 | 
				
			||||||
 | 
					    info->compute_shader                   = (pipeline.compute_shader != UINT_MAX)
 | 
				
			||||||
 | 
					                                                 ? shader_resources[pipeline.compute_shader]
 | 
				
			||||||
 | 
					                                                 : RT_INVALID_RESOURCE_ID;
 | 
				
			||||||
 | 
					    rt_attribute_binding *uniform_bindings = (rt_attribute_binding *)(info + 1);
 | 
				
			||||||
 | 
					    if (pipeline.uniform_binding_count > 0) {
 | 
				
			||||||
 | 
					        memcpy(uniform_bindings, pipeline.uniform_bindings, pipeline.uniform_binding_count);
 | 
				
			||||||
 | 
					        rtSetRelptr(&info->uniform_bindings, uniform_bindings);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rt_attribute_binding *texture_bindings = (uniform_bindings + pipeline.uniform_binding_count);
 | 
				
			||||||
 | 
					    if (pipeline.texture_binding_count > 0) {
 | 
				
			||||||
 | 
					        memcpy(texture_bindings, pipeline.texture_bindings, pipeline.texture_binding_count);
 | 
				
			||||||
 | 
					        rtSetRelptr(&info->texture_bindings, texture_bindings);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rt_attribute_binding *storage_bindings = (texture_bindings + pipeline.texture_binding_count);
 | 
				
			||||||
 | 
					    if (pipeline.texture_binding_count > 0) {
 | 
				
			||||||
 | 
					        memcpy(storage_bindings, pipeline.storage_bindings, pipeline.storage_binding_count);
 | 
				
			||||||
 | 
					        rtSetRelptr(&info->storage_bindings, storage_bindings);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rt_resource_id pipeline_id;
 | 
				
			||||||
 | 
					    const char *name = rtGetFilePath(file);
 | 
				
			||||||
 | 
					    result = rtCreateResources(1, &name, &pipeline_resource, &pipeline_id);
 | 
				
			||||||
 | 
					    if (result == RT_SUCCESS) {
 | 
				
			||||||
 | 
					        new_resources[0] = pipeline_id;
 | 
				
			||||||
 | 
					        memcpy(&new_resources[1], shader_resources, sizeof(shader_resources));
 | 
				
			||||||
 | 
					        *new_resource_count = 1 + pipeline.shader_count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
    rtReleaseBuffer(asset.buffer, asset.size);
 | 
					    rtReleaseBuffer(asset.buffer, asset.size);
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "gfx.h"
 | 
					#include "gfx.h"
 | 
				
			||||||
#include "runtime.h"
 | 
					#include "runtime.h"
 | 
				
			||||||
#include "assets.h"
 | 
					#include "resources.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
@ -31,10 +31,9 @@ struct rt_renderer_init_info_s {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    /* rt_uid vertex_shader;
 | 
					    rt_resource_id vertex_shader;
 | 
				
			||||||
    rt_uid fragment_shader;
 | 
					    rt_resource_id fragment_shader;
 | 
				
			||||||
    rt_uid compute_shader;
 | 
					    rt_resource_id compute_shader;
 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rt_relptr texture_bindings;
 | 
					    rt_relptr texture_bindings;
 | 
				
			||||||
    rt_relptr uniform_bindings;
 | 
					    rt_relptr uniform_bindings;
 | 
				
			||||||
@ -45,6 +44,26 @@ typedef struct {
 | 
				
			|||||||
    uint16_t storage_binding_count;
 | 
					    uint16_t storage_binding_count;
 | 
				
			||||||
} rt_pipeline_info;
 | 
					} rt_pipeline_info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    RT_SHADER_TYPE_INVALID,
 | 
				
			||||||
 | 
					    RT_SHADER_TYPE_VULKAN,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RT_SHADER_TYPE_COUNT,
 | 
				
			||||||
 | 
					} rt_shader_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    RT_SHADER_STAGE_VERTEX,
 | 
				
			||||||
 | 
					    RT_SHADER_STAGE_FRAGMENT,
 | 
				
			||||||
 | 
					    RT_SHADER_STAGE_COMPUTE,
 | 
				
			||||||
 | 
					} rt_shader_stage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    rt_shader_type type;
 | 
				
			||||||
 | 
					    rt_shader_stage stage;
 | 
				
			||||||
 | 
					    rt_relptr bytecode;
 | 
				
			||||||
 | 
					    size_t bytecode_length;
 | 
				
			||||||
 | 
					} rt_shader_info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef void rt_register_renderer_cvars_fn(void);
 | 
					typedef void rt_register_renderer_cvars_fn(void);
 | 
				
			||||||
typedef rt_result rt_init_renderer_fn(const rt_renderer_init_info *info);
 | 
					typedef rt_result rt_init_renderer_fn(const rt_renderer_init_info *info);
 | 
				
			||||||
typedef void rt_shutdown_renderer_fn(void);
 | 
					typedef void rt_shutdown_renderer_fn(void);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										444
									
								
								src/runtime/resource_manager.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								src/runtime/resource_manager.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,444 @@
 | 
				
			|||||||
 | 
					#include "aio.h"
 | 
				
			||||||
 | 
					#include "buffer_manager.h"
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "ds.h"
 | 
				
			||||||
 | 
					#include "file_tab.h"
 | 
				
			||||||
 | 
					#include "fsutils.h"
 | 
				
			||||||
 | 
					#include "hashing.h"
 | 
				
			||||||
 | 
					#include "renderer_api.h"
 | 
				
			||||||
 | 
					#include "resources.h"
 | 
				
			||||||
 | 
					#include "threading.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_CVAR_S(rt_ResourceDirectory, "The directory used for storing resources. Default: res", "res");
 | 
				
			||||||
 | 
					RT_CVAR_I(rt_ResourceCacheSize,
 | 
				
			||||||
 | 
					          "The maximum amount of memory used for caching resources. Default: 512MB",
 | 
				
			||||||
 | 
					          RT_MB(512));
 | 
				
			||||||
 | 
					RT_CVAR_I(rt_MaxCachedResources,
 | 
				
			||||||
 | 
					          "The maximum number of simultaneously cached resources. Default: 1024",
 | 
				
			||||||
 | 
					          1024);
 | 
				
			||||||
 | 
					RT_CVAR_I(rt_ResourceNamespaceSize,
 | 
				
			||||||
 | 
					          "The maximum number of resources that can exist. Default: 1.048.576",
 | 
				
			||||||
 | 
					          1048576);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RT_TOMBSTONE_ID 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    void *buffer;
 | 
				
			||||||
 | 
					    size_t size;
 | 
				
			||||||
 | 
					    int next_free;
 | 
				
			||||||
 | 
					    int usage_counter;
 | 
				
			||||||
 | 
					} rt_cached_resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    unsigned int index;
 | 
				
			||||||
 | 
					    rt_resource_id id;
 | 
				
			||||||
 | 
					} rt_cached_resource_ref;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    void *mem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt_minheap reclaim_heap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Used to lookup cached resources by id */
 | 
				
			||||||
 | 
					    rt_resource_id *resource_ids;
 | 
				
			||||||
 | 
					    unsigned int *resource_indices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt_cached_resource *resources;
 | 
				
			||||||
 | 
					    unsigned int first_free;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_t current_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt_rwlock lock;
 | 
				
			||||||
 | 
					} rt_resource_cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    rt_file_id file;
 | 
				
			||||||
 | 
					    size_t offset;
 | 
				
			||||||
 | 
					    size_t size;
 | 
				
			||||||
 | 
					} rt_resource_ref;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    rt_resource_id *ids;
 | 
				
			||||||
 | 
					    rt_resource_ref *refs;
 | 
				
			||||||
 | 
					    rt_rwlock lock;
 | 
				
			||||||
 | 
					} rt_resource_namespace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ~~~ Utilities ~~~ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static size_t GetResourceDataSize(const rt_resource *resource) {
 | 
				
			||||||
 | 
					    switch (resource->type) {
 | 
				
			||||||
 | 
					    case RT_RESOURCE_PIPELINE:
 | 
				
			||||||
 | 
					        return sizeof(rt_pipeline_info);
 | 
				
			||||||
 | 
					    case RT_RESOURCE_SHADER: {
 | 
				
			||||||
 | 
					        /* Sizeof metadata + bytecode */
 | 
				
			||||||
 | 
					        const rt_shader_info *info = resource->data;
 | 
				
			||||||
 | 
					        return sizeof(rt_shader_info) + (info) ? info->bytecode_length : 0;
 | 
				
			||||||
 | 
					    } break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        rtLog("RESMGR", "Tried to get size of an invalid resource type %u", resource->type);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void CopyResourceData(const rt_resource *resource, void *dest) {
 | 
				
			||||||
 | 
					    switch (resource->type) {
 | 
				
			||||||
 | 
					    case RT_RESOURCE_PIPELINE:
 | 
				
			||||||
 | 
					        memcpy(dest, resource->data, sizeof(rt_pipeline_info));
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case RT_RESOURCE_SHADER: {
 | 
				
			||||||
 | 
					        /* Sizeof metadata + bytecode */
 | 
				
			||||||
 | 
					        const rt_shader_info *info = resource->data;
 | 
				
			||||||
 | 
					        rt_shader_info *dest_info  = dest;
 | 
				
			||||||
 | 
					        memcpy(dest_info, info, sizeof(*info));
 | 
				
			||||||
 | 
					        memcpy(dest_info + 1, rtResolveConstRelptr(&info->bytecode), info->bytecode_length);
 | 
				
			||||||
 | 
					        rtSetRelptr(&dest_info->bytecode, (void *)(dest_info + 1));
 | 
				
			||||||
 | 
					    } break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        rtLog("RESMGR", "Tried to get copy a resource of invalid type %u", resource->type);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ~~~ Cache ~~~ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static rt_resource_cache _cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static rt_result InitResourceCache(void) {
 | 
				
			||||||
 | 
					    int count = rt_MaxCachedResources.i;
 | 
				
			||||||
 | 
					    if (count == 0) {
 | 
				
			||||||
 | 
					        rtReportError("RESMGR", "rt_MaxCachedResources must be greater than 0.");
 | 
				
			||||||
 | 
					        return RT_INVALID_VALUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_t required_mem = (size_t)count * (sizeof(rt_cached_resource_ref) + sizeof(int) +
 | 
				
			||||||
 | 
					                                           sizeof(rt_cached_resource)) +
 | 
				
			||||||
 | 
					                          2 * (size_t)count * (sizeof(rt_resource_id) + sizeof(unsigned int));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void *mem = malloc(required_mem);
 | 
				
			||||||
 | 
					    if (!mem)
 | 
				
			||||||
 | 
					        return RT_OUT_OF_MEMORY;
 | 
				
			||||||
 | 
					    rt_create_rwlock_result lock_create = rtCreateRWLock();
 | 
				
			||||||
 | 
					    if (!lock_create.ok) {
 | 
				
			||||||
 | 
					        free(mem);
 | 
				
			||||||
 | 
					        return RT_UNKNOWN_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    memset(mem, 0, required_mem);
 | 
				
			||||||
 | 
					    _cache.mem = mem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int *reclaim_keys                    = mem;
 | 
				
			||||||
 | 
					    rt_cached_resource_ref *reclaim_refs = (rt_cached_resource_ref *)reclaim_keys + count;
 | 
				
			||||||
 | 
					    _cache.reclaim_heap                  = rtCreateMinheap(reclaim_keys,
 | 
				
			||||||
 | 
					                                          reclaim_refs,
 | 
				
			||||||
 | 
					                                          sizeof(rt_cached_resource_ref),
 | 
				
			||||||
 | 
					                                          (size_t)count,
 | 
				
			||||||
 | 
					                                          0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _cache.current_size = 0;
 | 
				
			||||||
 | 
					    _cache.resources    = (rt_cached_resource *)(reclaim_keys + count);
 | 
				
			||||||
 | 
					    _cache.lock         = lock_create.lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = 0; i < count; ++i) {
 | 
				
			||||||
 | 
					        _cache.resources[i].next_free = (i < count - 1) ? i + 1 : UINT_MAX;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    _cache.first_free = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _cache.resource_ids     = (rt_resource_id *)(_cache.resources + count);
 | 
				
			||||||
 | 
					    _cache.resource_indices = (unsigned int *)(_cache.resource_ids + 2 * count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return RT_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void ShutdownResourceCache(void) {
 | 
				
			||||||
 | 
					    free(_cache.mem);
 | 
				
			||||||
 | 
					    rtDestroyRWLock(&_cache.lock);
 | 
				
			||||||
 | 
					    memset(&_cache, 0, sizeof(_cache));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool FreeCacheSpace(size_t space) {
 | 
				
			||||||
 | 
					    size_t total_freed = 0;
 | 
				
			||||||
 | 
					    while (total_freed < space && !rtMinheapIsEmpty(&_cache.reclaim_heap)) {
 | 
				
			||||||
 | 
					        rt_cached_resource_ref ref;
 | 
				
			||||||
 | 
					        rtMinheapPop(&_cache.reclaim_heap, &ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rt_cached_resource *res = &_cache.resources[ref.index];
 | 
				
			||||||
 | 
					        rtReleaseBuffer(res->buffer, res->size);
 | 
				
			||||||
 | 
					        total_freed += res->size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        res->next_free     = _cache.first_free;
 | 
				
			||||||
 | 
					        _cache.first_free  = ref.index;
 | 
				
			||||||
 | 
					        res->usage_counter = 0;
 | 
				
			||||||
 | 
					        res->buffer        = NULL;
 | 
				
			||||||
 | 
					        res->size          = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Remove from lookup table */
 | 
				
			||||||
 | 
					        size_t ht_size = (size_t)rt_MaxCachedResources.i * 2;
 | 
				
			||||||
 | 
					        for (size_t off = 0; off < ht_size; ++off) {
 | 
				
			||||||
 | 
					            size_t slot = (ref.id + off) % ht_size;
 | 
				
			||||||
 | 
					            if (_cache.resource_ids[slot] == ref.id) {
 | 
				
			||||||
 | 
					                _cache.resource_ids[slot] = RT_TOMBSTONE_ID;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            } else if (_cache.resource_ids[slot] == RT_INVALID_RESOURCE_ID) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return total_freed >= space;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned int FindCachedResource(rt_resource_id id) {
 | 
				
			||||||
 | 
					    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] == id)
 | 
				
			||||||
 | 
					            return _cache.resource_indices[slot];
 | 
				
			||||||
 | 
					        else if (_cache.resource_ids[slot] == RT_INVALID_RESOURCE_ID)
 | 
				
			||||||
 | 
					            return UINT_MAX;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return UINT_MAX;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static rt_resource *CacheResource(rt_resource_id id, const rt_resource *res) {
 | 
				
			||||||
 | 
					    rt_resource *cached = NULL;
 | 
				
			||||||
 | 
					    rtLockWrite(&_cache.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;
 | 
				
			||||||
 | 
					        rtMinheapUpdate(&_cache.reclaim_heap, &ref, cache_entry->usage_counter, NULL);
 | 
				
			||||||
 | 
					        cached = cache_entry->buffer;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* Insert into cache */
 | 
				
			||||||
 | 
					        size_t total_size = sizeof(rt_resource) + GetResourceDataSize(res);
 | 
				
			||||||
 | 
					        if (_cache.current_size + total_size >= (size_t)rt_ResourceCacheSize.i) {
 | 
				
			||||||
 | 
					            if (!FreeCacheSpace(total_size)) {
 | 
				
			||||||
 | 
					                rtLog("RESMGR",
 | 
				
			||||||
 | 
					                      "Unable to reclaim %zu kB from the resource cache.",
 | 
				
			||||||
 | 
					                      total_size / 1024);
 | 
				
			||||||
 | 
					                rtUnlockWrite(&_cache.lock);
 | 
				
			||||||
 | 
					                return NULL;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            RT_ASSERT(_cache.first_free != UINT_MAX,
 | 
				
			||||||
 | 
					                      "There must be a free cache entry after space was freed.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        memcpy(buffer, res, sizeof(rt_resource));
 | 
				
			||||||
 | 
					        cached       = buffer;
 | 
				
			||||||
 | 
					        cached->data = (void *)(cached + 1);
 | 
				
			||||||
 | 
					        CopyResourceData(res, cached->data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        index                                 = _cache.first_free;
 | 
				
			||||||
 | 
					        _cache.first_free                     = _cache.resources[index].next_free;
 | 
				
			||||||
 | 
					        _cache.resources[index].buffer        = buffer;
 | 
				
			||||||
 | 
					        _cache.resources[index].usage_counter = 1;
 | 
				
			||||||
 | 
					        _cache.resources[index].size          = total_size;
 | 
				
			||||||
 | 
					        _cache.resources[index].next_free     = UINT_MAX;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rt_cached_resource_ref reclaim_ref = {
 | 
				
			||||||
 | 
					            .id    = id,
 | 
				
			||||||
 | 
					            .index = index,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        rtMinheapPush(&_cache.reclaim_heap, 1, &reclaim_ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* 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_indices[slot] = index;
 | 
				
			||||||
 | 
					                inserted                      = true;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!inserted) {
 | 
				
			||||||
 | 
					            rtReportError("RESMGR",
 | 
				
			||||||
 | 
					                          "Failed to insert created resource into the resource lookup table.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rtUnlockWrite(&_cache.lock);
 | 
				
			||||||
 | 
					    return cached;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ~~~ Resource Namespace ~~~ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static rt_resource_namespace _namespace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static rt_result InitResourceNamespace(void) {
 | 
				
			||||||
 | 
					    size_t size = (size_t)rt_ResourceNamespaceSize.i;
 | 
				
			||||||
 | 
					    if (size == 0) {
 | 
				
			||||||
 | 
					        rtReportError("RESMGR", "rt_ResourceNamespaceSize must be greater than 0.");
 | 
				
			||||||
 | 
					        return RT_INVALID_VALUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void *mem = calloc(size, sizeof(rt_resource_id) + sizeof(rt_resource_ref));
 | 
				
			||||||
 | 
					    if (!mem)
 | 
				
			||||||
 | 
					        return RT_OUT_OF_MEMORY;
 | 
				
			||||||
 | 
					    rt_create_rwlock_result lock_create = rtCreateRWLock();
 | 
				
			||||||
 | 
					    if (!lock_create.ok) {
 | 
				
			||||||
 | 
					        free(mem);
 | 
				
			||||||
 | 
					        return RT_UNKNOWN_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    _namespace.lock = lock_create.lock;
 | 
				
			||||||
 | 
					    _namespace.ids  = mem;
 | 
				
			||||||
 | 
					    _namespace.refs = (rt_resource_ref *)(_namespace.ids + size);
 | 
				
			||||||
 | 
					    return RT_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void ShutdownNamespace(void) {
 | 
				
			||||||
 | 
					    rtDestroyRWLock(&_namespace.lock);
 | 
				
			||||||
 | 
					    free(_namespace.ids);
 | 
				
			||||||
 | 
					    memset(&_namespace, 0, sizeof(_namespace));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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 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 ~~~ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rt_result InitResourceManager(void) {
 | 
				
			||||||
 | 
					    if (!rtCreateDirectory(rt_ResourceDirectory.s))
 | 
				
			||||||
 | 
					        rtLog("RESMGR", "CreateDirectory(%s) failed.", rt_ResourceDirectory.s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt_result res;
 | 
				
			||||||
 | 
					    if ((res = InitResourceCache()) != RT_SUCCESS)
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    if ((res = InitResourceNamespace()) != RT_SUCCESS) {
 | 
				
			||||||
 | 
					        ShutdownResourceCache();
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return RT_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ShutdownResourceManager(void) {
 | 
				
			||||||
 | 
					    ShutdownResourceCache();
 | 
				
			||||||
 | 
					    ShutdownNamespace();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
 | 
				
			||||||
 | 
					                                         const char **names,
 | 
				
			||||||
 | 
					                                         const rt_resource *resources,
 | 
				
			||||||
 | 
					                                         rt_resource_id *ids) {
 | 
				
			||||||
 | 
					    rt_result result = RT_SUCCESS;
 | 
				
			||||||
 | 
					    size_t ns_size   = (size_t)rt_ResourceNamespaceSize.i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt_write_batch writes = {.num_writes = 0};
 | 
				
			||||||
 | 
					    rt_aio_handle write_handles[RT_WRITE_BATCH_MAX_SIZE];
 | 
				
			||||||
 | 
					    uint32_t outstanding_writes = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rtLockWrite(&_namespace.lock);
 | 
				
			||||||
 | 
					    for (uint32_t i = 0; i < count; ++i) {
 | 
				
			||||||
 | 
					        size_t name_len   = strlen(names[i]);
 | 
				
			||||||
 | 
					        rt_resource_id id = (rt_resource_id)rtHashBytes(names[i], name_len);
 | 
				
			||||||
 | 
					        if (id == RT_INVALID_RESOURCE_ID || id == RT_TOMBSTONE_ID)
 | 
				
			||||||
 | 
					            id = ~id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool inserted = false;
 | 
				
			||||||
 | 
					        for (size_t j = 0; j < ns_size; ++j) {
 | 
				
			||||||
 | 
					            size_t at = (id + j) % ns_size;
 | 
				
			||||||
 | 
					            if (_namespace.ids[at] == RT_INVALID_RESOURCE_ID) {
 | 
				
			||||||
 | 
					                inserted = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ids[i] = id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const rt_resource *cached_resource = 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;
 | 
				
			||||||
 | 
					                ++writes.num_writes;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            } else if (_namespace.ids[at] == id) {
 | 
				
			||||||
 | 
					                rtReportError("RESMGR",
 | 
				
			||||||
 | 
					                              "Resource ID collision occured with resource %s.\nID: %llx",
 | 
				
			||||||
 | 
					                              names[i],
 | 
				
			||||||
 | 
					                              id);
 | 
				
			||||||
 | 
					                result = RT_INVALID_FILE_ID;
 | 
				
			||||||
 | 
					                goto out;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!inserted) {
 | 
				
			||||||
 | 
					            result = RT_OUT_OF_MEMORY;
 | 
				
			||||||
 | 
					            goto out;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (writes.num_writes == RT_WRITE_BATCH_MAX_SIZE ||
 | 
				
			||||||
 | 
					            (i == count - 1 && writes.num_writes > 0)) {
 | 
				
			||||||
 | 
					            if (outstanding_writes > 0) {
 | 
				
			||||||
 | 
					                /* Wait until the previous batch is finished */
 | 
				
			||||||
 | 
					                for (uint32_t k = 0; k < outstanding_writes; ++k) {
 | 
				
			||||||
 | 
					                    if (rtWaitForAIOCompletion(write_handles[k]) != RT_AIO_STATE_FINISHED) {
 | 
				
			||||||
 | 
					                        rtReportError("RESMGR", "Resource write failed.");
 | 
				
			||||||
 | 
					                        result = RT_UNKNOWN_ERROR;
 | 
				
			||||||
 | 
					                        goto out;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    rtReleaseAIO(write_handles[k]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            outstanding_writes = writes.num_writes;
 | 
				
			||||||
 | 
					            if (rtSubmitWriteBatch(&writes, write_handles) != RT_SUCCESS) {
 | 
				
			||||||
 | 
					                rtReportError("RESMGR", "Failed to submit resource writes.");
 | 
				
			||||||
 | 
					                result = RT_UNKNOWN_ERROR;
 | 
				
			||||||
 | 
					                goto out;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (outstanding_writes > 0) {
 | 
				
			||||||
 | 
					        /* Wait until the last batch is finished */
 | 
				
			||||||
 | 
					        for (uint32_t i = 0; i < outstanding_writes; ++i) {
 | 
				
			||||||
 | 
					            if (rtWaitForAIOCompletion(write_handles[i]) != RT_AIO_STATE_FINISHED) {
 | 
				
			||||||
 | 
					                rtReportError("RESMGR", "Resource write failed.");
 | 
				
			||||||
 | 
					                result = RT_UNKNOWN_ERROR;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            rtReleaseAIO(write_handles[i]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
					    rtUnlockWrite(&_namespace.lock);
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										72
									
								
								src/runtime/resources.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/runtime/resources.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					#ifndef RT_RESOURCES_H
 | 
				
			||||||
 | 
					#define RT_RESOURCES_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Resource system interface
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * To differentiate the two ideas, we called processed assets "resources"
 | 
				
			||||||
 | 
					 * and the source files "assets".
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * For example a .pipeline file is an asset, while a compiled pipeline in
 | 
				
			||||||
 | 
					 * a binary file is a resource.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Furthermore, a single asset file might contain multiple resources,
 | 
				
			||||||
 | 
					 * i.e. a single texture file might be turned into multiple resources for the different mip-levels.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Identifies a single resource
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is a hash of the resource name. */
 | 
				
			||||||
 | 
					typedef uint64_t rt_resource_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RT_INVALID_RESOURCE_ID 0u
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    /* Compiled shader code */
 | 
				
			||||||
 | 
					    RT_RESOURCE_SHADER,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* A pipeline state object */
 | 
				
			||||||
 | 
					    RT_RESOURCE_PIPELINE,
 | 
				
			||||||
 | 
					} rt_resource_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RT_MAX_SUBRESOURCES          32
 | 
				
			||||||
 | 
					#define RT_MAX_RESOURCE_DEPENDENCIES 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    /* Points to the resource data. The size of which is determined by the type. */
 | 
				
			||||||
 | 
					    void *data;
 | 
				
			||||||
 | 
					    rt_resource_type type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Subresources are necessary to complete the resource.
 | 
				
			||||||
 | 
					     * For example, a texture might contain different mip-levels as sub-resources. */
 | 
				
			||||||
 | 
					    uint32_t subresource_count;
 | 
				
			||||||
 | 
					    rt_resource_id subresources[RT_MAX_SUBRESOURCES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Dependencies reference distinct resources that are necessary to use this resource.
 | 
				
			||||||
 | 
					     * For example, a model file might depend on its materials */
 | 
				
			||||||
 | 
					    uint32_t dependency_count;
 | 
				
			||||||
 | 
					    rt_resource_id dependencies[RT_MAX_RESOURCE_DEPENDENCIES];
 | 
				
			||||||
 | 
					} rt_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.
 | 
				
			||||||
 | 
					 * To package them, you will need to use a separate tool.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
 | 
				
			||||||
 | 
					                                         const char **names,
 | 
				
			||||||
 | 
					                                         const rt_resource *resources,
 | 
				
			||||||
 | 
					                                         rt_resource_id *ids);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@ -3,6 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* basic types and macros */
 | 
					/* basic types and macros */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdarg.h>
 | 
				
			||||||
#include <stddef.h>
 | 
					#include <stddef.h>
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,7 +23,6 @@ extern "C" {
 | 
				
			|||||||
#define RT_INLINE inline __attribute__((always_inline))
 | 
					#define RT_INLINE inline __attribute__((always_inline))
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
#define RT_UNUSED(x)      ((void)sizeof((x)))
 | 
					#define RT_UNUSED(x)      ((void)sizeof((x)))
 | 
				
			||||||
#define RT_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0]))
 | 
					#define RT_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,6 +36,7 @@ typedef unsigned int rt_result;
 | 
				
			|||||||
enum {
 | 
					enum {
 | 
				
			||||||
    RT_SUCCESS       = 0,
 | 
					    RT_SUCCESS       = 0,
 | 
				
			||||||
    RT_OUT_OF_MEMORY = 1,
 | 
					    RT_OUT_OF_MEMORY = 1,
 | 
				
			||||||
 | 
					    RT_INVALID_VALUE = 2,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    RT_CUSTOM_ERROR_START,
 | 
					    RT_CUSTOM_ERROR_START,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,6 +48,13 @@ typedef struct {
 | 
				
			|||||||
    unsigned int length;
 | 
					    unsigned int length;
 | 
				
			||||||
} rt_text_span;
 | 
					} rt_text_span;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* snprintf replacement.
 | 
				
			||||||
 | 
					 * Always returns a zero terminated string.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					RT_DLLEXPORT int rtSPrint(char *dest, size_t n, const char *fmt, ...);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT int rtVSPrint(char *dest, size_t n, const char *fmt, va_list ap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Returns results like strcmp():
 | 
					/* Returns results like strcmp():
 | 
				
			||||||
 * - If the first differing character is smaller in span than in cmp: < 0
 | 
					 * - If the first differing character is smaller in span than in cmp: < 0
 | 
				
			||||||
 * - If span and cmp are equal: 0
 | 
					 * - If span and cmp are equal: 0
 | 
				
			||||||
@ -60,6 +68,28 @@ RT_DLLEXPORT void rtReportError(const char *subsystem, const char *fmt, ...);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
RT_DLLEXPORT void rtLog(const char *subsystem, const char *fmt, ...);
 | 
					RT_DLLEXPORT void rtLog(const char *subsystem, const char *fmt, ...);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					#ifdef _MSC_VER
 | 
				
			||||||
 | 
					#define RT_DEBUGBREAK __debugbreak()
 | 
				
			||||||
 | 
					#elif defined(__clang__) && __has_builtin(__bultin_debugtrap)
 | 
				
			||||||
 | 
					#define RT_DEBUGBREAK __builtin_debugtrap()
 | 
				
			||||||
 | 
					#elif defined(__GNUC__)
 | 
				
			||||||
 | 
					#define RT_DEBUGBREAK __builtin_trap()
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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) {                            \
 | 
				
			||||||
 | 
					                RT_DEBUGBREAK;                                                                     \
 | 
				
			||||||
 | 
					            }                                                                                      \
 | 
				
			||||||
 | 
					       }                                                                                           \
 | 
				
			||||||
 | 
					    } while (0)
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define RT_ASSERT(x, msg) RT_UNUSED(x)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
    RT_INVALID_UNICODE = RT_CUSTOM_ERROR_START,
 | 
					    RT_INVALID_UNICODE = RT_CUSTOM_ERROR_START,
 | 
				
			||||||
    RT_INSUFFICIENT_BUFFER,
 | 
					    RT_INSUFFICIENT_BUFFER,
 | 
				
			||||||
@ -89,6 +119,15 @@ static RT_INLINE void *rtResolveRelptr(rt_relptr *ptr) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static RT_INLINE const void *rtResolveConstRelptr(const rt_relptr *ptr) {
 | 
				
			||||||
 | 
					    if (ptr->off != 0) {
 | 
				
			||||||
 | 
					        const char *p = (const char *)ptr;
 | 
				
			||||||
 | 
					        return (const void *)(p + (ptrdiff_t)ptr->off);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static RT_INLINE void rtSetRelptr(rt_relptr *ptr, void *target) {
 | 
					static RT_INLINE void rtSetRelptr(rt_relptr *ptr, void *target) {
 | 
				
			||||||
    if (target) {
 | 
					    if (target) {
 | 
				
			||||||
        char *p = (char *)ptr, *t = (char *)target;
 | 
					        char *p = (char *)ptr, *t = (char *)target;
 | 
				
			||||||
 | 
				
			|||||||
@ -4,23 +4,12 @@
 | 
				
			|||||||
#include "mem_arena.h"
 | 
					#include "mem_arena.h"
 | 
				
			||||||
#include "runtime.h"
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "renderer_api.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    RT_SHADER_TYPE_INVALID,
 | 
					 | 
				
			||||||
    RT_SHADER_TYPE_VULKAN,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RT_SHADER_TYPE_COUNT,
 | 
					 | 
				
			||||||
} rt_shader_type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    RT_SHADER_STAGE_VERTEX,
 | 
					 | 
				
			||||||
    RT_SHADER_STAGE_FRAGMENT,
 | 
					 | 
				
			||||||
    RT_SHADER_STAGE_COMPUTE,
 | 
					 | 
				
			||||||
} rt_shader_stage;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    RT_SHADER_OPTIMIZATION_NONE,
 | 
					    RT_SHADER_OPTIMIZATION_NONE,
 | 
				
			||||||
    RT_SHADER_OPTIMIZATION_SPEED,
 | 
					    RT_SHADER_OPTIMIZATION_SPEED,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										47
									
								
								src/runtime/sprint.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/runtime/sprint.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdarg.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __SANITIZE_ADDRESS__
 | 
				
			||||||
 | 
					/* stb_sprintf has issues with ASAN under msvc */
 | 
				
			||||||
 | 
					#define ENABLE_STB_SPRINTF 0
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define ENABLE_STB_SPRINTF 1
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if ENABLE_STB_SPRINTF
 | 
				
			||||||
 | 
					#pragma warning(push, 0)
 | 
				
			||||||
 | 
					#define STB_SPRINTF_IMPLEMENTATION
 | 
				
			||||||
 | 
					#define STB_SPRINTF_STATIC
 | 
				
			||||||
 | 
					#include <stb_sprintf.h>
 | 
				
			||||||
 | 
					#pragma warning(pop)
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT int rtSPrint(char *dest, size_t n, const char *fmt, ...)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    va_list ap;
 | 
				
			||||||
 | 
					    va_start(ap, fmt);
 | 
				
			||||||
 | 
					#if ENABLE_STB_SPRINTF
 | 
				
			||||||
 | 
					    int r = stbsp_vsnprintf(dest, (int)n, fmt, ap);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    int r = vsnprintf(dest, n, fmt, ap);
 | 
				
			||||||
 | 
					    if (r >= (int)n)
 | 
				
			||||||
 | 
					        dest[n - 1] = '\0';
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    va_end(ap);
 | 
				
			||||||
 | 
					    return r;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RT_DLLEXPORT int rtVSPrint(char *dest, size_t n, const char *fmt, va_list ap) 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if ENABLE_STB_SPRINTF
 | 
				
			||||||
 | 
					    return stbsp_vsnprintf(dest, (int)n, fmt, ap);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    int r = vsnprintf(dest, n, fmt, ap);
 | 
				
			||||||
 | 
					    if (r >= (int)n)
 | 
				
			||||||
 | 
					        dest[n - 1] = '\0';
 | 
				
			||||||
 | 
					    return r;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user