Add a resource namespace file format
This commit is contained in:
parent
0b6e855cef
commit
870f238410
@ -389,7 +389,6 @@ rt_loaded_asset LoadAsset(rt_file_id file) {
|
||||
size_t file_size = rtGetFileSize(path);
|
||||
|
||||
void *buffer = rtAllocBuffer(file_size);
|
||||
rtLog("AC", "Buffer ptr %llx", (uintptr_t)buffer);
|
||||
if (!buffer) {
|
||||
rtLog("AC", "Failed to allocate buffer for loading %s.", path);
|
||||
return (rt_loaded_asset){.buffer = NULL, .size = 0};
|
||||
|
@ -156,7 +156,6 @@ RT_DLLEXPORT void *rtAllocBuffer(size_t size) {
|
||||
}
|
||||
|
||||
rtUnlockMutex(_guard);
|
||||
rtLog("BUFFERMGR", "Result ptr %llx", (uintptr_t)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,9 @@ RT_CVAR_I(rt_MaxCachedResources,
|
||||
RT_CVAR_I(rt_ResourceNamespaceSize,
|
||||
"The maximum number of resources that can exist. Default: 1.048.576",
|
||||
1048576);
|
||||
RT_CVAR_I(rt_DisableResourceNamespaceLoad,
|
||||
"Disables the load of the saved resource namespace. Default: 0 (off)",
|
||||
0);
|
||||
|
||||
#define RT_TOMBSTONE_ID 1
|
||||
|
||||
@ -72,6 +75,18 @@ typedef struct {
|
||||
rt_rwlock lock;
|
||||
} rt_resource_namespace;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
rt_hash64 checksum;
|
||||
uint32_t num_entries;
|
||||
} rt_namespace_file_header;
|
||||
|
||||
typedef struct {
|
||||
rt_resource_id id;
|
||||
rt_resource_ref ref;
|
||||
} rt_namespace_file_entry;
|
||||
#pragma pack(pop)
|
||||
|
||||
/* ~~~ Utilities ~~~ */
|
||||
|
||||
static size_t GetResourceDataSize(const rt_resource *resource) {
|
||||
@ -107,25 +122,6 @@ static void CopyResourceData(const rt_resource *resource, void *dest) {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static rt_resource_ref *GetResourceRefPtr(rt_resource_id id) {
|
||||
rt_resource_ref *ref = NULL;
|
||||
rtLockRead(&_namespace.lock);
|
||||
size_t ns_size = (size_t)rt_ResourceNamespaceSize.i;
|
||||
for (size_t j = 0; j < ns_size; ++j) {
|
||||
size_t at = (id + j) % ns_size;
|
||||
if (_namespace.ids[at] == RT_INVALID_RESOURCE_ID) {
|
||||
break;
|
||||
} else if (_namespace.ids[at] == id) {
|
||||
ref = &_namespace.refs[at];
|
||||
break;
|
||||
}
|
||||
}
|
||||
rtUnlockRead(&_namespace.lock);
|
||||
return ref;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fills the passed write struct with the necessary information to save the resource to a file */
|
||||
static bool PrepareResourceFlushToFile(rt_resource_id id,
|
||||
const rt_resource *resource,
|
||||
@ -441,6 +437,79 @@ static void ShutdownNamespace(void) {
|
||||
memset(&_namespace, 0, sizeof(_namespace));
|
||||
}
|
||||
|
||||
static void LoadNamespace(void) {
|
||||
char path[260];
|
||||
rtSPrint(path, RT_ARRAY_COUNT(path), "%s/namespace.bin", rt_ResourceDirectory.s);
|
||||
rt_file_id fid = rtAddFile(path);
|
||||
size_t file_size = rtGetFileSize(path);
|
||||
if (file_size == 0) {
|
||||
rtLog("RESMGR", "Unable to determine size of %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
|
||||
if (!temp.arena) {
|
||||
rtLog("RESMGR", "Unable to get temporary arena for loading the namespace.");
|
||||
return;
|
||||
}
|
||||
void *dest = rtArenaPush(temp.arena, file_size);
|
||||
if (!dest) {
|
||||
rtReturnTemporaryArena(temp);
|
||||
rtLog("RESMGR", "Unable to allocate temporary space for loading the namespace.");
|
||||
return;
|
||||
}
|
||||
|
||||
rt_aio_state state = rtSubmitSingleLoadSync(
|
||||
(rt_file_load){.file = fid, .num_bytes = file_size, .offset = 0, .dest = dest});
|
||||
if (state == RT_AIO_STATE_FINISHED) {
|
||||
const rt_namespace_file_header *header = dest;
|
||||
const rt_namespace_file_entry *entries = (const rt_namespace_file_entry *)(header + 1);
|
||||
|
||||
if ((header->num_entries * sizeof(rt_namespace_file_entry)) <=
|
||||
(file_size - sizeof(*header))) {
|
||||
rt_hash64 entries_hash =
|
||||
rtHashBytes(entries, sizeof(rt_namespace_file_entry) * header->num_entries);
|
||||
if (entries_hash == header->checksum) {
|
||||
size_t ns_size = (size_t)rt_ResourceNamespaceSize.i;
|
||||
for (uint32_t i = 0; i < header->num_entries; ++i) {
|
||||
bool inserted = false;
|
||||
for (size_t j = 0; j < ns_size; ++j) {
|
||||
size_t at = (entries[i].id + j) % ns_size;
|
||||
if (_namespace.ids[at] == RT_INVALID_RESOURCE_ID) {
|
||||
inserted = true;
|
||||
_namespace.ids[at] = entries[i].id;
|
||||
_namespace.refs[at] = entries[i].ref;
|
||||
break;
|
||||
} else if (_namespace.ids[at] == entries[i].id) {
|
||||
rtReportError(
|
||||
"RESMGR",
|
||||
"Resource ID (%llx) collision detected in namespace file %s",
|
||||
entries[i].id,
|
||||
path);
|
||||
rtReturnTemporaryArena(temp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!inserted) {
|
||||
rtReportError("RESMGR",
|
||||
"Failed to insert namespace entry %llx",
|
||||
entries[i].id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rtLog("RESMGR", "Checksum mismatch in %s", path);
|
||||
}
|
||||
} else {
|
||||
rtLog("RESMGR", "Number of entries in %s is inconsistent with the file size.", path);
|
||||
}
|
||||
} else {
|
||||
rtLog("RESMGR", "Failed to load %s.", path);
|
||||
}
|
||||
|
||||
rtReturnTemporaryArena(temp);
|
||||
}
|
||||
|
||||
static rt_resource_ref GetResourceRef(rt_resource_id id) {
|
||||
rt_resource_ref ref = {.file = RT_INVALID_FILE_ID};
|
||||
rtLockRead(&_namespace.lock);
|
||||
@ -472,6 +541,9 @@ rt_result InitResourceManager(void) {
|
||||
ShutdownResourceCache();
|
||||
return res;
|
||||
}
|
||||
if (!rt_DisableResourceNamespaceLoad.i) {
|
||||
LoadNamespace();
|
||||
}
|
||||
return RT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -736,7 +808,7 @@ RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
|
||||
"Resource ID collision occured with resource %s.\nID: %llx",
|
||||
names[i],
|
||||
id);
|
||||
result = RT_INVALID_FILE_ID;
|
||||
result = RT_INVALID_VALUE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -858,3 +930,61 @@ RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resour
|
||||
rtLog("RESMGR", " unknown data at: %llx", (uintptr_t)resource->data);
|
||||
}
|
||||
}
|
||||
|
||||
RT_DLLEXPORT void rtSaveResourceNamespace(void) {
|
||||
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
|
||||
rtLockRead(&_namespace.lock);
|
||||
uint32_t entry_count = 0;
|
||||
for (size_t i = 0; i < (size_t)rt_ResourceNamespaceSize.i; ++i) {
|
||||
if (_namespace.ids[i] != RT_INVALID_RESOURCE_ID)
|
||||
++entry_count;
|
||||
}
|
||||
|
||||
size_t buffer_size =
|
||||
sizeof(rt_namespace_file_header) + entry_count * sizeof(rt_namespace_file_entry);
|
||||
void *buffer = rtArenaPush(temp.arena, buffer_size);
|
||||
if (!buffer) {
|
||||
rtReportError(
|
||||
"RESMGR",
|
||||
"Failed to allocate temporary buffer (%zu bytes) for writing the namespace file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rt_namespace_file_header *header = buffer;
|
||||
rt_namespace_file_entry *entries = (rt_namespace_file_entry *)(header + 1);
|
||||
size_t at = 0;
|
||||
for (size_t i = 0; i < (size_t)rt_ResourceNamespaceSize.i; ++i) {
|
||||
if (_namespace.ids[i] != RT_INVALID_RESOURCE_ID) {
|
||||
entries[at].id = _namespace.ids[i];
|
||||
entries[at].ref = _namespace.refs[i];
|
||||
++at;
|
||||
}
|
||||
}
|
||||
RT_ASSERT(at == entry_count, "");
|
||||
header->num_entries = entry_count;
|
||||
header->checksum = rtHashBytes(entries, entry_count * sizeof(rt_namespace_file_entry));
|
||||
|
||||
char path[260];
|
||||
rtSPrint(path, RT_ARRAY_COUNT(path), "%s/namespace.bin", rt_ResourceDirectory.s);
|
||||
|
||||
rt_write_batch write = {.num_writes = 1};
|
||||
write.writes[0] = (rt_file_write){
|
||||
.file = rtAddFile(path),
|
||||
.buffer = buffer,
|
||||
.num_bytes = buffer_size,
|
||||
.offset = 0,
|
||||
};
|
||||
rt_aio_handle handle;
|
||||
if (rtSubmitWriteBatch(&write, &handle) != RT_SUCCESS) {
|
||||
rtReportError("RESMGR", "Failed to submit the write for %s", path);
|
||||
goto out;
|
||||
}
|
||||
rt_aio_state state = rtWaitForAIOCompletion(handle);
|
||||
rtReleaseAIO(handle);
|
||||
if (state != RT_AIO_STATE_FINISHED)
|
||||
rtReportError("RESMGR", "Write to %s failed: %u", path, state);
|
||||
|
||||
out:
|
||||
rtUnlockRead(&_namespace.lock);
|
||||
rtReturnTemporaryArena(temp);
|
||||
}
|
@ -77,6 +77,8 @@ RT_DLLEXPORT size_t rtGetResourceSize(rt_resource_id id);
|
||||
/* Logs information about a resource. Useful for debugging */
|
||||
RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resource);
|
||||
|
||||
RT_DLLEXPORT void rtSaveResourceNamespace(void);
|
||||
|
||||
/* 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.
|
||||
|
Loading…
Reference in New Issue
Block a user