Remove unnecessary files
This commit is contained in:
		
							parent
							
								
									cfec746545
								
							
						
					
					
						commit
						b80d0d1bf9
					
				@ -1,20 +0,0 @@
 | 
				
			|||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
#include "file_tab.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result LoadPackageNames(void) {
 | 
					 | 
				
			||||||
    FILE *f = fopen("data/packages.txt", "r");
 | 
					 | 
				
			||||||
    if (!f) {
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while (!feof(f)) {
 | 
					 | 
				
			||||||
        char line[256];
 | 
					 | 
				
			||||||
        fscanf(f, "%255s\n", line);
 | 
					 | 
				
			||||||
        line[255] = '\0';
 | 
					 | 
				
			||||||
        rtAddFile(line);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fclose(f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,18 +0,0 @@
 | 
				
			|||||||
#ifndef RT_PACKAGES_H
 | 
					 | 
				
			||||||
#define RT_PACKAGES_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef RT_DEFINE_PACKAGE_FILE_STRUCTURES
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include "xxhash/xxhash.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma pack(push, 1)
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    XXH64_canonical_t checksum;
 | 
					 | 
				
			||||||
    uint32_t decompressed_size;
 | 
					 | 
				
			||||||
} rt_package_asset_header;
 | 
					 | 
				
			||||||
#pragma pack(pop)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,149 +0,0 @@
 | 
				
			|||||||
#include "assetmeta.h"
 | 
					 | 
				
			||||||
#include "processing.h"
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
#include "packages.h"
 | 
					 | 
				
			||||||
#include "dependency_tracking.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define RT_ASSETC_DONT_DEFINE_OPTIONS_GLOBAL
 | 
					 | 
				
			||||||
#include "options.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/aio.h"
 | 
					 | 
				
			||||||
#include "runtime/buffer_manager.h"
 | 
					 | 
				
			||||||
#include "runtime/uidtab.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern rt_result rtProcessPipelineFile(rt_file_id file,
 | 
					 | 
				
			||||||
                                       void *buffer,
 | 
					 | 
				
			||||||
                                       size_t size,
 | 
					 | 
				
			||||||
                                       uint32_t flags,
 | 
					 | 
				
			||||||
                                       rt_processor_output *output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern rt_result rtProcessShaderFile(rt_file_id file,
 | 
					 | 
				
			||||||
                                     void *buffer,
 | 
					 | 
				
			||||||
                                     size_t size,
 | 
					 | 
				
			||||||
                                     uint32_t flags,
 | 
					 | 
				
			||||||
                                     rt_processor_output *output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern void rtDiscoverAssets(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_assetc_options g_assetc_options = {
 | 
					 | 
				
			||||||
    .root_directory   = ".",
 | 
					 | 
				
			||||||
    .optimization     = RT_ASSET_OPTIMIZATION_NONE,
 | 
					 | 
				
			||||||
    .renderer_backend = RT_RENDERER_BACKEND_CODE_VK,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseCommandLineArgs(int argc, char **argv) {
 | 
					 | 
				
			||||||
    bool have_root_directory = false;
 | 
					 | 
				
			||||||
    for (int i = 1; i < argc; ++i) {
 | 
					 | 
				
			||||||
        if (i < argc - 1) {
 | 
					 | 
				
			||||||
            const char *val = argv[i + 1];
 | 
					 | 
				
			||||||
            bool matched    = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--renderer") == 0) {
 | 
					 | 
				
			||||||
                if (strcmp(val, "vk") == 0) {
 | 
					 | 
				
			||||||
                    g_assetc_options.renderer_backend = RT_RENDERER_BACKEND_CODE_VK;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                                  "Invalid render backend %s. Valid options are: vk",
 | 
					 | 
				
			||||||
                                  val);
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                matched = true;
 | 
					 | 
				
			||||||
            } else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--optimization") == 0) {
 | 
					 | 
				
			||||||
                if (strcmp(val, "none") == 0) {
 | 
					 | 
				
			||||||
                    g_assetc_options.optimization = RT_ASSET_OPTIMIZATION_NONE;
 | 
					 | 
				
			||||||
                } else if (strcmp(val, "space") == 0) {
 | 
					 | 
				
			||||||
                    g_assetc_options.optimization = RT_ASSET_OPTIMIZATION_SPACE;
 | 
					 | 
				
			||||||
                } else if (strcmp(val, "performance") == 0) {
 | 
					 | 
				
			||||||
                    g_assetc_options.optimization = RT_ASSET_OPTIMIZATION_PERFORMANCE;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                                  "Invalid optimization level %s. Valid options are: none, space, "
 | 
					 | 
				
			||||||
                                  "performance",
 | 
					 | 
				
			||||||
                                  val);
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                matched = true;
 | 
					 | 
				
			||||||
            } else if (argv[i][0] == '-') {
 | 
					 | 
				
			||||||
                rtReportError("ASSETC", "Invalid command line argument %s", argv[i]);
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (matched) {
 | 
					 | 
				
			||||||
                /* Skip the value */
 | 
					 | 
				
			||||||
                ++i;
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!have_root_directory) {
 | 
					 | 
				
			||||||
            g_assetc_options.root_directory = argv[i];
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            /* Maybe have secondary directories later? */
 | 
					 | 
				
			||||||
            rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                          "More than one root directory passed as command line "
 | 
					 | 
				
			||||||
                          "argument.");
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main(int argc, char **argv) {
 | 
					 | 
				
			||||||
    rtInitRuntime();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Init assetc */
 | 
					 | 
				
			||||||
    if (!ParseCommandLineArgs(argc, argv)) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtInitPackages();
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (rtLoadAssetMeta() != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if (rtInitDependencyTracking() != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (rtAddAssetProcessor(".pipeline", rtProcessPipelineFile) != RT_SUCCESS)
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    if (rtAddAssetProcessor(".glsl", rtProcessShaderFile) != RT_SUCCESS)
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (rtStartProcessing() != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Create necessary directories */
 | 
					 | 
				
			||||||
    rtSetWorkingDirectory(g_assetc_options.root_directory);
 | 
					 | 
				
			||||||
    rtCreateDirectory("assets");
 | 
					 | 
				
			||||||
    rtCreateDirectory("actemp");
 | 
					 | 
				
			||||||
    rtCreateDirectory("data");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* "Mainloop" */
 | 
					 | 
				
			||||||
    rtDiscoverAssets();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtWaitUntilProcessingIsFinished();
 | 
					 | 
				
			||||||
    rtStopProcessing();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Write result */
 | 
					 | 
				
			||||||
    rtSaveAssetMeta();
 | 
					 | 
				
			||||||
    if (rtSavePackages() != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (rtWriteUIDTab() != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (rtSaveAssetDependencies() != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtShutdownRuntime();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,222 +0,0 @@
 | 
				
			|||||||
#include "assetmeta.h"
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
#include "options.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/threading.h"
 | 
					 | 
				
			||||||
#include "runtime/aio.h"
 | 
					 | 
				
			||||||
#include "runtime/buffer_manager.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <xxhash/xxhash.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAP_SIZE 2048
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_file_id fids[MAP_SIZE];
 | 
					 | 
				
			||||||
    rt_uid uids[MAP_SIZE];
 | 
					 | 
				
			||||||
    rt_assetmeta meta[MAP_SIZE];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsigned int used_slots;
 | 
					 | 
				
			||||||
} rt_uid_map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma pack(push, 1)
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    XXH64_canonical_t checksum;
 | 
					 | 
				
			||||||
    uint32_t num_entries;
 | 
					 | 
				
			||||||
    uint32_t _reserved;
 | 
					 | 
				
			||||||
} rt_assetmeta_header;
 | 
					 | 
				
			||||||
#pragma pack(pop)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma pack(push, 1)
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_uid uid;
 | 
					 | 
				
			||||||
    rt_file_id source_file;
 | 
					 | 
				
			||||||
    rt_assetmeta meta;
 | 
					 | 
				
			||||||
} rt_assetmeta_entry;
 | 
					 | 
				
			||||||
#pragma pack(pop)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_uid_map _map;
 | 
					 | 
				
			||||||
static rt_mutex *_guard;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtLoadAssetMeta(void) {
 | 
					 | 
				
			||||||
    _guard = rtCreateMutex();
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /* Load the meta file */
 | 
					 | 
				
			||||||
    size_t fsz = rtGetFileSize("actemp/meta.bin");
 | 
					 | 
				
			||||||
    if (fsz == 0) {
 | 
					 | 
				
			||||||
        rtLog("ASSETC", "Metadata file 'meta.bin' not found. All assets will be processed.");
 | 
					 | 
				
			||||||
        return RT_SUCCESS;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void *buffer = rtAllocBuffer(fsz);
 | 
					 | 
				
			||||||
    if (!buffer) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to allocate buffer for holding asset metadata.");
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    rt_load_batch load;
 | 
					 | 
				
			||||||
    load.loads[0].dest      = buffer;
 | 
					 | 
				
			||||||
    load.loads[0].file      = rtAddFile("actemp/meta.bin");
 | 
					 | 
				
			||||||
    load.loads[0].num_bytes = fsz;
 | 
					 | 
				
			||||||
    load.loads[0].offset    = 0;
 | 
					 | 
				
			||||||
    load.num_loads          = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_aio_handle handle;
 | 
					 | 
				
			||||||
    if (rtSubmitLoadBatch(&load, &handle) != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to submit load of 'meta.bin'.");
 | 
					 | 
				
			||||||
        rtReleaseBuffer(buffer, fsz);
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (rtWaitForAIOCompletion(handle) != RT_AIO_STATE_FINISHED) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to load 'meta.bin'.");
 | 
					 | 
				
			||||||
        rtReleaseBuffer(buffer, fsz);
 | 
					 | 
				
			||||||
        rtReleaseAIO(handle);
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtReleaseAIO(handle);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const rt_assetmeta_header *header = buffer;
 | 
					 | 
				
			||||||
    const rt_assetmeta_entry *entries = (rt_assetmeta_entry *)(header + 1);
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if ((sizeof(rt_assetmeta_entry) * header->num_entries + sizeof(*header)) > fsz) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Header of 'meta.bin' is corrupted: Mismatched num_entries and filesize.");
 | 
					 | 
				
			||||||
        rtReleaseBuffer(buffer, fsz);
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    XXH64_hash_t hash = XXH3_64bits(entries, sizeof(rt_assetmeta_entry) * header->num_entries);
 | 
					 | 
				
			||||||
    XXH64_hash_t header_hash = XXH64_hashFromCanonical(&header->checksum);
 | 
					 | 
				
			||||||
    if (hash != header_hash) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                      "Metadata file 'meta.bin' is corrupted: Wrong checksum.");
 | 
					 | 
				
			||||||
        rtReleaseBuffer(buffer, fsz);
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (uint32_t i = 0; i < header->num_entries; ++i) {
 | 
					 | 
				
			||||||
        /* Load here to avoid unaligned pointer */
 | 
					 | 
				
			||||||
        rt_assetmeta meta = entries[i].meta;
 | 
					 | 
				
			||||||
        rtAddUIDMapping(entries[i].source_file, entries[i].uid, &meta);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtReleaseBuffer(buffer, fsz);
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtSaveAssetMeta(void) {
 | 
					 | 
				
			||||||
    rt_assetmeta_header header = {0};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Count number of entries */
 | 
					 | 
				
			||||||
    for (size_t i = 0; i < MAP_SIZE; ++i) {
 | 
					 | 
				
			||||||
        if (_map.fids[i] != RT_INVALID_FILE_ID)
 | 
					 | 
				
			||||||
            header.num_entries += 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_assetmeta_entry *entries = rtAllocBuffer(sizeof(rt_assetmeta_entry) * header.num_entries);
 | 
					 | 
				
			||||||
    if (!entries)
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Store entries */
 | 
					 | 
				
			||||||
    size_t j = 0;
 | 
					 | 
				
			||||||
    for (size_t i = 0; i < MAP_SIZE; ++i) {
 | 
					 | 
				
			||||||
        if (_map.fids[i] != RT_INVALID_FILE_ID) {
 | 
					 | 
				
			||||||
            entries[j].source_file = _map.fids[i];
 | 
					 | 
				
			||||||
            entries[j].uid         = _map.uids[i];
 | 
					 | 
				
			||||||
            entries[j].meta        = _map.meta[i];
 | 
					 | 
				
			||||||
            ++j;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    XXH64_hash_t hash = XXH3_64bits(entries, sizeof(rt_assetmeta_entry) * header.num_entries);
 | 
					 | 
				
			||||||
    XXH64_canonicalFromHash(&header.checksum, hash);
 | 
					 | 
				
			||||||
    header._reserved = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FILE *f = fopen("actemp/meta.bin", "wb");
 | 
					 | 
				
			||||||
    if (!f) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to open 'meta.bin'");
 | 
					 | 
				
			||||||
        rtReleaseBuffer(entries, sizeof(rt_assetmeta_entry) * header.num_entries);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (fwrite(&header, sizeof(header), 1, f) != 1) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to write 'meta.bin'");
 | 
					 | 
				
			||||||
        rtReleaseBuffer(entries, sizeof(rt_assetmeta_entry) * header.num_entries);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (fwrite(entries, sizeof(rt_assetmeta_entry), header.num_entries, f) != header.num_entries) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to write 'meta.bin'");
 | 
					 | 
				
			||||||
        rtReleaseBuffer(entries, sizeof(rt_assetmeta_entry) * header.num_entries);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fclose(f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_uid rtLookupUID(rt_file_id fid) {
 | 
					 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
    unsigned int i = 0;
 | 
					 | 
				
			||||||
    rt_uid result  = RT_INVALID_UID;
 | 
					 | 
				
			||||||
    while (i < MAP_SIZE) {
 | 
					 | 
				
			||||||
        unsigned int slot = (fid + i) % MAP_SIZE;
 | 
					 | 
				
			||||||
        if (_map.fids[slot] == fid) {
 | 
					 | 
				
			||||||
            result          = _map.uids[slot];
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        } else if (_map.fids[slot] == RT_INVALID_FILE_ID) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ++i;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool rtGetAssetMeta(rt_file_id source_file, rt_assetmeta *meta) {
 | 
					 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
    unsigned int i = 0;
 | 
					 | 
				
			||||||
    bool result    = false;
 | 
					 | 
				
			||||||
    while (i < MAP_SIZE) {
 | 
					 | 
				
			||||||
        unsigned int slot = (source_file + i) % MAP_SIZE;
 | 
					 | 
				
			||||||
        if (_map.fids[slot] == source_file) {
 | 
					 | 
				
			||||||
            *meta  = _map.meta[slot];
 | 
					 | 
				
			||||||
            result = true;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        } else if (_map.fids[slot] == RT_INVALID_FILE_ID) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ++i;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtAddUIDMapping(rt_file_id fid, rt_uid uid, const rt_assetmeta *meta) {
 | 
					 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
    float fill_rate = (float)_map.used_slots / MAP_SIZE;
 | 
					 | 
				
			||||||
    if (fill_rate >= .5f) {
 | 
					 | 
				
			||||||
        rtLog("ASSETC", "UID map is above 50% filled.");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    unsigned int i = 0;
 | 
					 | 
				
			||||||
    while (i < MAP_SIZE) {
 | 
					 | 
				
			||||||
        unsigned int slot = (fid + i) % MAP_SIZE;
 | 
					 | 
				
			||||||
        if (_map.fids[slot] == RT_INVALID_FILE_ID) {
 | 
					 | 
				
			||||||
            _map.fids[slot] = fid;
 | 
					 | 
				
			||||||
            _map.uids[slot] = uid;
 | 
					 | 
				
			||||||
            if (meta) {
 | 
					 | 
				
			||||||
                _map.meta[slot] = *meta;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                _map.meta[slot].last_changed = 0;
 | 
					 | 
				
			||||||
                _map.meta[slot].compiled_ts = 0;
 | 
					 | 
				
			||||||
                _map.meta[slot].processing_flags = 0;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ++_map.used_slots;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        } else if (_map.fids[slot] == fid) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ++i;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (i == MAP_SIZE) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to insert entry into UID map.");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,28 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_ASSETMETA_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_ASSETMETA_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/file_tab.h"
 | 
					 | 
				
			||||||
#include "runtime/assets.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Metadata for assets used only by assetc */
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    uint64_t last_changed;
 | 
					 | 
				
			||||||
    uint64_t compiled_ts;
 | 
					 | 
				
			||||||
    uint32_t processing_flags;
 | 
					 | 
				
			||||||
} rt_assetmeta;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtLoadAssetMeta(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtSaveAssetMeta(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* The UID map associates processed files with generated asset uids. */
 | 
					 | 
				
			||||||
void rtAddUIDMapping(rt_file_id fid, rt_uid uid, const rt_assetmeta *meta);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Returns true if the asset is found. false otherwise */
 | 
					 | 
				
			||||||
bool rtGetAssetMeta(rt_file_id source_file, rt_assetmeta *meta);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_uid rtLookupUID(rt_file_id fid);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,144 +0,0 @@
 | 
				
			|||||||
#include "assetsettings.h"
 | 
					 | 
				
			||||||
#include "description_parser.h"
 | 
					 | 
				
			||||||
#include "packages.h"
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/aio.h"
 | 
					 | 
				
			||||||
#include "runtime/buffer_manager.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtParseAssetSettings(const char *text,
 | 
					 | 
				
			||||||
                               size_t length,
 | 
					 | 
				
			||||||
                               const char *file_path,
 | 
					 | 
				
			||||||
                               rt_asset_settings *settings) {
 | 
					 | 
				
			||||||
    unsigned int root_list;
 | 
					 | 
				
			||||||
    rt_parse_state state;
 | 
					 | 
				
			||||||
    rt_result res = rtParseDescription(text, length, file_path, &root_list, &state);
 | 
					 | 
				
			||||||
    if (res != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to parse asset settings: %s", file_path);
 | 
					 | 
				
			||||||
        return res;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Default settings */
 | 
					 | 
				
			||||||
    settings->package = 0;
 | 
					 | 
				
			||||||
    settings->processing_flags = 0;
 | 
					 | 
				
			||||||
    settings->reprocess_on_dependency_change = false;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    const rt_parsed_stmt *package_stmt = rtFindStatement(&state, root_list, "package");
 | 
					 | 
				
			||||||
    if (package_stmt) {
 | 
					 | 
				
			||||||
        if (package_stmt->form != RT_STMT_FORM_VALUE) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                          "Expected a package name as the value of 'package' in %s.",
 | 
					 | 
				
			||||||
                          file_path);
 | 
					 | 
				
			||||||
            res = RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
            goto out;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        settings->package = rtAddPackageFile(package_stmt->value);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const rt_parsed_stmt *flags_stmt = rtFindStatement(&state, root_list, "processing_flags");
 | 
					 | 
				
			||||||
    if (flags_stmt) {
 | 
					 | 
				
			||||||
        if (flags_stmt->form != RT_STMT_FORM_VALUE) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                          "Expected a hexadecimal number as the value of 'processing_flags' in %s.",
 | 
					 | 
				
			||||||
                          file_path);
 | 
					 | 
				
			||||||
            res = RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
            goto out;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sscanf(flags_stmt->value.start, "%x", &settings->processing_flags);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const rt_parsed_stmt *reprocess_stmt =
 | 
					 | 
				
			||||||
        rtFindStatement(&state, root_list, "reprocess_on_dependency_change");
 | 
					 | 
				
			||||||
    if (reprocess_stmt) {
 | 
					 | 
				
			||||||
        if (reprocess_stmt->form != RT_STMT_FORM_VALUE) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                          "Expected either 'true' or 'false' as the value of 'reprocess_on_dependency_change' in %s.",
 | 
					 | 
				
			||||||
                          file_path);
 | 
					 | 
				
			||||||
            res = RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
            goto out;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (rtCompareSpanToString(reprocess_stmt->value, "true") == 0)
 | 
					 | 
				
			||||||
            settings->reprocess_on_dependency_change = true;
 | 
					 | 
				
			||||||
        else if (rtCompareSpanToString(reprocess_stmt->value, "false") == 0)
 | 
					 | 
				
			||||||
            settings->reprocess_on_dependency_change = false;
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                          "Expected either 'true' or 'false' as the value of "
 | 
					 | 
				
			||||||
                          "'reprocess_on_dependency_change' in %s.",
 | 
					 | 
				
			||||||
                          file_path);
 | 
					 | 
				
			||||||
            res = RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
            goto out;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
    rtReleaseParseState(&state);
 | 
					 | 
				
			||||||
    return res;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtLoadAssetSettings(const char *asset_path, rt_asset_settings *settings) {
 | 
					 | 
				
			||||||
    size_t path_len = strlen(asset_path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    char *as_path = malloc(path_len + 3);
 | 
					 | 
				
			||||||
    if (!as_path) {
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    memcpy(as_path, asset_path, path_len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t ext_len = 0;
 | 
					 | 
				
			||||||
    while (ext_len < path_len) {
 | 
					 | 
				
			||||||
        if (as_path[path_len - ext_len] == '.')
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        ++ext_len;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    strcpy(&as_path[path_len - ext_len], ".as");
 | 
					 | 
				
			||||||
    size_t as_size = rtGetFileSize(as_path);
 | 
					 | 
				
			||||||
    if (as_size == 0) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to retrieve size of setting file %s", as_path);
 | 
					 | 
				
			||||||
        free(as_path);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void *as_buffer = rtAllocBuffer(as_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_load_batch as_load;
 | 
					 | 
				
			||||||
    as_load.loads[0].file      = rtAddFile(as_path);
 | 
					 | 
				
			||||||
    as_load.loads[0].num_bytes = as_size;
 | 
					 | 
				
			||||||
    as_load.loads[0].dest      = as_buffer;
 | 
					 | 
				
			||||||
    if (!as_load.loads[0].dest) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to allocate buffer for setting file %s", as_path);
 | 
					 | 
				
			||||||
        free(as_path);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    as_load.loads[0].offset = 0;
 | 
					 | 
				
			||||||
    as_load.num_loads       = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_aio_handle as_handle;
 | 
					 | 
				
			||||||
    if (rtSubmitLoadBatch(&as_load, &as_handle) != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to submit load of setting file %s", as_path);
 | 
					 | 
				
			||||||
        free(as_path);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (rtWaitForAIOCompletion(as_handle) != RT_AIO_STATE_FINISHED) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to load setting file %s", as_path);
 | 
					 | 
				
			||||||
        free(as_path);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtReleaseAIO(as_handle);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (rtParseAssetSettings(as_buffer, as_size, as_path, settings) != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        free(as_path);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    free(as_path);
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,21 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_ASSETSETTINGS_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_ASSETSETTINGS_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/runtime.h"
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    unsigned int package;
 | 
					 | 
				
			||||||
    uint32_t processing_flags;
 | 
					 | 
				
			||||||
    bool reprocess_on_dependency_change;
 | 
					 | 
				
			||||||
} rt_asset_settings;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtParseAssetSettings(const char *text,
 | 
					 | 
				
			||||||
                               size_t length,
 | 
					 | 
				
			||||||
                               const char *file_path,
 | 
					 | 
				
			||||||
                               rt_asset_settings *settings);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtLoadAssetSettings(const char *asset_path, rt_asset_settings *settings);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,13 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_COMPILED_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_COMPILED_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/runtime.h"
 | 
					 | 
				
			||||||
#include "runtime/assets.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_uid rtGetUID(const char *name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtStoreOutput(rt_uid uid, const void *data, size_t size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtWriteCompiledFiles(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,228 +0,0 @@
 | 
				
			|||||||
#include "dependency_tracking.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define RT_DEFINE_DEPENDENCY_FILE_STRUCTURES
 | 
					 | 
				
			||||||
#include "runtime/assets.h"
 | 
					 | 
				
			||||||
#include "runtime/threading.h"
 | 
					 | 
				
			||||||
#include "runtime/asset_dependencies.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <limits.h>
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Track a list of dependencies per asset.
 | 
					 | 
				
			||||||
 * For the runtime, we only care about dependent -> dependency (so for example material -> texture),
 | 
					 | 
				
			||||||
 * but for assetc we also care about the opposite direction:
 | 
					 | 
				
			||||||
 * If an asset has the reprocess_on_dependency_change setting enabled,
 | 
					 | 
				
			||||||
 * we need to queue a reprocessing, even if the asset itself did not change.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define END_OF_LIST 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* 64 byte cache line - 8 (next index + count) / 4 (u32) = 14 */
 | 
					 | 
				
			||||||
#define BUCKET_ENTRY_COUNT 14
 | 
					 | 
				
			||||||
typedef struct rt_dep_list_bucket_s {
 | 
					 | 
				
			||||||
    uint32_t next;
 | 
					 | 
				
			||||||
    uint32_t count;
 | 
					 | 
				
			||||||
    rt_uid entries[BUCKET_ENTRY_COUNT];
 | 
					 | 
				
			||||||
} rt_dep_list_bucket;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef union {
 | 
					 | 
				
			||||||
    /* Indices of the first buckets */
 | 
					 | 
				
			||||||
    struct {
 | 
					 | 
				
			||||||
        uint32_t dependencies;
 | 
					 | 
				
			||||||
        uint32_t dependents;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    uint32_t lists[2];
 | 
					 | 
				
			||||||
} rt_dep_list;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_mutex *_guard;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_dep_list_bucket *_buckets;
 | 
					 | 
				
			||||||
static uint32_t _bucket_count;
 | 
					 | 
				
			||||||
static uint32_t _bucket_capacity;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAP_SIZE 2048
 | 
					 | 
				
			||||||
static rt_uid _uids[MAP_SIZE];
 | 
					 | 
				
			||||||
static rt_dep_list _lists[MAP_SIZE];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtInitDependencyTracking(void) {
 | 
					 | 
				
			||||||
    _guard = rtCreateMutex();
 | 
					 | 
				
			||||||
    if (!_guard)
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _buckets = malloc(sizeof(rt_dep_list_bucket) * 256);
 | 
					 | 
				
			||||||
    if (!_buckets)
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    _bucket_capacity = 256;
 | 
					 | 
				
			||||||
    _bucket_count    = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static uint32_t AllocNewBucket(void) {
 | 
					 | 
				
			||||||
    if (_bucket_count == _bucket_capacity) {
 | 
					 | 
				
			||||||
        void *t = realloc(_buckets, (size_t)_bucket_capacity * 2 * sizeof(rt_dep_list_bucket));
 | 
					 | 
				
			||||||
        if (!t)
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        _buckets = t;
 | 
					 | 
				
			||||||
        _bucket_capacity *= 2;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    memset(&_buckets[_bucket_count], 0, sizeof(rt_dep_list_bucket));
 | 
					 | 
				
			||||||
    return _bucket_count++;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_result InsertIntoList(rt_uid list_asset, rt_uid uid, int list_index) {
 | 
					 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
    bool inserted = false;
 | 
					 | 
				
			||||||
    for (uint32_t i = 0; i < MAP_SIZE; i++) {
 | 
					 | 
				
			||||||
        uint32_t at = (list_asset + i) % MAP_SIZE;
 | 
					 | 
				
			||||||
        if (_uids[at] == list_asset || _uids[at] == 0) {
 | 
					 | 
				
			||||||
            _uids[at] = list_asset;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Alloc a new list, if necessary */
 | 
					 | 
				
			||||||
            if (_lists[at].lists[list_index] == 0) {
 | 
					 | 
				
			||||||
                _lists[at].lists[list_index] = AllocNewBucket();
 | 
					 | 
				
			||||||
                if (!_lists[at].lists[list_index]) {
 | 
					 | 
				
			||||||
                    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
                    return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Advance to the end of the list */
 | 
					 | 
				
			||||||
            rt_dep_list_bucket *bucket = &_buckets[_lists[at].lists[list_index]];
 | 
					 | 
				
			||||||
            while (bucket->next != END_OF_LIST) {
 | 
					 | 
				
			||||||
                bucket = &_buckets[bucket->next];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Grow the list, if necessary */
 | 
					 | 
				
			||||||
            if (bucket->count == BUCKET_ENTRY_COUNT) {
 | 
					 | 
				
			||||||
                bucket->next = AllocNewBucket();
 | 
					 | 
				
			||||||
                if (!bucket->next) {
 | 
					 | 
				
			||||||
                    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
                    return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                bucket = &_buckets[bucket->next];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            assert(bucket->count < BUCKET_ENTRY_COUNT);
 | 
					 | 
				
			||||||
            bucket->entries[bucket->count++] = uid;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            inserted = true;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
    assert(inserted);
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtAddAssetDependency(rt_uid dependent, rt_uid dependency) {
 | 
					 | 
				
			||||||
    rt_result res = InsertIntoList(dependent, dependency, 0);
 | 
					 | 
				
			||||||
    if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
        return res;
 | 
					 | 
				
			||||||
    res = InsertIntoList(dependency, dependent, 1);
 | 
					 | 
				
			||||||
    return res;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtSaveAssetDependencies(void) {
 | 
					 | 
				
			||||||
    assert(rtIsMainThread());
 | 
					 | 
				
			||||||
    rt_dependency_file_header header = {.num_lists = 0, .data_size = 0};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (size_t i = 0; i < MAP_SIZE; ++i) {
 | 
					 | 
				
			||||||
        if (_uids[i] != RT_INVALID_UID) {
 | 
					 | 
				
			||||||
            if (!_lists[i].dependencies)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            header.num_lists += 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Determine the list size */
 | 
					 | 
				
			||||||
            rt_dep_list_bucket *bucket = &_buckets[_lists[i].dependencies];
 | 
					 | 
				
			||||||
            uint32_t total_list_size   = bucket->count;
 | 
					 | 
				
			||||||
            while (bucket->next != END_OF_LIST) {
 | 
					 | 
				
			||||||
                bucket = &_buckets[bucket->next];
 | 
					 | 
				
			||||||
                total_list_size += bucket->count;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            header.data_size += total_list_size * sizeof(rt_uid) + sizeof(rt_dependency_file_list_header);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    FILE *f = fopen("data/deps.bin", "wb");
 | 
					 | 
				
			||||||
    if (!f) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to open 'deps.bin' for writing.");
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (fwrite(&header, sizeof(header), 1, f) != 1) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to write to 'deps.bin'.");
 | 
					 | 
				
			||||||
        fclose(f);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void *buffer       = NULL;
 | 
					 | 
				
			||||||
    size_t buffer_size = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (size_t i = 0; i < MAP_SIZE; ++i) {
 | 
					 | 
				
			||||||
        if (_uids[i] != RT_INVALID_UID) {
 | 
					 | 
				
			||||||
            if (!_lists[i].dependencies)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Determine the list size */
 | 
					 | 
				
			||||||
            rt_dep_list_bucket *bucket = &_buckets[_lists[i].dependencies];
 | 
					 | 
				
			||||||
            uint32_t total_list_size = bucket->count;
 | 
					 | 
				
			||||||
            while (bucket->next != END_OF_LIST) {
 | 
					 | 
				
			||||||
                bucket = &_buckets[bucket->next];
 | 
					 | 
				
			||||||
                total_list_size += bucket->count;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Allocate */
 | 
					 | 
				
			||||||
            size_t required_size =
 | 
					 | 
				
			||||||
                total_list_size * sizeof(rt_uid) + sizeof(rt_dependency_file_list_header);
 | 
					 | 
				
			||||||
            if (required_size > buffer_size) {
 | 
					 | 
				
			||||||
                void *t = realloc(buffer, required_size);
 | 
					 | 
				
			||||||
                if (!t) {
 | 
					 | 
				
			||||||
                    free(buffer);
 | 
					 | 
				
			||||||
                    fclose(f);
 | 
					 | 
				
			||||||
                    return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                buffer = t;
 | 
					 | 
				
			||||||
                buffer_size = required_size;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Fill header */
 | 
					 | 
				
			||||||
            rt_dependency_file_list_header *list_header = buffer;
 | 
					 | 
				
			||||||
            rt_uid *list                                = (rt_uid *)(list_header + 1);
 | 
					 | 
				
			||||||
            list_header->uid                            = _uids[i];
 | 
					 | 
				
			||||||
            list_header->num_entries                    = total_list_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Copy the list */
 | 
					 | 
				
			||||||
            uint32_t at = 0;
 | 
					 | 
				
			||||||
            bucket = &_buckets[_lists[i].dependencies];
 | 
					 | 
				
			||||||
            do {
 | 
					 | 
				
			||||||
                memcpy(&list[at], bucket->entries, sizeof(rt_uid) * bucket->count);
 | 
					 | 
				
			||||||
                at += bucket->count;
 | 
					 | 
				
			||||||
                bucket = &_buckets[bucket->next];
 | 
					 | 
				
			||||||
            } while (bucket != &_buckets[END_OF_LIST]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            XXH64_hash_t hash = XXH3_64bits(list, sizeof(rt_uid) * total_list_size);
 | 
					 | 
				
			||||||
            XXH64_canonicalFromHash(&list_header->checksum, hash);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (fwrite(buffer, required_size, 1, f) != 1) {
 | 
					 | 
				
			||||||
                rtReportError("ASSETC", "Failed to write to 'deps.bin'.");
 | 
					 | 
				
			||||||
                fclose(f);
 | 
					 | 
				
			||||||
                free(buffer);
 | 
					 | 
				
			||||||
                return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fclose(f);
 | 
					 | 
				
			||||||
    free(buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,14 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_DEPENDENCY_TRACKING_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_DEPENDENCY_TRACKING_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/runtime.h"
 | 
					 | 
				
			||||||
#include "runtime/assets.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtInitDependencyTracking(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtAddAssetDependency(rt_uid dependent, rt_uid dependency);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtSaveAssetDependencies(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,252 +0,0 @@
 | 
				
			|||||||
#include "description_parser.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/runtime.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <limits.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int memcmp(const void *s1, const void *s2, size_t n);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool IsAllowedChar(char c) {
 | 
					 | 
				
			||||||
    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
 | 
					 | 
				
			||||||
           (c == '.') || (c == '_') || (c == '/');
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool IsWhitespace(char c) {
 | 
					 | 
				
			||||||
    return c == ' ' || c == '\t' || c == '\n' || c == '\r';
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void SkipWhitespace(rt_parse_state *state) {
 | 
					 | 
				
			||||||
    while (state->at < state->length && IsWhitespace(state->text[state->at])) {
 | 
					 | 
				
			||||||
        if (state->text[state->at] == '\n')
 | 
					 | 
				
			||||||
            ++state->line;
 | 
					 | 
				
			||||||
        ++state->at;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseAttribute(rt_parse_state *state, rt_text_span *_name) {
 | 
					 | 
				
			||||||
    rt_text_span name;
 | 
					 | 
				
			||||||
    name.start  = &state->text[state->at];
 | 
					 | 
				
			||||||
    name.length = 0;
 | 
					 | 
				
			||||||
    while (state->at < state->length && !IsWhitespace(state->text[state->at])) {
 | 
					 | 
				
			||||||
        if (IsAllowedChar(state->text[state->at])) {
 | 
					 | 
				
			||||||
            ++state->at;
 | 
					 | 
				
			||||||
            ++name.length;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            rtReportError("GFX",
 | 
					 | 
				
			||||||
                          "%s:%d Unexpected character %c",
 | 
					 | 
				
			||||||
                          state->file,
 | 
					 | 
				
			||||||
                          state->line,
 | 
					 | 
				
			||||||
                          state->text[state->at]);
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    *_name = name;
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseValue(rt_parse_state *state, rt_text_span *_value) {
 | 
					 | 
				
			||||||
    rt_text_span value;
 | 
					 | 
				
			||||||
    value.start  = &state->text[state->at];
 | 
					 | 
				
			||||||
    value.length = 0;
 | 
					 | 
				
			||||||
    while (state->at < state->length && !IsWhitespace(state->text[state->at]) &&
 | 
					 | 
				
			||||||
           state->text[state->at] != ';') {
 | 
					 | 
				
			||||||
        if (IsAllowedChar(state->text[state->at])) {
 | 
					 | 
				
			||||||
            ++state->at;
 | 
					 | 
				
			||||||
            ++value.length;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            rtReportError("GFX",
 | 
					 | 
				
			||||||
                          "%s:%d Unexpected character %c",
 | 
					 | 
				
			||||||
                          state->file,
 | 
					 | 
				
			||||||
                          state->line,
 | 
					 | 
				
			||||||
                          state->text[state->at]);
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    *_value = value;
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define BLOCK_BEGIN        "BEGIN"
 | 
					 | 
				
			||||||
#define BLOCK_BEGIN_LENGTH 5
 | 
					 | 
				
			||||||
#define BLOCK_END        "END"
 | 
					 | 
				
			||||||
#define BLOCK_END_LENGTH 3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_INLINE static bool IsBlockBegin(rt_parse_state *state) {
 | 
					 | 
				
			||||||
    return (state->length - state->at >= BLOCK_BEGIN_LENGTH) &&
 | 
					 | 
				
			||||||
           (memcmp(&state->text[state->at], BLOCK_BEGIN, BLOCK_BEGIN_LENGTH) == 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RT_INLINE static bool IsBlockEnd(rt_parse_state *state) {
 | 
					 | 
				
			||||||
    return (state->length - state->at >= BLOCK_END_LENGTH) &&
 | 
					 | 
				
			||||||
           (memcmp(&state->text[state->at], BLOCK_END, BLOCK_END_LENGTH) == 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseBlock(rt_parse_state *state, rt_text_span *p_value) {
 | 
					 | 
				
			||||||
    rt_text_span value;
 | 
					 | 
				
			||||||
    value.start = &state->text[state->at];
 | 
					 | 
				
			||||||
    value.length = 0;
 | 
					 | 
				
			||||||
    while (state->at < state->length) {
 | 
					 | 
				
			||||||
        if (state->text[state->at] == BLOCK_END[0] && IsBlockEnd(state)) {
 | 
					 | 
				
			||||||
            *p_value = value;
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ++value.length;
 | 
					 | 
				
			||||||
        ++state->at;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    /* did not find END */
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseStmtList(rt_parse_state *state, unsigned int *list_index);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseStmt(rt_parse_state *state, unsigned int *stmt_index) {
 | 
					 | 
				
			||||||
    rt_parsed_stmt stmt;
 | 
					 | 
				
			||||||
    stmt.next = UINT_MAX; /* end of list */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    SkipWhitespace(state);
 | 
					 | 
				
			||||||
    if (!ParseAttribute(state, &stmt.attribute))
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    SkipWhitespace(state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (state->at == state->length) {
 | 
					 | 
				
			||||||
        rtReportError("GFX", "%s:%d Expected either a value or '{'", state->file, state->line);
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (state->text[state->at] == '{') {
 | 
					 | 
				
			||||||
        /* Consume '{' */
 | 
					 | 
				
			||||||
        ++state->at;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        stmt.form = RT_STMT_FORM_LIST;
 | 
					 | 
				
			||||||
        if (!ParseStmtList(state, &stmt.list_index))
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* Consume '}' */
 | 
					 | 
				
			||||||
        if (state->at < state->length && state->text[state->at] == '}')
 | 
					 | 
				
			||||||
            ++state->at;
 | 
					 | 
				
			||||||
    } else if (IsBlockBegin(state)) {
 | 
					 | 
				
			||||||
        /* Consume BEGIN */
 | 
					 | 
				
			||||||
        state->at += BLOCK_BEGIN_LENGTH;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        stmt.form = RT_STMT_FORM_BLOCK;
 | 
					 | 
				
			||||||
        if (!ParseBlock(state, &stmt.block))
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* Consume END */
 | 
					 | 
				
			||||||
        state->at += BLOCK_END_LENGTH;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        stmt.form = RT_STMT_FORM_VALUE;
 | 
					 | 
				
			||||||
        if (!ParseValue(state, &stmt.value))
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* Consume ';' */
 | 
					 | 
				
			||||||
        if (state->at < state->length && state->text[state->at] == ';')
 | 
					 | 
				
			||||||
            ++state->at;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    SkipWhitespace(state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Add statement to array */
 | 
					 | 
				
			||||||
    if (state->statement_count == state->statement_capacity) {
 | 
					 | 
				
			||||||
        unsigned int cap     = (state->statement_capacity > 0) ? state->statement_capacity * 2 : 64;
 | 
					 | 
				
			||||||
        rt_parsed_stmt *temp = realloc(state->statements, sizeof(rt_parsed_stmt) * cap);
 | 
					 | 
				
			||||||
        if (!temp) {
 | 
					 | 
				
			||||||
            rtReportError("GFX", "While parsing %s: Out of memory\n", state->file);
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        state->statements         = temp;
 | 
					 | 
				
			||||||
        state->statement_capacity = cap;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    state->statements[state->statement_count] = stmt;
 | 
					 | 
				
			||||||
    *stmt_index                               = state->statement_count++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseStmtList(rt_parse_state *state, unsigned int *list_index) {
 | 
					 | 
				
			||||||
    rt_parsed_stmt_list list;
 | 
					 | 
				
			||||||
    list.first = UINT_MAX;
 | 
					 | 
				
			||||||
    list.count = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsigned int last = UINT_MAX;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while (state->at < state->length && state->text[state->at] != '}') {
 | 
					 | 
				
			||||||
        unsigned int stmt;
 | 
					 | 
				
			||||||
        if (!ParseStmt(state, &stmt))
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (last != UINT_MAX)
 | 
					 | 
				
			||||||
            state->statements[last].next = stmt;
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            list.first = stmt;
 | 
					 | 
				
			||||||
        last = stmt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ++list.count;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Add list to array */
 | 
					 | 
				
			||||||
    if (state->statement_list_count == state->statement_list_capacity) {
 | 
					 | 
				
			||||||
        unsigned int cap =
 | 
					 | 
				
			||||||
            (state->statement_list_capacity > 0) ? state->statement_list_capacity * 2 : 64;
 | 
					 | 
				
			||||||
        rt_parsed_stmt_list *temp =
 | 
					 | 
				
			||||||
            realloc(state->statement_lists, sizeof(rt_parsed_stmt_list) * cap);
 | 
					 | 
				
			||||||
        if (!temp) {
 | 
					 | 
				
			||||||
            rtReportError("GFX", "While parsing %s: Out of memory\n", state->file);
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        state->statement_lists         = temp;
 | 
					 | 
				
			||||||
        state->statement_list_capacity = cap;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    state->statement_lists[state->statement_list_count] = list;
 | 
					 | 
				
			||||||
    *list_index                                         = state->statement_list_count++;
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const rt_parsed_stmt * rtFindStatement(const rt_parse_state *state, unsigned int list_index, const char *attribute) {
 | 
					 | 
				
			||||||
    if (list_index >= state->statement_list_count)
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
    const rt_parsed_stmt_list *list = &state->statement_lists[list_index];
 | 
					 | 
				
			||||||
    unsigned int stmt_index         = list->first;
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < list->count; ++i) {
 | 
					 | 
				
			||||||
        const rt_parsed_stmt *stmt = &state->statements[stmt_index];
 | 
					 | 
				
			||||||
        if (rtCompareSpanToString(stmt->attribute, attribute) == 0)
 | 
					 | 
				
			||||||
            return stmt;
 | 
					 | 
				
			||||||
        stmt_index = stmt->next;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtParseDescription(const char *text,
 | 
					 | 
				
			||||||
                             size_t length,
 | 
					 | 
				
			||||||
                             const char *file_path,
 | 
					 | 
				
			||||||
                             unsigned int *_root_list,
 | 
					 | 
				
			||||||
                             rt_parse_state *_state) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_parse_state state = {.text                    = text,
 | 
					 | 
				
			||||||
                            .at                      = 0,
 | 
					 | 
				
			||||||
                            .length                  = length,
 | 
					 | 
				
			||||||
                            .line                    = 1,
 | 
					 | 
				
			||||||
                            .file                    = file_path,
 | 
					 | 
				
			||||||
                            .statements              = NULL,
 | 
					 | 
				
			||||||
                            .statement_lists         = NULL,
 | 
					 | 
				
			||||||
                            .statement_capacity      = 0,
 | 
					 | 
				
			||||||
                            .statement_list_capacity = 0};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsigned int root_list = 0;
 | 
					 | 
				
			||||||
    if (!ParseStmtList(&state, &root_list)) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    *_root_list = root_list;
 | 
					 | 
				
			||||||
    *_state     = state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtReleaseParseState(rt_parse_state *state) {
 | 
					 | 
				
			||||||
    free(state->statements);
 | 
					 | 
				
			||||||
    free(state->statement_lists);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,55 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_DESCRIPTION_PARSER_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_DESCRIPTION_PARSER_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/runtime.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    RT_STMT_FORM_VALUE,
 | 
					 | 
				
			||||||
    RT_STMT_FORM_LIST,
 | 
					 | 
				
			||||||
    RT_STMT_FORM_BLOCK,
 | 
					 | 
				
			||||||
} rt_stmt_form;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    unsigned int first;
 | 
					 | 
				
			||||||
    unsigned int count;
 | 
					 | 
				
			||||||
} rt_parsed_stmt_list;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_stmt_form form;
 | 
					 | 
				
			||||||
    rt_text_span attribute;
 | 
					 | 
				
			||||||
    union {
 | 
					 | 
				
			||||||
        rt_text_span value;
 | 
					 | 
				
			||||||
        rt_text_span block;
 | 
					 | 
				
			||||||
        unsigned int list_index;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    /* For lists */
 | 
					 | 
				
			||||||
    unsigned int next;
 | 
					 | 
				
			||||||
} rt_parsed_stmt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    const char *file;
 | 
					 | 
				
			||||||
    const char *text;
 | 
					 | 
				
			||||||
    size_t at;
 | 
					 | 
				
			||||||
    size_t length;
 | 
					 | 
				
			||||||
    int line;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_parsed_stmt *statements;
 | 
					 | 
				
			||||||
    unsigned int statement_count;
 | 
					 | 
				
			||||||
    unsigned int statement_capacity;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_parsed_stmt_list *statement_lists;
 | 
					 | 
				
			||||||
    unsigned int statement_list_count;
 | 
					 | 
				
			||||||
    unsigned int statement_list_capacity;
 | 
					 | 
				
			||||||
} rt_parse_state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtParseDescription(const char *text,
 | 
					 | 
				
			||||||
                             size_t length,
 | 
					 | 
				
			||||||
                             const char *file_path,
 | 
					 | 
				
			||||||
                             unsigned int *root_list,
 | 
					 | 
				
			||||||
                             rt_parse_state *state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const rt_parsed_stmt *rtFindStatement(const rt_parse_state *state, unsigned int list_index, const char *attribute);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtReleaseParseState(rt_parse_state *state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,119 +0,0 @@
 | 
				
			|||||||
#include "utils.h"
 | 
					 | 
				
			||||||
#include "assetmeta.h"
 | 
					 | 
				
			||||||
#include "processing.h"
 | 
					 | 
				
			||||||
#include "assetsettings.h"
 | 
					 | 
				
			||||||
#include "packages.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/uidtab.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    char path_scratch[1024];
 | 
					 | 
				
			||||||
    unsigned int path_end;
 | 
					 | 
				
			||||||
} rt_discovery_data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_result LoadCompressedAsset(rt_uid uid, void **buffer, size_t size) {
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_result DirectoryHandler(const char *name, rtIterateDirElementType type, void *user) {
 | 
					 | 
				
			||||||
    rt_discovery_data *data = user;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t name_len = strlen(name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (type == RT_DIR_ELEMENT_TYPE_FILE) {
 | 
					 | 
				
			||||||
        /* Skip files we don't want to process */
 | 
					 | 
				
			||||||
        if (name_len >= 3) {
 | 
					 | 
				
			||||||
            if (memcmp(&name[name_len - 3], ".as", 3) == 0)
 | 
					 | 
				
			||||||
                return RT_SUCCESS;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (name_len >= 4) {
 | 
					 | 
				
			||||||
            if (memcmp(&name[name_len - 4], ".pkg", 4) == 0)
 | 
					 | 
				
			||||||
                return RT_SUCCESS;
 | 
					 | 
				
			||||||
            else if (memcmp(&name[name_len - 4], ".bin", 4) == 0)
 | 
					 | 
				
			||||||
                return RT_SUCCESS;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (strcmp(name, "packages.txt") == 0)
 | 
					 | 
				
			||||||
            return RT_SUCCESS;
 | 
					 | 
				
			||||||
        if (name[0] == '.') {
 | 
					 | 
				
			||||||
            return RT_SUCCESS;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* Check if we know that file */
 | 
					 | 
				
			||||||
        if (data->path_end > 0) {
 | 
					 | 
				
			||||||
            data->path_scratch[data->path_end] = '/';
 | 
					 | 
				
			||||||
            memcpy(data->path_scratch + data->path_end + 1, name, name_len);
 | 
					 | 
				
			||||||
            data->path_scratch[data->path_end + 1 + name_len] = '\0';
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            memcpy(data->path_scratch, name, name_len);
 | 
					 | 
				
			||||||
            data->path_scratch[name_len] = '\0';
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rt_file_id fid = rtAddFile(data->path_scratch);
 | 
					 | 
				
			||||||
        if (rtLookupUID(fid) != RT_INVALID_UID) {
 | 
					 | 
				
			||||||
            rt_assetmeta meta = {0};
 | 
					 | 
				
			||||||
            if (!rtGetAssetMeta(fid, &meta) || (meta.last_changed >= meta.compiled_ts)) {
 | 
					 | 
				
			||||||
                /* The file (may have) changed */
 | 
					 | 
				
			||||||
                rt_result res = rtAddFileToProcessingQueue(fid, meta.processing_flags);
 | 
					 | 
				
			||||||
                if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
                    return res;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                /* The file is unchanged, we just need to add it to the output again. */
 | 
					 | 
				
			||||||
                rtLog("ASSETC", "File %s is unchanged.", data->path_scratch);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                rt_asset_settings settings = {0};
 | 
					 | 
				
			||||||
                if (rtLoadAssetSettings(data->path_scratch, &settings) != RT_SUCCESS) {
 | 
					 | 
				
			||||||
                    rtLog("ASSETC", "Failed to load settings for %s", data->path_scratch);
 | 
					 | 
				
			||||||
                    return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                /* We need to load the processed data and add it to the package */
 | 
					 | 
				
			||||||
                rt_uid uid = rtLookupUID(fid);
 | 
					 | 
				
			||||||
                if (uid == RT_INVALID_UID) {
 | 
					 | 
				
			||||||
                    rtLog("ASSETC", "Failed to lookup UID of known asset %s", data->path_scratch);
 | 
					 | 
				
			||||||
                    return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                rtAddUnchangedAssetToPackage(settings.package, uid);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            /* Process it */
 | 
					 | 
				
			||||||
            rt_asset_settings settings = {0};
 | 
					 | 
				
			||||||
            if (rtLoadAssetSettings(data->path_scratch, &settings) != RT_SUCCESS) {
 | 
					 | 
				
			||||||
                rtLog("ASSETC", "Failed to load settings for %s", data->path_scratch);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            rt_result res = rtAddFileToProcessingQueue(fid, settings.processing_flags);
 | 
					 | 
				
			||||||
            if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
                return res;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else if (type == RT_DIR_ELEMENT_TYPE_DIRECTORY) {
 | 
					 | 
				
			||||||
        if (strcmp(name, ".") == 0)
 | 
					 | 
				
			||||||
            return RT_SUCCESS;
 | 
					 | 
				
			||||||
        if (strcmp(name, "..") == 0)
 | 
					 | 
				
			||||||
            return RT_SUCCESS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        unsigned int path_end_before = data->path_end;
 | 
					 | 
				
			||||||
        if (data->path_end > 0)
 | 
					 | 
				
			||||||
            data->path_scratch[data->path_end++] = '/';
 | 
					 | 
				
			||||||
        memcpy(data->path_scratch + data->path_end, name, name_len);
 | 
					 | 
				
			||||||
        data->path_scratch[data->path_end + name_len] = '\0';
 | 
					 | 
				
			||||||
        data->path_end += (unsigned int)name_len;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rt_result res = rtIterateDirectory(data->path_scratch, user, DirectoryHandler);
 | 
					 | 
				
			||||||
        if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
            return res;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        data->path_end = path_end_before;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtDiscoverAssets(void) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_discovery_data data;
 | 
					 | 
				
			||||||
    memcpy(data.path_scratch, "assets", sizeof("assets"));
 | 
					 | 
				
			||||||
    data.path_end = sizeof("assets") - 1;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    rtIterateDirectory("assets", &data, DirectoryHandler);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,28 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_OPTIONS_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_OPTIONS_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/assets.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    /* No optimization */
 | 
					 | 
				
			||||||
    RT_ASSET_OPTIMIZATION_NONE,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Create small assets */
 | 
					 | 
				
			||||||
    RT_ASSET_OPTIMIZATION_SPACE,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Create assets for fast execution */
 | 
					 | 
				
			||||||
    RT_ASSET_OPTIMIZATION_PERFORMANCE,
 | 
					 | 
				
			||||||
} rt_asset_optimization_level;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Options parsed from command line arguments */
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    const char *root_directory;
 | 
					 | 
				
			||||||
    rt_renderer_backend_code renderer_backend;
 | 
					 | 
				
			||||||
    rt_asset_optimization_level optimization;
 | 
					 | 
				
			||||||
} rt_assetc_options;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef RT_ASSETC_DONT_DEFINE_OPTIONS_GLOBAL
 | 
					 | 
				
			||||||
extern rt_assetc_options g_assetc_options;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,274 +0,0 @@
 | 
				
			|||||||
#include "packages.h"
 | 
					 | 
				
			||||||
#include "processing.h"
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define RT_DEFINE_PACKAGE_FILE_STRUCTURES
 | 
					 | 
				
			||||||
#include "runtime/threading.h"
 | 
					 | 
				
			||||||
#include "runtime/assets.h"
 | 
					 | 
				
			||||||
#include "runtime/file_tab.h"
 | 
					 | 
				
			||||||
#include "runtime/packages.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <limits.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "xxhash/xxhash.h"
 | 
					 | 
				
			||||||
#include "lz4/lz4.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_uid uid;
 | 
					 | 
				
			||||||
    size_t disk_size;
 | 
					 | 
				
			||||||
} rt_package_entry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    char *name;
 | 
					 | 
				
			||||||
    unsigned int num_entries;
 | 
					 | 
				
			||||||
    unsigned int entry_capacity;
 | 
					 | 
				
			||||||
    rt_package_entry *entries;
 | 
					 | 
				
			||||||
} rt_package;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAX_PACKAGES 1024
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_package _packages[MAX_PACKAGES];
 | 
					 | 
				
			||||||
unsigned int _package_count = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_mutex *_guard;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
unsigned int rtAddPackageFile(rt_text_span name) {
 | 
					 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < _package_count; ++i) {
 | 
					 | 
				
			||||||
        if (rtCompareSpanToString(name, _packages[i].name + 5) == 0) {
 | 
					 | 
				
			||||||
            rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
            return i;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Create a new package */
 | 
					 | 
				
			||||||
    _packages[_package_count].name = malloc(name.length + 1 + 5);
 | 
					 | 
				
			||||||
    if (!_packages[_package_count].name) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC",
 | 
					 | 
				
			||||||
                      "Failed to allocate storage for new package %*.s",
 | 
					 | 
				
			||||||
                      name.length,
 | 
					 | 
				
			||||||
                      name.start);
 | 
					 | 
				
			||||||
        rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
        return UINT_MAX;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    memcpy(_packages[_package_count].name, "data/", 5);
 | 
					 | 
				
			||||||
    memcpy(_packages[_package_count].name + 5, name.start, name.length);
 | 
					 | 
				
			||||||
    _packages[_package_count].name[name.length + 5] = '\0';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsigned int index = _package_count++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
    return index;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtInitPackages(void) {
 | 
					 | 
				
			||||||
    _guard = rtCreateMutex();
 | 
					 | 
				
			||||||
    /* Create the default package (0) */
 | 
					 | 
				
			||||||
    rtAddPackageFile((rt_text_span){.start = "default.pkg", .length = 12});
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void AddAssetToPackageImpl(unsigned int package,
 | 
					 | 
				
			||||||
                                  rt_uid uid,
 | 
					 | 
				
			||||||
                                  void *buffer,
 | 
					 | 
				
			||||||
                                  size_t size,
 | 
					 | 
				
			||||||
                                  bool needs_compression) {
 | 
					 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
    if (package >= _package_count) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Trying to add an asset to a non-existing package.");
 | 
					 | 
				
			||||||
        rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_package *pkg = &_packages[package];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < pkg->num_entries; ++i) {
 | 
					 | 
				
			||||||
        if (pkg->entries[i].uid == uid) {
 | 
					 | 
				
			||||||
            rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (pkg->num_entries == pkg->entry_capacity) {
 | 
					 | 
				
			||||||
        unsigned int new_cap = (pkg->entry_capacity > 0) ? 2 * pkg->entry_capacity : 256;
 | 
					 | 
				
			||||||
        rt_package_entry *n  = realloc(pkg->entries, new_cap * sizeof(rt_package_entry));
 | 
					 | 
				
			||||||
        if (!n) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to grow storage for package %u.", package);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        pkg->entry_capacity = new_cap;
 | 
					 | 
				
			||||||
        pkg->entries        = n;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    char tmp_path[256];
 | 
					 | 
				
			||||||
    snprintf(tmp_path, 256, "actemp/%u.bin", uid);
 | 
					 | 
				
			||||||
    if (needs_compression) {
 | 
					 | 
				
			||||||
        FILE *tmp_f = fopen(tmp_path, "wb");
 | 
					 | 
				
			||||||
        if (!tmp_f) {
 | 
					 | 
				
			||||||
            rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        int required_size       = LZ4_compressBound((int)size);
 | 
					 | 
				
			||||||
        void *compressed_buffer = malloc(required_size);
 | 
					 | 
				
			||||||
        if (!compressed_buffer) {
 | 
					 | 
				
			||||||
            fclose(tmp_f);
 | 
					 | 
				
			||||||
            rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        int compressed_bytes =
 | 
					 | 
				
			||||||
            LZ4_compress_default(buffer, compressed_buffer, (int)size, required_size);
 | 
					 | 
				
			||||||
        if (compressed_bytes == 0) {
 | 
					 | 
				
			||||||
            free(compressed_buffer);
 | 
					 | 
				
			||||||
            fclose(tmp_f);
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to compress asset %x of package %s", uid, pkg->name);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rt_package_asset_header header;
 | 
					 | 
				
			||||||
        XXH64_hash_t checksum = XXH3_64bits_withSeed(buffer, (size_t)compressed_bytes, 0);
 | 
					 | 
				
			||||||
        XXH64_canonicalFromHash(&header.checksum, checksum);
 | 
					 | 
				
			||||||
        header.decompressed_size = (uint32_t)size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (fwrite(&header, sizeof(header), 1, tmp_f) != 1) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to write to actemp/%u.bin", uid);
 | 
					 | 
				
			||||||
            free(compressed_buffer);
 | 
					 | 
				
			||||||
            fclose(tmp_f);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (fwrite(buffer, compressed_bytes, 1, tmp_f) != 1) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to write to actemp/%u.bin", uid);
 | 
					 | 
				
			||||||
            free(compressed_buffer);
 | 
					 | 
				
			||||||
            fclose(tmp_f);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fclose(tmp_f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pkg->entries[pkg->num_entries].disk_size =
 | 
					 | 
				
			||||||
            (size_t)compressed_bytes + sizeof(rt_package_asset_header);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        pkg->entries[pkg->num_entries].disk_size = rtGetFileSize(tmp_path);
 | 
					 | 
				
			||||||
        if (pkg->entries[pkg->num_entries].disk_size == 0) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to determine size of actemp/%u.bin", uid);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    pkg->entries[pkg->num_entries].uid               = uid;
 | 
					 | 
				
			||||||
    ++pkg->num_entries;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtAddAssetToPackage(unsigned int package, rt_uid uid, void *buffer, size_t size) {
 | 
					 | 
				
			||||||
    AddAssetToPackageImpl(package, uid, buffer, size, true);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtAddUnchangedAssetToPackage(unsigned int package, rt_uid uid) {
 | 
					 | 
				
			||||||
    AddAssetToPackageImpl(package, uid, NULL, 0, false);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_result SavePackage(rt_package *pkg) {
 | 
					 | 
				
			||||||
    if (pkg->num_entries == 0) {
 | 
					 | 
				
			||||||
        rtLog("ASSETC", "Package %s has no entries.", pkg->name);
 | 
					 | 
				
			||||||
        return RT_SUCCESS;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t current_buffer_size = 0;
 | 
					 | 
				
			||||||
    void *buffer               = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t offset_in_file = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_file_id package_fid = rtAddFile(pkg->name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FILE *f = fopen(pkg->name, "wb");
 | 
					 | 
				
			||||||
    if (!f) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to open %s for writing.", pkg->name);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < pkg->num_entries; ++i) {
 | 
					 | 
				
			||||||
        char tmp_path[256];
 | 
					 | 
				
			||||||
        snprintf(tmp_path, 256, "actemp/%u.bin", pkg->entries[i].uid);
 | 
					 | 
				
			||||||
        FILE *tmp_f = fopen(tmp_path, "rb");
 | 
					 | 
				
			||||||
        if (!tmp_f) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to open %s for reading.", tmp_path);
 | 
					 | 
				
			||||||
            fclose(f);
 | 
					 | 
				
			||||||
            free(buffer);
 | 
					 | 
				
			||||||
            return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (current_buffer_size < pkg->entries[i].disk_size) {
 | 
					 | 
				
			||||||
            void *tmp = realloc(buffer, pkg->entries[i].disk_size);
 | 
					 | 
				
			||||||
            if (!tmp) {
 | 
					 | 
				
			||||||
                rtReportError("ASSETC", "Failed to allocate buffer (%zu bytes) for reading %s.", pkg->entries[i].disk_size, tmp_path);
 | 
					 | 
				
			||||||
                fclose(f);
 | 
					 | 
				
			||||||
                fclose(tmp_f);
 | 
					 | 
				
			||||||
                free(buffer);
 | 
					 | 
				
			||||||
                return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            buffer = tmp;
 | 
					 | 
				
			||||||
            current_buffer_size = pkg->entries[i].disk_size;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (fread(buffer, pkg->entries[i].disk_size, 1, tmp_f) != 1) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to read %s.", tmp_path);
 | 
					 | 
				
			||||||
            fclose(f);
 | 
					 | 
				
			||||||
            fclose(tmp_f);
 | 
					 | 
				
			||||||
            free(buffer);
 | 
					 | 
				
			||||||
            return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (fwrite(buffer, pkg->entries[i].disk_size, 1, f) != 1) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to write (%zu bytes) to %s.", pkg->entries[i].disk_size, pkg->name);
 | 
					 | 
				
			||||||
            fclose(f);
 | 
					 | 
				
			||||||
            fclose(tmp_f);
 | 
					 | 
				
			||||||
            free(buffer);
 | 
					 | 
				
			||||||
            return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rtAddUIDTabEntry(package_fid,
 | 
					 | 
				
			||||||
                         pkg->entries[i].uid,
 | 
					 | 
				
			||||||
                         offset_in_file,
 | 
					 | 
				
			||||||
                         pkg->entries[i].disk_size);
 | 
					 | 
				
			||||||
        offset_in_file += pkg->entries[i].disk_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fclose(tmp_f);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    free(buffer);
 | 
					 | 
				
			||||||
    fclose(f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtSavePackages(void) {
 | 
					 | 
				
			||||||
    assert(rtIsMainThread());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Save a .txt file with one line per package.
 | 
					 | 
				
			||||||
     * Enables us to re-init the file-tab in future runs. */
 | 
					 | 
				
			||||||
    FILE *f = fopen("data/packages.txt", "w");
 | 
					 | 
				
			||||||
    if (!f) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to write to 'packages.txt'");
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < _package_count; ++i) {
 | 
					 | 
				
			||||||
        if (_packages[i].num_entries == 0)
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
        fprintf(f, "%s\n", _packages[i].name);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    fclose(f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < _package_count; ++i) {
 | 
					 | 
				
			||||||
        rt_result res = SavePackage(&_packages[i]);
 | 
					 | 
				
			||||||
        if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
            return res;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,16 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_PACKAGES_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_PACKAGES_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/runtime.h"
 | 
					 | 
				
			||||||
#include "runtime/assets.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtInitPackages(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
unsigned int rtAddPackageFile(rt_text_span name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtAddAssetToPackage(unsigned int package, rt_uid uid, void *buffer, size_t size);
 | 
					 | 
				
			||||||
void rtAddUnchangedAssetToPackage(unsigned int package, rt_uid uid);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtSavePackages(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,423 +0,0 @@
 | 
				
			|||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <limits.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stddef.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/aio.h"
 | 
					 | 
				
			||||||
#include "runtime/gfx.h"
 | 
					 | 
				
			||||||
#include "runtime/handles.h"
 | 
					 | 
				
			||||||
#include "runtime/runtime.h"
 | 
					 | 
				
			||||||
#include "runtime/buffer_manager.h"
 | 
					 | 
				
			||||||
#include "runtime/renderer_api.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "assetmeta.h"
 | 
					 | 
				
			||||||
#include "description_parser.h"
 | 
					 | 
				
			||||||
#include "options.h"
 | 
					 | 
				
			||||||
#include "processing.h"
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
#include "processing_flags.h"
 | 
					 | 
				
			||||||
#include "dependency_tracking.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_attribute_binding *uniform_bindings;
 | 
					 | 
				
			||||||
    rt_attribute_binding *storage_bindings;
 | 
					 | 
				
			||||||
    rt_attribute_binding *texture_bindings;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_uid vertex_shader;
 | 
					 | 
				
			||||||
    rt_uid fragment_shader;
 | 
					 | 
				
			||||||
    rt_uid compute_shader;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* TODO Fixed function settings */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Sampler settings */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uint16_t uniform_binding_count;
 | 
					 | 
				
			||||||
    uint16_t storage_binding_count;
 | 
					 | 
				
			||||||
    uint16_t texture_binding_count;
 | 
					 | 
				
			||||||
} rt_parsed_pipeline_data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
DbgPrintShaderFile(const rt_parse_state *state, unsigned int list_index, unsigned int indent) {
 | 
					 | 
				
			||||||
    assert(list_index < state->statement_list_count);
 | 
					 | 
				
			||||||
    const rt_parsed_stmt_list *list = &state->statement_lists[list_index];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsigned int stmt_index = list->first;
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < list->count; ++i) {
 | 
					 | 
				
			||||||
        const rt_parsed_stmt *stmt = &state->statements[stmt_index];
 | 
					 | 
				
			||||||
        for (unsigned int j = 0; j < indent; ++j)
 | 
					 | 
				
			||||||
            printf(" ");
 | 
					 | 
				
			||||||
        printf("%.*s: ", stmt->attribute.length, stmt->attribute.start);
 | 
					 | 
				
			||||||
        if (stmt->form == RT_STMT_FORM_VALUE) {
 | 
					 | 
				
			||||||
            printf("%.*s\n", stmt->value.length, stmt->value.start);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            printf("{\n");
 | 
					 | 
				
			||||||
            DbgPrintShaderFile(state, stmt->list_index, indent + 2);
 | 
					 | 
				
			||||||
            printf("}\n");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        stmt_index = stmt->next;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    assert(stmt_index == UINT_MAX || stmt_index == 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseBindingIndex(rt_text_span span, unsigned int *index) {
 | 
					 | 
				
			||||||
    if (span.length == 0)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    int at           = (int)span.length - 1;
 | 
					 | 
				
			||||||
    unsigned int exp = 1;
 | 
					 | 
				
			||||||
    unsigned int n   = 0;
 | 
					 | 
				
			||||||
    while (at >= 0) {
 | 
					 | 
				
			||||||
        if (span.start[at] >= '0' && span.start[at] <= '9') {
 | 
					 | 
				
			||||||
            unsigned int digit = (unsigned int)(span.start[at] - '0');
 | 
					 | 
				
			||||||
            n += digit * exp;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            rtReportError("GFX", "Unexpected non-digit character in binding index");
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        --at;
 | 
					 | 
				
			||||||
        exp *= 10;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    *index = n;
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_attribute_value ParseBindingValue(rt_text_span span) {
 | 
					 | 
				
			||||||
    if (rtCompareSpanToString(span, "MATERIAL_ALBEDO") == 0) {
 | 
					 | 
				
			||||||
        return RT_ATTRIBUTE_VALUE_MATERIAL_ALBEDO;
 | 
					 | 
				
			||||||
    } else if (rtCompareSpanToString(span, "MATERIAL_NORMAL") == 0) {
 | 
					 | 
				
			||||||
        return RT_ATTRIBUTE_VALUE_MATERIAL_NORMAL;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtReportError("GFX", "Unsupported binding value %*.s", span.length, span.start);
 | 
					 | 
				
			||||||
    return RT_ATTRIBUTE_VALUE_UNDEFINED;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool ParseBindings(rt_parse_state *state,
 | 
					 | 
				
			||||||
                          unsigned int root_list,
 | 
					 | 
				
			||||||
                          const char *name,
 | 
					 | 
				
			||||||
                          const char *file_path,
 | 
					 | 
				
			||||||
                          rt_attribute_binding **p_bindings,
 | 
					 | 
				
			||||||
                          uint16_t *p_binding_count) {
 | 
					 | 
				
			||||||
    const rt_parsed_stmt *bindings = rtFindStatement(state, root_list, name);
 | 
					 | 
				
			||||||
    if (bindings) {
 | 
					 | 
				
			||||||
        if (bindings->form != RT_STMT_FORM_LIST) {
 | 
					 | 
				
			||||||
            rtReportError("GFX",
 | 
					 | 
				
			||||||
                          "Expected list of bindings as the value of "
 | 
					 | 
				
			||||||
                          "\"%s\" in %s",
 | 
					 | 
				
			||||||
                          name,
 | 
					 | 
				
			||||||
                          file_path);
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const rt_parsed_stmt_list *binding_list = &state->statement_lists[bindings->list_index];
 | 
					 | 
				
			||||||
        rt_attribute_binding *shader_bindings =
 | 
					 | 
				
			||||||
            rtAllocBuffer(sizeof(rt_attribute_binding) * binding_list->count);
 | 
					 | 
				
			||||||
        if (!bindings) {
 | 
					 | 
				
			||||||
            rtReportError("GFX", "Out of memory");
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        unsigned int binding_count = binding_list->count;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        unsigned int stmt_index = binding_list->first;
 | 
					 | 
				
			||||||
        for (unsigned int i = 0; i < binding_list->count; ++i) {
 | 
					 | 
				
			||||||
            const rt_parsed_stmt *stmt = &state->statements[stmt_index];
 | 
					 | 
				
			||||||
            if (!ParseBindingIndex(stmt->attribute, &shader_bindings[i].index)) {
 | 
					 | 
				
			||||||
                rtReleaseBuffer(shader_bindings,
 | 
					 | 
				
			||||||
                                sizeof(rt_attribute_binding) * binding_list->count);
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            shader_bindings[i].value = ParseBindingValue(stmt->value);
 | 
					 | 
				
			||||||
            if (shader_bindings[i].value == RT_ATTRIBUTE_VALUE_UNDEFINED) {
 | 
					 | 
				
			||||||
                rtReleaseBuffer(shader_bindings,
 | 
					 | 
				
			||||||
                                sizeof(rt_attribute_binding) * binding_list->count);
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            stmt_index = stmt->next;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        *p_bindings      = shader_bindings;
 | 
					 | 
				
			||||||
        *p_binding_count = (uint16_t)binding_count;
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        *p_bindings      = NULL;
 | 
					 | 
				
			||||||
        *p_binding_count = 0;
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_result ParseShader(rt_parse_state *state,
 | 
					 | 
				
			||||||
                             unsigned int root_list,
 | 
					 | 
				
			||||||
                             const char *name,
 | 
					 | 
				
			||||||
                             const char *file_path,
 | 
					 | 
				
			||||||
                             uint32_t processing_flags,
 | 
					 | 
				
			||||||
                             rt_uid *p_shader_uid) {
 | 
					 | 
				
			||||||
    const rt_parsed_stmt *stmt = rtFindStatement(state, root_list, name);
 | 
					 | 
				
			||||||
    if (stmt) {
 | 
					 | 
				
			||||||
        if (stmt->form != RT_STMT_FORM_LIST) {
 | 
					 | 
				
			||||||
            rtReportError("GFX",
 | 
					 | 
				
			||||||
                          "Expected a list as the value of "
 | 
					 | 
				
			||||||
                          "\"%s\" in %s",
 | 
					 | 
				
			||||||
                          name,
 | 
					 | 
				
			||||||
                          file_path);
 | 
					 | 
				
			||||||
            return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const rt_parsed_stmt_list *shader_list = &state->statement_lists[stmt->list_index];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        unsigned int stmt_index = shader_list->first;
 | 
					 | 
				
			||||||
        for (unsigned int i = 0; i < shader_list->count; ++i) {
 | 
					 | 
				
			||||||
            const rt_parsed_stmt *shader = &state->statements[stmt_index];
 | 
					 | 
				
			||||||
            if (shader->form != RT_STMT_FORM_VALUE) {
 | 
					 | 
				
			||||||
                rtReportError("GFX",
 | 
					 | 
				
			||||||
                              "Expected a list as the value of "
 | 
					 | 
				
			||||||
                              "\"%s.%*s\" in %s",
 | 
					 | 
				
			||||||
                              name,
 | 
					 | 
				
			||||||
                              (int)shader->attribute.length,
 | 
					 | 
				
			||||||
                              shader->attribute.start,
 | 
					 | 
				
			||||||
                              file_path);
 | 
					 | 
				
			||||||
                return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            rt_renderer_backend_code backend = RT_INVALID_RENDERER_BACKEND_CODE;
 | 
					 | 
				
			||||||
            if (rtCompareSpanToString(shader->attribute, "vk") == 0) {
 | 
					 | 
				
			||||||
                backend = RT_RENDERER_BACKEND_CODE_VK;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                rtReportError("GFX",
 | 
					 | 
				
			||||||
                              "Invalid renderer backend"
 | 
					 | 
				
			||||||
                              "\"%*s\" in %s of file %s",
 | 
					 | 
				
			||||||
                              (int)shader->attribute.length,
 | 
					 | 
				
			||||||
                              shader->attribute.start,
 | 
					 | 
				
			||||||
                              name,
 | 
					 | 
				
			||||||
                              file_path);
 | 
					 | 
				
			||||||
                return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (backend == g_assetc_options.renderer_backend) {
 | 
					 | 
				
			||||||
                rt_file_id shader_file = rtAddFileFromSpan(shader->value);
 | 
					 | 
				
			||||||
                rt_uid uid             = rtLookupUID(shader_file);
 | 
					 | 
				
			||||||
                if (uid == RT_INVALID_UID) {
 | 
					 | 
				
			||||||
                    /* Add the shader file to processing and wait until its done
 | 
					 | 
				
			||||||
                     */
 | 
					 | 
				
			||||||
                    if (rtAddFileToProcessingQueue(shader_file, processing_flags) != RT_SUCCESS)
 | 
					 | 
				
			||||||
                        return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
                    return RT_PROCESSING_TRY_AGAIN;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                *p_shader_uid = uid;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                /* Don't break, because that validates the file. (attributes are checked for invalid
 | 
					 | 
				
			||||||
                 * values). */
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            stmt_index = shader->next;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return RT_SUCCESS;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static uint32_t
 | 
					 | 
				
			||||||
ParseOptimizationLevel(rt_parse_state *state, unsigned int root_list, const char *file_path) {
 | 
					 | 
				
			||||||
    uint32_t optimization_level;
 | 
					 | 
				
			||||||
    switch (g_assetc_options.optimization) { 
 | 
					 | 
				
			||||||
    case RT_ASSET_OPTIMIZATION_PERFORMANCE:
 | 
					 | 
				
			||||||
        optimization_level = RT_SHADER_FLAG_OPTIMIZE_SPEED;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case RT_ASSET_OPTIMIZATION_SPACE:
 | 
					 | 
				
			||||||
        optimization_level = RT_SHADER_FLAG_OPTIMIZE_SIZE;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
        optimization_level = 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const rt_parsed_stmt *stmt = rtFindStatement(state, root_list, "optimization");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (stmt) {
 | 
					 | 
				
			||||||
        if (stmt->form != RT_STMT_FORM_VALUE) {
 | 
					 | 
				
			||||||
            rtReportError("GFX",
 | 
					 | 
				
			||||||
                          "Expected a simple statement for"
 | 
					 | 
				
			||||||
                          "\"optimization\" in %s",
 | 
					 | 
				
			||||||
                          file_path);
 | 
					 | 
				
			||||||
            return optimization_level;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (rtCompareSpanToString(stmt->value, "speed") == 0) {
 | 
					 | 
				
			||||||
            optimization_level = RT_SHADER_FLAG_OPTIMIZE_SPEED;
 | 
					 | 
				
			||||||
        } else if (rtCompareSpanToString(stmt->value, "size") == 0) {
 | 
					 | 
				
			||||||
            optimization_level = RT_SHADER_FLAG_OPTIMIZE_SIZE;
 | 
					 | 
				
			||||||
        } else if (rtCompareSpanToString(stmt->value, "none") == 0) {
 | 
					 | 
				
			||||||
            optimization_level = 0;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            rtReportError("GFX",
 | 
					 | 
				
			||||||
                          "Expected one of 'speed', 'size' and 'none' for \"optimization\" in %s",
 | 
					 | 
				
			||||||
                          file_path);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return optimization_level;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_result
 | 
					 | 
				
			||||||
ParsePipelineFile(rt_file_id fid, const char *text, size_t length, rt_parsed_pipeline_data *pipeline) {
 | 
					 | 
				
			||||||
    /* This is the grammar for pipeline files:
 | 
					 | 
				
			||||||
     * <stmt-list> ::= <stmt>*
 | 
					 | 
				
			||||||
     * <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
 | 
					 | 
				
			||||||
     * <attribute> ::= [:alnum:]*
 | 
					 | 
				
			||||||
     * <value>:: = [:alnum:]* */
 | 
					 | 
				
			||||||
    const char *file_path = rtGetFilePath(fid);
 | 
					 | 
				
			||||||
    rt_parse_state state;
 | 
					 | 
				
			||||||
    unsigned int root_list;
 | 
					 | 
				
			||||||
    rt_result result = rtParseDescription(text, length, file_path, &root_list, &state);
 | 
					 | 
				
			||||||
    if (result != RT_SUCCESS) {
 | 
					 | 
				
			||||||
        goto out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* We allow the pipeline file to overwrite the optimization level */
 | 
					 | 
				
			||||||
    uint32_t optimization = ParseOptimizationLevel(&state, root_list, file_path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Process shader stages */
 | 
					 | 
				
			||||||
    if (ParseShader(&state,
 | 
					 | 
				
			||||||
                    root_list,
 | 
					 | 
				
			||||||
                    "vertex",
 | 
					 | 
				
			||||||
                    file_path,
 | 
					 | 
				
			||||||
                    RT_SHADER_FLAG_VERTEX | optimization,
 | 
					 | 
				
			||||||
                    &pipeline->vertex_shader) == RT_PROCESSING_TRY_AGAIN) {
 | 
					 | 
				
			||||||
        result = RT_PROCESSING_TRY_AGAIN;
 | 
					 | 
				
			||||||
        goto out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (ParseShader(&state,
 | 
					 | 
				
			||||||
                    root_list,
 | 
					 | 
				
			||||||
                    "fragment",
 | 
					 | 
				
			||||||
                    file_path,
 | 
					 | 
				
			||||||
                    RT_SHADER_FLAG_FRAGMENT | optimization,
 | 
					 | 
				
			||||||
                    &pipeline->fragment_shader) == RT_PROCESSING_TRY_AGAIN) {
 | 
					 | 
				
			||||||
        result = RT_PROCESSING_TRY_AGAIN;
 | 
					 | 
				
			||||||
        goto out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (ParseShader(&state,
 | 
					 | 
				
			||||||
                    root_list,
 | 
					 | 
				
			||||||
                    "compute",
 | 
					 | 
				
			||||||
                    file_path,
 | 
					 | 
				
			||||||
                    RT_SHADER_FLAG_COMPUTE | optimization,
 | 
					 | 
				
			||||||
                    &pipeline->compute_shader) == RT_PROCESSING_TRY_AGAIN) {
 | 
					 | 
				
			||||||
        result = RT_PROCESSING_TRY_AGAIN;
 | 
					 | 
				
			||||||
        goto out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Process bindings */
 | 
					 | 
				
			||||||
    pipeline->texture_bindings      = NULL;
 | 
					 | 
				
			||||||
    pipeline->texture_binding_count = 0;
 | 
					 | 
				
			||||||
    pipeline->uniform_bindings      = NULL;
 | 
					 | 
				
			||||||
    pipeline->uniform_binding_count = 0;
 | 
					 | 
				
			||||||
    pipeline->storage_bindings      = NULL;
 | 
					 | 
				
			||||||
    pipeline->storage_binding_count = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!ParseBindings(&state,
 | 
					 | 
				
			||||||
                       root_list,
 | 
					 | 
				
			||||||
                       "texture_bindings",
 | 
					 | 
				
			||||||
                       file_path,
 | 
					 | 
				
			||||||
                       &pipeline->texture_bindings,
 | 
					 | 
				
			||||||
                       &pipeline->texture_binding_count)) {
 | 
					 | 
				
			||||||
        result = RT_AIO_STATE_FAILED;
 | 
					 | 
				
			||||||
        goto out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!ParseBindings(&state,
 | 
					 | 
				
			||||||
                       root_list,
 | 
					 | 
				
			||||||
                       "uniform_bindings",
 | 
					 | 
				
			||||||
                       file_path,
 | 
					 | 
				
			||||||
                       &pipeline->uniform_bindings,
 | 
					 | 
				
			||||||
                       &pipeline->uniform_binding_count)) {
 | 
					 | 
				
			||||||
        result = RT_AIO_STATE_FAILED;
 | 
					 | 
				
			||||||
        goto out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!ParseBindings(&state,
 | 
					 | 
				
			||||||
                       root_list,
 | 
					 | 
				
			||||||
                       "storage_bindings",
 | 
					 | 
				
			||||||
                       file_path,
 | 
					 | 
				
			||||||
                       &pipeline->storage_bindings,
 | 
					 | 
				
			||||||
                       &pipeline->storage_binding_count)) {
 | 
					 | 
				
			||||||
        result = RT_AIO_STATE_FAILED;
 | 
					 | 
				
			||||||
        goto out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
    free(state.statements);
 | 
					 | 
				
			||||||
    free(state.statement_lists);
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtProcessPipelineFile(rt_file_id file,
 | 
					 | 
				
			||||||
                                void *buffer,
 | 
					 | 
				
			||||||
                                size_t size,
 | 
					 | 
				
			||||||
                                uint32_t flags,
 | 
					 | 
				
			||||||
                                rt_processor_output *output) {
 | 
					 | 
				
			||||||
    RT_UNUSED(flags);
 | 
					 | 
				
			||||||
    rt_parsed_pipeline_data tmp;
 | 
					 | 
				
			||||||
    memset(&tmp, 0, sizeof(tmp));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_result result = ParsePipelineFile(file, buffer, size, &tmp);
 | 
					 | 
				
			||||||
    if (result == RT_SUCCESS) {
 | 
					 | 
				
			||||||
        /* parsed_pipeline_data contains arrays of bindings.
 | 
					 | 
				
			||||||
         * We need to convert these to a flat buffer that can be written to a file */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        size_t outbuffer_size =
 | 
					 | 
				
			||||||
            sizeof(rt_pipeline_info) +
 | 
					 | 
				
			||||||
            sizeof(rt_attribute_binding) *
 | 
					 | 
				
			||||||
                (tmp.storage_binding_count + tmp.texture_binding_count + tmp.uniform_binding_count);
 | 
					 | 
				
			||||||
        void *out_buffer = rtAllocBuffer(outbuffer_size);
 | 
					 | 
				
			||||||
        if (!buffer) {
 | 
					 | 
				
			||||||
            return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        rt_pipeline_info *info      = out_buffer;
 | 
					 | 
				
			||||||
        info->vertex_shader         = tmp.vertex_shader;
 | 
					 | 
				
			||||||
        info->fragment_shader       = tmp.fragment_shader;
 | 
					 | 
				
			||||||
        info->compute_shader        = tmp.compute_shader;
 | 
					 | 
				
			||||||
        info->texture_binding_count = tmp.texture_binding_count;
 | 
					 | 
				
			||||||
        info->uniform_binding_count = tmp.uniform_binding_count;
 | 
					 | 
				
			||||||
        info->storage_binding_count = tmp.storage_binding_count;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rt_attribute_binding *texture_bindings = NULL;
 | 
					 | 
				
			||||||
        if (tmp.texture_binding_count > 0) {
 | 
					 | 
				
			||||||
            texture_bindings = (rt_attribute_binding *)(info + 1);
 | 
					 | 
				
			||||||
            memcpy(texture_bindings,
 | 
					 | 
				
			||||||
                   tmp.texture_bindings,
 | 
					 | 
				
			||||||
                   sizeof(rt_attribute_binding) * tmp.texture_binding_count);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        rt_attribute_binding *uniform_bindings = NULL;
 | 
					 | 
				
			||||||
        if (tmp.uniform_binding_count > 0) {
 | 
					 | 
				
			||||||
            uniform_bindings = texture_bindings + tmp.texture_binding_count;
 | 
					 | 
				
			||||||
            memcpy(uniform_bindings,
 | 
					 | 
				
			||||||
                   tmp.uniform_bindings,
 | 
					 | 
				
			||||||
                   sizeof(rt_attribute_binding) * tmp.uniform_binding_count);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        rt_attribute_binding *storage_bindings = NULL;
 | 
					 | 
				
			||||||
        if (tmp.storage_binding_count > 0) {
 | 
					 | 
				
			||||||
            storage_bindings = uniform_bindings + tmp.uniform_binding_count;
 | 
					 | 
				
			||||||
            memcpy(storage_bindings,
 | 
					 | 
				
			||||||
                   tmp.storage_bindings,
 | 
					 | 
				
			||||||
                   sizeof(rt_attribute_binding) * tmp.storage_binding_count);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        rtSetRelptr(&info->texture_bindings, texture_bindings);
 | 
					 | 
				
			||||||
        rtSetRelptr(&info->uniform_bindings, uniform_bindings);
 | 
					 | 
				
			||||||
        rtSetRelptr(&info->storage_bindings, storage_bindings);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rtReleaseBuffer(tmp.texture_bindings,
 | 
					 | 
				
			||||||
                        sizeof(rt_attribute_binding) * tmp.texture_binding_count);
 | 
					 | 
				
			||||||
        rtReleaseBuffer(tmp.storage_bindings,
 | 
					 | 
				
			||||||
                        sizeof(rt_attribute_binding) * tmp.storage_binding_count);
 | 
					 | 
				
			||||||
        rtReleaseBuffer(tmp.uniform_bindings,
 | 
					 | 
				
			||||||
                        sizeof(rt_attribute_binding) * tmp.uniform_binding_count);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        output->data      = out_buffer;
 | 
					 | 
				
			||||||
        output->size      = outbuffer_size;
 | 
					 | 
				
			||||||
        output->asset_uid = rtCalculateUID(rtGetFilePath(file));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* Store dependencies to shaders */
 | 
					 | 
				
			||||||
        if (info->vertex_shader != RT_INVALID_UID)
 | 
					 | 
				
			||||||
            result = rtAddAssetDependency(output->asset_uid, info->vertex_shader);
 | 
					 | 
				
			||||||
        if (info->fragment_shader != RT_INVALID_UID)
 | 
					 | 
				
			||||||
            result = rtAddAssetDependency(output->asset_uid, info->fragment_shader);
 | 
					 | 
				
			||||||
        if (info->compute_shader != RT_INVALID_UID)
 | 
					 | 
				
			||||||
            result = rtAddAssetDependency(output->asset_uid, info->compute_shader);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,46 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_PROCESSING_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_PROCESSING_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/assets.h"
 | 
					 | 
				
			||||||
#include "runtime/file_tab.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum {
 | 
					 | 
				
			||||||
    RT_PROCESSING_FAILED = RT_CUSTOM_ERROR_START,
 | 
					 | 
				
			||||||
    /* Used if the processing depends on other files beeing processed first. */
 | 
					 | 
				
			||||||
    RT_PROCESSING_TRY_AGAIN,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_file_id output_file;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} rt_asset_options;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_uid asset_uid;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Allocated via the rtAllocBuffer API */
 | 
					 | 
				
			||||||
    void *data;
 | 
					 | 
				
			||||||
    size_t size;
 | 
					 | 
				
			||||||
} rt_processor_output;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef rt_result rt_processor_fn(rt_file_id file,
 | 
					 | 
				
			||||||
                                  void *buffer,
 | 
					 | 
				
			||||||
                                  size_t size,
 | 
					 | 
				
			||||||
                                  uint32_t flags,
 | 
					 | 
				
			||||||
                                  rt_processor_output *output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtAddAssetProcessor(const char *file_extension, rt_processor_fn fn);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Flags are file type specific */
 | 
					 | 
				
			||||||
rt_result rtAddFileToProcessingQueue(rt_file_id file, uint32_t flags);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtStartProcessing(void);
 | 
					 | 
				
			||||||
void rtStopProcessing(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_uid rtCalculateUID(const char *name);
 | 
					 | 
				
			||||||
rt_result rtWriteUIDTab(void);
 | 
					 | 
				
			||||||
rt_result rtAddUIDTabEntry(rt_file_id package_fid, rt_uid uid, uint64_t offset, uint64_t size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtWaitUntilProcessingIsFinished(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,20 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_PROCESSING_FLAGS_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_PROCESSING_FLAGS_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Shader processing flags */
 | 
					 | 
				
			||||||
enum {
 | 
					 | 
				
			||||||
    /* Type is encoded in the lower bits */
 | 
					 | 
				
			||||||
    RT_SHADER_FLAG_VERTEX   = 0x01,
 | 
					 | 
				
			||||||
    RT_SHADER_FLAG_FRAGMENT = 0x02,
 | 
					 | 
				
			||||||
    RT_SHADER_FLAG_COMPUTE  = 0x03,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RT_SHADER_FLAG_TYPE_MASK =
 | 
					 | 
				
			||||||
        RT_SHADER_FLAG_VERTEX | RT_SHADER_FLAG_FRAGMENT | RT_SHADER_FLAG_COMPUTE,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RT_SHADER_FLAG_OPTIMIZE_SPEED = 0x04,
 | 
					 | 
				
			||||||
    RT_SHADER_FLAG_OPTIMIZE_SIZE  = 0x08,
 | 
					 | 
				
			||||||
    RT_SHADER_FLAG_OPTIMIZE_MASK  = RT_SHADER_FLAG_OPTIMIZE_SPEED | RT_SHADER_FLAG_OPTIMIZE_SIZE,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,381 +0,0 @@
 | 
				
			|||||||
#include "processing.h"
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
#include "description_parser.h"
 | 
					 | 
				
			||||||
#include "packages.h"
 | 
					 | 
				
			||||||
#include "assetmeta.h"
 | 
					 | 
				
			||||||
#include "assetsettings.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/aio.h"
 | 
					 | 
				
			||||||
#include "runtime/buffer_manager.h"
 | 
					 | 
				
			||||||
#include "runtime/file_tab.h"
 | 
					 | 
				
			||||||
#include "runtime/threading.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_file_id fid;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uint32_t flags;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* How many times has this file been added? */
 | 
					 | 
				
			||||||
    unsigned int turn;
 | 
					 | 
				
			||||||
} rt_file_processing_queue_entry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define QUEUE_LENGTH 1024
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    rt_file_processing_queue_entry entries[QUEUE_LENGTH];
 | 
					 | 
				
			||||||
    unsigned int head;
 | 
					 | 
				
			||||||
    unsigned int tail;
 | 
					 | 
				
			||||||
} rt_file_processing_queue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_file_processing_queue _queues[2];
 | 
					 | 
				
			||||||
static rt_file_processing_queue *_processing_queue;
 | 
					 | 
				
			||||||
static rt_file_processing_queue *_retry_queue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_mutex *_guard;
 | 
					 | 
				
			||||||
static bool _keep_running;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* A single file could have a lot of dependencies. */
 | 
					 | 
				
			||||||
#define MAX_TURNS 100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAX_PROCESSING_THREADS 16
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define FORCE_SINGLE_THREAD 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static unsigned int _num_processing_threads = 0;
 | 
					 | 
				
			||||||
static rt_thread *_processing_threads[MAX_PROCESSING_THREADS];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static unsigned int _processing_thread_count = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_result rtAddFileToProcessingQueueImpl(rt_file_processing_queue *queue,
 | 
					 | 
				
			||||||
                                                rt_file_id file,
 | 
					 | 
				
			||||||
                                                uint32_t flags,
 | 
					 | 
				
			||||||
                                                unsigned int turn) {
 | 
					 | 
				
			||||||
    rt_result result = RT_SUCCESS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtLog("ASSETC", "Adding %s to processing queue.", rtGetFilePath(file));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_file_processing_queue_entry entry = {
 | 
					 | 
				
			||||||
        .fid           = file,
 | 
					 | 
				
			||||||
        .flags         = flags,
 | 
					 | 
				
			||||||
        .turn          = turn,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    if ((queue->head + 1) % QUEUE_LENGTH != queue->tail) {
 | 
					 | 
				
			||||||
        unsigned int slot                = queue->head;
 | 
					 | 
				
			||||||
        queue->entries[slot]    = entry;
 | 
					 | 
				
			||||||
        queue->head                      = (queue->head + 1) % QUEUE_LENGTH;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "The processing queue is full!");
 | 
					 | 
				
			||||||
        result = 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtAddFileToProcessingQueue(rt_file_id file, uint32_t flags) {
 | 
					 | 
				
			||||||
    assert(_guard != NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
    rt_result res = rtAddFileToProcessingQueueImpl(_processing_queue, file, flags, 1);
 | 
					 | 
				
			||||||
    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
    return res;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAX_PROCESSORS 256
 | 
					 | 
				
			||||||
static rt_processor_fn *_processor_fns[MAX_PROCESSORS];
 | 
					 | 
				
			||||||
static const char *_processor_exts[MAX_PROCESSORS];
 | 
					 | 
				
			||||||
static unsigned int _processor_count;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtAddAssetProcessor(const char *file_extension, rt_processor_fn fn) {
 | 
					 | 
				
			||||||
    /* Should only be called from main thread */
 | 
					 | 
				
			||||||
    if (_processor_count == MAX_PROCESSORS) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Too many asset processor functions!");
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    _processor_fns[_processor_count]  = fn;
 | 
					 | 
				
			||||||
    _processor_exts[_processor_count] = file_extension;
 | 
					 | 
				
			||||||
    ++_processor_count;
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void PopAndSwapSubmittedData(unsigned int at,
 | 
					 | 
				
			||||||
                                    unsigned int *count,
 | 
					 | 
				
			||||||
                                    rt_file_processing_queue_entry *queue_entries,
 | 
					 | 
				
			||||||
                                    rt_aio_handle *handles,
 | 
					 | 
				
			||||||
                                    void **buffers,
 | 
					 | 
				
			||||||
                                    size_t *sizes) {
 | 
					 | 
				
			||||||
    if (at < *count - 1) {
 | 
					 | 
				
			||||||
        queue_entries[at] = queue_entries[*count - 1];
 | 
					 | 
				
			||||||
        buffers[at]       = buffers[*count - 1];
 | 
					 | 
				
			||||||
        handles[at]       = handles[*count - 1];
 | 
					 | 
				
			||||||
        sizes[at]         = sizes[*count - 1];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    *count = *count - 1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_result ProcessLoadedFile(rt_file_processing_queue_entry entry, void *buffer, size_t size) {
 | 
					 | 
				
			||||||
    /* Search for a matching processor function */
 | 
					 | 
				
			||||||
    const char *path = rtGetFilePath(entry.fid);
 | 
					 | 
				
			||||||
    size_t path_len  = strlen(path);
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < _processor_count; ++i) {
 | 
					 | 
				
			||||||
        size_t ext_len = strlen(_processor_exts[i]);
 | 
					 | 
				
			||||||
        if (ext_len > path_len)
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const char *path_end = &path[path_len - ext_len];
 | 
					 | 
				
			||||||
        if (memcmp(path_end, _processor_exts[i], ext_len) == 0) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Load the corresponding .as file.
 | 
					 | 
				
			||||||
             * TODO: Using malloc here is probably relatively slow.
 | 
					 | 
				
			||||||
             */
 | 
					 | 
				
			||||||
            rt_result res;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            rt_asset_settings settings;
 | 
					 | 
				
			||||||
            if ((res = rtLoadAssetSettings(path, &settings)) != RT_SUCCESS) {
 | 
					 | 
				
			||||||
                return res;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Process the asset */
 | 
					 | 
				
			||||||
            rt_processor_output out;
 | 
					 | 
				
			||||||
            res = _processor_fns[i](entry.fid, buffer, size, entry.flags, &out);
 | 
					 | 
				
			||||||
            if (res == RT_SUCCESS) {
 | 
					 | 
				
			||||||
                /* Add the output to the appropriate package file */
 | 
					 | 
				
			||||||
                rt_assetmeta meta;
 | 
					 | 
				
			||||||
                meta.compiled_ts  = rtGetCurrentTimestamp();
 | 
					 | 
				
			||||||
                meta.last_changed = rtGetFileModificationTimestamp(entry.fid);
 | 
					 | 
				
			||||||
                meta.processing_flags = entry.flags;
 | 
					 | 
				
			||||||
                rtAddUIDMapping(entry.fid, out.asset_uid, &meta);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                rtAddAssetToPackage(settings.package, out.asset_uid, out.data, out.size);
 | 
					 | 
				
			||||||
            } else if (res == RT_PROCESSING_TRY_AGAIN) {
 | 
					 | 
				
			||||||
                if (entry.turn < MAX_TURNS) {
 | 
					 | 
				
			||||||
                    rtLockMutex(_guard);
 | 
					 | 
				
			||||||
                    rtAddFileToProcessingQueueImpl(_retry_queue, entry.fid, entry.flags, entry.turn + 1);
 | 
					 | 
				
			||||||
                    rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    rtLog("ASSETC",
 | 
					 | 
				
			||||||
                          "File '%s' took too many turns to process: %u",
 | 
					 | 
				
			||||||
                          path,
 | 
					 | 
				
			||||||
                          entry.turn);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                rtLog("ASSETC", "Failed to process file: %s  (Result %u)", path, res);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return res;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rtLog("ASSETC", "No asset processor for file: %s", path);
 | 
					 | 
				
			||||||
    return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void ProcessingThread(void *_param) {
 | 
					 | 
				
			||||||
    RT_UNUSED(_param);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    rt_file_processing_queue_entry submitted_entries[RT_LOAD_BATCH_MAX_SIZE];
 | 
					 | 
				
			||||||
    rt_aio_handle submitted_handles[RT_LOAD_BATCH_MAX_SIZE];
 | 
					 | 
				
			||||||
    void *submitted_buffers[RT_LOAD_BATCH_MAX_SIZE];
 | 
					 | 
				
			||||||
    size_t submitted_sizes[RT_LOAD_BATCH_MAX_SIZE];
 | 
					 | 
				
			||||||
    unsigned int submitted_outstanding = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while (_keep_running) {
 | 
					 | 
				
			||||||
        rt_load_batch load_batch;
 | 
					 | 
				
			||||||
        rt_file_processing_queue_entry load_entries[RT_LOAD_BATCH_MAX_SIZE];
 | 
					 | 
				
			||||||
        void *load_buffers[RT_LOAD_BATCH_MAX_SIZE];
 | 
					 | 
				
			||||||
        size_t load_sizes[RT_LOAD_BATCH_MAX_SIZE];
 | 
					 | 
				
			||||||
        load_batch.num_loads = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        bool got_entry = false;
 | 
					 | 
				
			||||||
        do {
 | 
					 | 
				
			||||||
            got_entry = false;
 | 
					 | 
				
			||||||
            rt_file_processing_queue_entry entry = {0};
 | 
					 | 
				
			||||||
            rtLockMutex(_guard);
 | 
					 | 
				
			||||||
            if (_processing_queue->head != _processing_queue->tail) {
 | 
					 | 
				
			||||||
                entry                  = _processing_queue->entries[_processing_queue->tail];
 | 
					 | 
				
			||||||
                _processing_queue->tail = (_processing_queue->tail + 1) % QUEUE_LENGTH;
 | 
					 | 
				
			||||||
                got_entry              = true;
 | 
					 | 
				
			||||||
            } else if (load_batch.num_loads == 0) {
 | 
					 | 
				
			||||||
                /* Switch the queues -> Retry all the entries that returned RT_PROCESSING_TRY_AGAIN */
 | 
					 | 
				
			||||||
                if (_retry_queue->head != _retry_queue->tail) {
 | 
					 | 
				
			||||||
                    rt_file_processing_queue *tmp = _retry_queue;
 | 
					 | 
				
			||||||
                    _retry_queue                  = _processing_queue;
 | 
					 | 
				
			||||||
                    _processing_queue             = tmp;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            /* Retry, if we did not get an entry */
 | 
					 | 
				
			||||||
            if (!got_entry)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const char *path = rtGetFilePath(entry.fid);
 | 
					 | 
				
			||||||
            if (!path) {
 | 
					 | 
				
			||||||
                rtLog("ASSETC", "Invalid file id: %#x", entry.fid);
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            rtLog("ASSETC", "Processing %s", path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            size_t fsz = rtGetFileSize(path);
 | 
					 | 
				
			||||||
            void *dest = rtAllocBuffer(fsz);
 | 
					 | 
				
			||||||
            if (!dest) {
 | 
					 | 
				
			||||||
                rtLog("ASSETC", "Ran out of memory for loading the file: %s", path);
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            memset(dest, 0, fsz);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            load_sizes[load_batch.num_loads]                 = fsz;
 | 
					 | 
				
			||||||
            load_buffers[load_batch.num_loads]               = dest;
 | 
					 | 
				
			||||||
            load_batch.loads[load_batch.num_loads].file      = entry.fid;
 | 
					 | 
				
			||||||
            load_batch.loads[load_batch.num_loads].dest      = dest;
 | 
					 | 
				
			||||||
            load_batch.loads[load_batch.num_loads].num_bytes = fsz;
 | 
					 | 
				
			||||||
            load_batch.loads[load_batch.num_loads].offset    = 0;
 | 
					 | 
				
			||||||
            load_entries[load_batch.num_loads]               = entry;
 | 
					 | 
				
			||||||
            ++load_batch.num_loads;
 | 
					 | 
				
			||||||
        } while (got_entry && load_batch.num_loads < RT_LOAD_BATCH_MAX_SIZE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rt_aio_handle load_handles[RT_LOAD_BATCH_MAX_SIZE];
 | 
					 | 
				
			||||||
        if (load_batch.num_loads > 0) {
 | 
					 | 
				
			||||||
            rt_result submit_result = rtSubmitLoadBatch(&load_batch, load_handles);
 | 
					 | 
				
			||||||
            if (submit_result != RT_SUCCESS) {
 | 
					 | 
				
			||||||
                rtLog("ASSETC", "SubmitLoadBatch failed: %u", submit_result);
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* Process the previously submitted loads */
 | 
					 | 
				
			||||||
        while (submitted_outstanding > 0) {
 | 
					 | 
				
			||||||
            rtLockMutex(_guard);
 | 
					 | 
				
			||||||
            _processing_thread_count += 1;
 | 
					 | 
				
			||||||
            rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (unsigned int i = 0; i < submitted_outstanding; ++i) {
 | 
					 | 
				
			||||||
                rt_aio_state state = rtGetAIOState(submitted_handles[i]);
 | 
					 | 
				
			||||||
                switch (state) {
 | 
					 | 
				
			||||||
                case RT_AIO_STATE_PENDING:
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                case RT_AIO_STATE_FAILED:
 | 
					 | 
				
			||||||
                    rtLog("ASSETC",
 | 
					 | 
				
			||||||
                          "Loading file %s failed.",
 | 
					 | 
				
			||||||
                          rtGetFilePath(submitted_entries[i].fid));
 | 
					 | 
				
			||||||
                    rtReleaseAIO(submitted_handles[i]);
 | 
					 | 
				
			||||||
                    rtReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
 | 
					 | 
				
			||||||
                    PopAndSwapSubmittedData(i,
 | 
					 | 
				
			||||||
                                            &submitted_outstanding,
 | 
					 | 
				
			||||||
                                            submitted_entries,
 | 
					 | 
				
			||||||
                                            submitted_handles,
 | 
					 | 
				
			||||||
                                            submitted_buffers,
 | 
					 | 
				
			||||||
                                            submitted_sizes);
 | 
					 | 
				
			||||||
                    --i;
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                case RT_AIO_STATE_INVALID:
 | 
					 | 
				
			||||||
                    rtLog("ASSETC",
 | 
					 | 
				
			||||||
                          "Got invalid AIO handle for file: %s",
 | 
					 | 
				
			||||||
                          rtGetFilePath(submitted_entries[i].fid));
 | 
					 | 
				
			||||||
                    rtReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
 | 
					 | 
				
			||||||
                    PopAndSwapSubmittedData(i,
 | 
					 | 
				
			||||||
                                            &submitted_outstanding,
 | 
					 | 
				
			||||||
                                            submitted_entries,
 | 
					 | 
				
			||||||
                                            submitted_handles,
 | 
					 | 
				
			||||||
                                            submitted_buffers,
 | 
					 | 
				
			||||||
                                            submitted_sizes);
 | 
					 | 
				
			||||||
                    --i;
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                case RT_AIO_STATE_FINISHED:
 | 
					 | 
				
			||||||
                    ProcessLoadedFile(submitted_entries[i],
 | 
					 | 
				
			||||||
                                      submitted_buffers[i],
 | 
					 | 
				
			||||||
                                      submitted_sizes[i]);
 | 
					 | 
				
			||||||
                    rtReleaseAIO(submitted_handles[i]);
 | 
					 | 
				
			||||||
                    rtReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
 | 
					 | 
				
			||||||
                    PopAndSwapSubmittedData(i,
 | 
					 | 
				
			||||||
                                            &submitted_outstanding,
 | 
					 | 
				
			||||||
                                            submitted_entries,
 | 
					 | 
				
			||||||
                                            submitted_handles,
 | 
					 | 
				
			||||||
                                            submitted_buffers,
 | 
					 | 
				
			||||||
                                            submitted_sizes);
 | 
					 | 
				
			||||||
                    --i;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            rtLockMutex(_guard);
 | 
					 | 
				
			||||||
            _processing_thread_count -= 1;
 | 
					 | 
				
			||||||
            rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* Start new round */
 | 
					 | 
				
			||||||
        assert(sizeof(submitted_entries) == sizeof(load_entries));
 | 
					 | 
				
			||||||
        assert(sizeof(submitted_handles) == sizeof(load_handles));
 | 
					 | 
				
			||||||
        assert(sizeof(submitted_buffers) == sizeof(load_buffers));
 | 
					 | 
				
			||||||
        assert(sizeof(submitted_sizes) == sizeof(load_sizes));
 | 
					 | 
				
			||||||
        memcpy(submitted_entries, load_entries, sizeof(submitted_entries));
 | 
					 | 
				
			||||||
        memcpy(submitted_handles, load_handles, sizeof(submitted_handles));
 | 
					 | 
				
			||||||
        memcpy(submitted_buffers, load_buffers, sizeof(submitted_buffers));
 | 
					 | 
				
			||||||
        memcpy(submitted_sizes, load_sizes, sizeof(submitted_sizes));
 | 
					 | 
				
			||||||
        submitted_outstanding = load_batch.num_loads;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtStartProcessing(void) {
 | 
					 | 
				
			||||||
    if (!_guard)
 | 
					 | 
				
			||||||
        _guard = rtCreateMutex();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if !FORCE_SINGLE_THREAD
 | 
					 | 
				
			||||||
    _num_processing_threads     = rtGetCPUCoreCount();
 | 
					 | 
				
			||||||
    if (_num_processing_threads > MAX_PROCESSING_THREADS)
 | 
					 | 
				
			||||||
        _num_processing_threads = MAX_PROCESSING_THREADS;
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
    _num_processing_threads = 1;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _processing_queue = &_queues[0];
 | 
					 | 
				
			||||||
    _retry_queue      = &_queues[1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _keep_running               = true;
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < _num_processing_threads; ++i) {
 | 
					 | 
				
			||||||
        _processing_threads[i] = rtSpawnThread(ProcessingThread, NULL, "Processor");
 | 
					 | 
				
			||||||
        if (!_processing_threads[i]) {
 | 
					 | 
				
			||||||
            rtReportError("ASSETC", "Failed to spawn processing thread %u!", i);
 | 
					 | 
				
			||||||
            _keep_running = false;
 | 
					 | 
				
			||||||
            return 1;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtStopProcessing(void) {
 | 
					 | 
				
			||||||
    _keep_running = false;
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < _num_processing_threads; ++i)
 | 
					 | 
				
			||||||
        rtJoinThread(_processing_threads[i]);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
#define WIN32_LEAN_AND_MEAN
 | 
					 | 
				
			||||||
#include <Windows.h>
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtWaitUntilProcessingIsFinished(void) {
 | 
					 | 
				
			||||||
    unsigned int done_counter = 0;
 | 
					 | 
				
			||||||
    while (done_counter < 3) {
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
        Sleep(200);
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
        sleep(1);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        rtLockMutex(_guard);
 | 
					 | 
				
			||||||
        volatile bool done = _processing_queue->head == _processing_queue->tail &&
 | 
					 | 
				
			||||||
                             _retry_queue->head == _retry_queue->tail &&
 | 
					 | 
				
			||||||
                             _processing_thread_count == 0;
 | 
					 | 
				
			||||||
        rtUnlockMutex(_guard);
 | 
					 | 
				
			||||||
        if (done)
 | 
					 | 
				
			||||||
            ++done_counter;
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            done_counter = 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,123 +0,0 @@
 | 
				
			|||||||
#include "assetmeta.h"
 | 
					 | 
				
			||||||
#include "processing.h"
 | 
					 | 
				
			||||||
#include "processing_flags.h"
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/buffer_manager.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <shaderc/shaderc.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static shaderc_include_result *ResolveInclude(void *user_data,
 | 
					 | 
				
			||||||
                                              const char *requested_source,
 | 
					 | 
				
			||||||
                                              int type,
 | 
					 | 
				
			||||||
                                              const char *requesting_source,
 | 
					 | 
				
			||||||
                                              size_t include_depth) {
 | 
					 | 
				
			||||||
    shaderc_include_result *result = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void ReleaseIncludeResult(void *user_data, shaderc_include_result *result) {
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtProcessShaderFile(rt_file_id file,
 | 
					 | 
				
			||||||
                              void *buffer,
 | 
					 | 
				
			||||||
                              size_t size,
 | 
					 | 
				
			||||||
                              uint32_t flags,
 | 
					 | 
				
			||||||
                              rt_processor_output *output) {
 | 
					 | 
				
			||||||
    /* If we determine that shader compilation takes too long, we can instead
 | 
					 | 
				
			||||||
     * keep the compiler around */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    shaderc_compiler_t compiler = shaderc_compiler_initialize();
 | 
					 | 
				
			||||||
    if (!compiler) {
 | 
					 | 
				
			||||||
        rtLog("ASSETC", "Failed to initialize the shaderc compiler.");
 | 
					 | 
				
			||||||
        return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const char *path = rtGetFilePath(file);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    shaderc_compile_options_t options = shaderc_compile_options_initialize();
 | 
					 | 
				
			||||||
    if (!options) {
 | 
					 | 
				
			||||||
        rtLog("ASSETC", "Failed to initialize shader compile options.");
 | 
					 | 
				
			||||||
        shaderc_compiler_release(compiler);
 | 
					 | 
				
			||||||
        return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    shaderc_compile_options_set_include_callbacks(options,
 | 
					 | 
				
			||||||
                                                  ResolveInclude,
 | 
					 | 
				
			||||||
                                                  ReleaseIncludeResult,
 | 
					 | 
				
			||||||
                                                  NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uint32_t optimize_from_flags = flags & RT_SHADER_FLAG_OPTIMIZE_MASK;
 | 
					 | 
				
			||||||
    if (optimize_from_flags == RT_SHADER_FLAG_OPTIMIZE_SPEED)
 | 
					 | 
				
			||||||
        shaderc_compile_options_set_optimization_level(options,
 | 
					 | 
				
			||||||
                                                       shaderc_optimization_level_performance);
 | 
					 | 
				
			||||||
    else if (optimize_from_flags == RT_SHADER_FLAG_OPTIMIZE_SIZE)
 | 
					 | 
				
			||||||
        shaderc_compile_options_set_optimization_level(options, shaderc_optimization_level_size);
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        shaderc_compile_options_set_optimization_level(options, shaderc_optimization_level_zero);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uint32_t type_from_flags = flags & RT_SHADER_FLAG_TYPE_MASK;
 | 
					 | 
				
			||||||
    shaderc_shader_kind shaderc_kind;
 | 
					 | 
				
			||||||
    switch (type_from_flags) {
 | 
					 | 
				
			||||||
    case RT_SHADER_FLAG_VERTEX:
 | 
					 | 
				
			||||||
        shaderc_kind = shaderc_glsl_vertex_shader;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case RT_SHADER_FLAG_FRAGMENT:
 | 
					 | 
				
			||||||
        shaderc_kind = shaderc_glsl_fragment_shader;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case RT_SHADER_FLAG_COMPUTE:
 | 
					 | 
				
			||||||
        shaderc_kind = shaderc_glsl_compute_shader;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
        rtLog("ASSETC", "Invalid shader stage flag %u", type_from_flags);
 | 
					 | 
				
			||||||
        shaderc_compile_options_release(options);
 | 
					 | 
				
			||||||
        shaderc_compiler_release(compiler);
 | 
					 | 
				
			||||||
        return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    shaderc_compilation_result_t result = shaderc_compile_into_spv(compiler,
 | 
					 | 
				
			||||||
                                                                   (const char *)buffer,
 | 
					 | 
				
			||||||
                                                                   size,
 | 
					 | 
				
			||||||
                                                                   shaderc_kind,
 | 
					 | 
				
			||||||
                                                                   path,
 | 
					 | 
				
			||||||
                                                                   "main",
 | 
					 | 
				
			||||||
                                                                   options);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    shaderc_compilation_status status = shaderc_result_get_compilation_status(result);
 | 
					 | 
				
			||||||
    if (status != shaderc_compilation_status_success || shaderc_result_get_num_errors(result) > 0) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rtLog("ASSETC",
 | 
					 | 
				
			||||||
              "Compilation of %s failed: %s",
 | 
					 | 
				
			||||||
              path,
 | 
					 | 
				
			||||||
              shaderc_result_get_error_message(result));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        shaderc_result_release(result);
 | 
					 | 
				
			||||||
        shaderc_compile_options_release(options);
 | 
					 | 
				
			||||||
        shaderc_compiler_release(compiler);
 | 
					 | 
				
			||||||
        return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t output_size = shaderc_result_get_length(result);
 | 
					 | 
				
			||||||
    output->data       = rtAllocBuffer(output_size);
 | 
					 | 
				
			||||||
    if (!output->data) {
 | 
					 | 
				
			||||||
        shaderc_result_release(result);
 | 
					 | 
				
			||||||
        shaderc_compile_options_release(options);
 | 
					 | 
				
			||||||
        shaderc_compiler_release(compiler);
 | 
					 | 
				
			||||||
        rtLog("ASSETC", "Failed to allocate %zu bytes for shader compilation output.", output_size);
 | 
					 | 
				
			||||||
        return RT_PROCESSING_FAILED;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const uint32_t *spv_bytes = (uint32_t *)shaderc_result_get_bytes(result);
 | 
					 | 
				
			||||||
    memcpy(output->data, spv_bytes, output_size);
 | 
					 | 
				
			||||||
    output->size = output_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    shaderc_result_release(result);
 | 
					 | 
				
			||||||
    shaderc_compile_options_release(options);
 | 
					 | 
				
			||||||
    shaderc_compiler_release(compiler);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    output->asset_uid = rtCalculateUID(path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,69 +0,0 @@
 | 
				
			|||||||
#include "processing.h"
 | 
					 | 
				
			||||||
#include "utils.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define RT_DEFINE_UIDTAB_FILE_STRUCTURES
 | 
					 | 
				
			||||||
#include "runtime/threading.h"
 | 
					 | 
				
			||||||
#include "runtime/uidtab.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <xxhash/xxhash.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static rt_uidtab_entry *_entries;
 | 
					 | 
				
			||||||
static size_t _entry_capacity;
 | 
					 | 
				
			||||||
static size_t _entry_count;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_uid rtCalculateUID(const char *name) {
 | 
					 | 
				
			||||||
    assert(sizeof(XXH32_hash_t) == sizeof(rt_uid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t len = strlen(name);
 | 
					 | 
				
			||||||
    return (rt_uid)XXH32(name, len, 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtAddUIDTabEntry(rt_file_id package_fid, rt_uid uid, uint64_t offset, uint64_t size) {
 | 
					 | 
				
			||||||
    assert(rtIsMainThread());
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if (_entry_count == _entry_capacity) {
 | 
					 | 
				
			||||||
        size_t new_cap = (_entry_capacity > 0) ? _entry_capacity * 2 : 256;
 | 
					 | 
				
			||||||
        void *t        = realloc(_entries, sizeof(rt_uidtab_entry) * new_cap);
 | 
					 | 
				
			||||||
        if (!t)
 | 
					 | 
				
			||||||
            return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
        _entry_capacity = new_cap;
 | 
					 | 
				
			||||||
        _entries        = t;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    _entries[_entry_count].file   = package_fid;
 | 
					 | 
				
			||||||
    _entries[_entry_count].offset = offset;
 | 
					 | 
				
			||||||
    _entries[_entry_count].size   = size;
 | 
					 | 
				
			||||||
    _entries[_entry_count].uid    = uid;
 | 
					 | 
				
			||||||
    _entry_count++;
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtWriteUIDTab(void) {
 | 
					 | 
				
			||||||
    rt_uidtab_header header;
 | 
					 | 
				
			||||||
    XXH64_hash_t checksum = XXH3_64bits(_entries, sizeof(rt_uidtab_entry) * _entry_count);
 | 
					 | 
				
			||||||
    XXH64_canonicalFromHash(&header.checksum, checksum);
 | 
					 | 
				
			||||||
    header.num_entries = (uint32_t)_entry_count;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    FILE *f = fopen("data/uidtab.bin", "wb");
 | 
					 | 
				
			||||||
    if (!f) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to open 'uidtab.bin' for writing.");
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (fwrite(&header, sizeof(header), 1, f) != 1) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to write header to 'uidtab.bin'");
 | 
					 | 
				
			||||||
        fclose(f);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (fwrite(_entries, sizeof(rt_uidtab_entry), _entry_count, f) != _entry_count) {
 | 
					 | 
				
			||||||
        rtReportError("ASSETC", "Failed to write entries to 'uidtab.bin'");
 | 
					 | 
				
			||||||
        fclose(f);
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fclose(f);
 | 
					 | 
				
			||||||
    return RT_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,101 +0,0 @@
 | 
				
			|||||||
#include "utils.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
#define WIN32_LEAN_AND_MEAN
 | 
					 | 
				
			||||||
#include <Windows.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
size_t rtGetFileSize(const char *path) {
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
    WCHAR wpath[MAX_PATH];
 | 
					 | 
				
			||||||
    MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH);
 | 
					 | 
				
			||||||
    WIN32_FILE_ATTRIBUTE_DATA attribs;
 | 
					 | 
				
			||||||
    if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &attribs))
 | 
					 | 
				
			||||||
        return 0;
 | 
					 | 
				
			||||||
    return (size_t)attribs.nFileSizeHigh << 32 | (size_t)attribs.nFileSizeLow;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtSetWorkingDirectory(const char *path) {
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
    WCHAR wpath[MAX_PATH];
 | 
					 | 
				
			||||||
    MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH);
 | 
					 | 
				
			||||||
    SetCurrentDirectoryW(wpath);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint64_t rtGetCurrentTimestamp(void) {
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
    FILETIME ft;
 | 
					 | 
				
			||||||
    GetSystemTimeAsFileTime(&ft);
 | 
					 | 
				
			||||||
    uint64_t ts = ft.dwLowDateTime;
 | 
					 | 
				
			||||||
    ts |= (uint64_t)ft.dwHighDateTime << 32;
 | 
					 | 
				
			||||||
    return ts;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint64_t rtGetFileModificationTimestamp(rt_file_id fid) {
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
    const char *path = rtGetFilePath(fid);
 | 
					 | 
				
			||||||
    if (!path) {
 | 
					 | 
				
			||||||
        rtLog("ASSETC", "Tried to get modification timestamp of unknown file.");
 | 
					 | 
				
			||||||
        return 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    WCHAR wpath[MAX_PATH];
 | 
					 | 
				
			||||||
    MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH);
 | 
					 | 
				
			||||||
    WIN32_FILE_ATTRIBUTE_DATA attribs;
 | 
					 | 
				
			||||||
    if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &attribs))
 | 
					 | 
				
			||||||
        return 0;
 | 
					 | 
				
			||||||
    uint64_t ts = attribs.ftLastWriteTime.dwLowDateTime;
 | 
					 | 
				
			||||||
    ts |= (uint64_t)attribs.ftLastWriteTime.dwHighDateTime << 32;
 | 
					 | 
				
			||||||
    return ts;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtIterateDirectory(const char *path, void *user, rt_iterate_directory_cb_func iterate_cb) {
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
    rt_result res;
 | 
					 | 
				
			||||||
    WCHAR wpath[MAX_PATH];
 | 
					 | 
				
			||||||
    char wildcard_path[MAX_PATH];
 | 
					 | 
				
			||||||
    strncpy(wildcard_path, path, MAX_PATH);
 | 
					 | 
				
			||||||
    strncat(wildcard_path, "\\*", MAX_PATH - strlen(path));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    res = rtUTF8ToWStr(wildcard_path, wpath, MAX_PATH);
 | 
					 | 
				
			||||||
    if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
        return res;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    WIN32_FIND_DATAW find;
 | 
					 | 
				
			||||||
    HANDLE h = FindFirstFileW(wpath, &find);
 | 
					 | 
				
			||||||
    if (h == INVALID_HANDLE_VALUE)
 | 
					 | 
				
			||||||
        return RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    do {
 | 
					 | 
				
			||||||
        char utf8_file[MAX_PATH];
 | 
					 | 
				
			||||||
        res = rtWStrToUTF8(find.cFileName, utf8_file, MAX_PATH);
 | 
					 | 
				
			||||||
        if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        rtIterateDirElementType type;
 | 
					 | 
				
			||||||
        if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 | 
					 | 
				
			||||||
            type = RT_DIR_ELEMENT_TYPE_DIRECTORY;
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            type = RT_DIR_ELEMENT_TYPE_FILE;
 | 
					 | 
				
			||||||
        res = iterate_cb(utf8_file, type, user);
 | 
					 | 
				
			||||||
        if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
    } while (FindNextFileW(h, &find) != 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FindClose(h);
 | 
					 | 
				
			||||||
    return res;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtCreateDirectory(const char *path) {
 | 
					 | 
				
			||||||
#ifdef _WIN32
 | 
					 | 
				
			||||||
    WCHAR wpath[MAX_PATH];
 | 
					 | 
				
			||||||
    rt_result res = rtUTF8ToWStr(path, wpath, MAX_PATH);
 | 
					 | 
				
			||||||
    if (res != RT_SUCCESS)
 | 
					 | 
				
			||||||
        return res;
 | 
					 | 
				
			||||||
    if (!CreateDirectoryW(wpath, NULL))
 | 
					 | 
				
			||||||
        res = RT_UNKNOWN_ERROR;
 | 
					 | 
				
			||||||
    return res;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,27 +0,0 @@
 | 
				
			|||||||
#ifndef RT_ASSETC_UTILS_H
 | 
					 | 
				
			||||||
#define RT_ASSETC_UTILS_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "runtime/file_tab.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
size_t rtGetFileSize(const char *path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rtSetWorkingDirectory(const char *path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint64_t rtGetCurrentTimestamp(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint64_t rtGetFileModificationTimestamp(rt_file_id fid);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    RT_DIR_ELEMENT_TYPE_FILE,
 | 
					 | 
				
			||||||
    RT_DIR_ELEMENT_TYPE_DIRECTORY,
 | 
					 | 
				
			||||||
} rtIterateDirElementType;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef rt_result rt_iterate_directory_cb_func(const char *name, rtIterateDirElementType type, void *user);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtIterateDirectory(const char *path, void *user, rt_iterate_directory_cb_func iterate_cb);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rt_result rtCreateDirectory(const char *path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user