diff options
| -rw-r--r-- | src/asset-manager.cpp | 155 | ||||
| -rw-r--r-- | src/asset-manager.hpp | 17 | ||||
| -rw-r--r-- | src/entities.cpp | 2 | ||||
| -rw-r--r-- | src/memory-type-defs.hpp | 18 | ||||
| -rw-r--r-- | src/window.cpp | 57 |
5 files changed, 181 insertions, 68 deletions
diff --git a/src/asset-manager.cpp b/src/asset-manager.cpp index 753da24..9fda1c1 100644 --- a/src/asset-manager.cpp +++ b/src/asset-manager.cpp @@ -1,7 +1,14 @@ #include "asset-manager.hpp" -const PkeHandleItemIndex_T maxAssetItemsPerBucket = 256; +#include "thread_pool.hpp" + +#include <chrono> +#include <filesystem> +#include <fstream> +#include <future> + +const PkeHandleItemIndex_T maxAssetItemsPerBucket = 64; struct AssetBucket { Asset assets[maxAssetItemsPerBucket]; @@ -9,65 +16,108 @@ struct AssetBucket { BucketContainer<AssetBucket, AssetHandle> Asset_BucketContainer{}; +ThreadPoolHandle assetThreadPool = ThreadPoolHandle_MAX; + void AM_Init() { Buckets_Init(Asset_BucketContainer); + assetThreadPool = PkeThreads_Init(2, 255); } -AssetHandle AM_Register_Inner(const void *src, void *dst, int64_t size, const char *key) { - bool moved = false; - AssetHandle assetHandle{Buckets_NewHandle<AssetBucket>(maxAssetItemsPerBucket, Asset_BucketContainer, moved)}; - - if (src != nullptr) { - std::memcpy(dst, src, size); +void AM_Load_Task(Asset &asset) { + std::ifstream file(asset.basePath, std::ios::ate | std::ios::binary); + if (!file.is_open()) { + asset.state = PKE_ASSET_LOADING_STATE_FAILED; + return; } - Asset *asset = &Asset_BucketContainer.buckets[assetHandle.bucketIndex].assets[assetHandle.itemIndex]; - asset->ptr = dst; - asset->size = size; - int64_t keyLen = std::strlen(key); - std::memcpy(asset->key, key, keyLen > 16 ? 16 : keyLen); - return assetHandle; + asset.size = std::filesystem::file_size(asset.basePath); + asset.ptr = Pke_New(asset.size, 64); + file.seekg(0, std::ios::beg); + file.read(static_cast<char *>(asset.ptr), asset.size); + file.close(); + asset.state = PKE_ASSET_LOADING_STATE_LOADED; } AssetHandle AM_Register(const void *data, int64_t size, std::size_t alignment, const char *key) { assert(data != nullptr && "Attempt to register invalid asset data"); assert(data != CAFE_BABE(void) && "Attempt to register invalid asset data"); assert(size != 0 && "Attempt to register asset data of size 0"); - void *target = Pke_New(size, alignment); - return AM_Register_Inner(data, target, size, key); + bool moved; + AssetHandle assetHandle{Buckets_NewHandle<AssetBucket>(maxAssetItemsPerBucket, Asset_BucketContainer, moved)}; + Asset &asset = Asset_BucketContainer.buckets[assetHandle.bucketIndex].assets[assetHandle.itemIndex]; + new (&asset) Asset{}; + int64_t keyLen = strlen(key); + int64_t keyOffset = (keyLen > 16 ? keyLen - 16 : 0); + strncpy(asset.key, key + keyOffset, 16); + asset.basePath = nullptr; + asset.size = size; + asset.ptr = Pke_New(size, alignment); + memcpy(asset.ptr, data, size); + asset.state = PKE_ASSET_LOADING_STATE_LOADED; + return assetHandle; } AssetHandle AM_Register(const char *path) { - std::ifstream file(path, std::ios::ate | std::ios::binary); - if (!file.is_open()) { - throw "failed to open file"; - } - auto fileSize = (int64_t)file.tellg(); - void *target = Pke_New(fileSize, alignof(uint64_t)); - + bool moved = false; + AssetHandle assetHandle{Buckets_NewHandle<AssetBucket>(maxAssetItemsPerBucket, Asset_BucketContainer, moved)}; + Asset &asset = Asset_BucketContainer.buckets[assetHandle.bucketIndex].assets[assetHandle.itemIndex]; + new (&asset) Asset{}; int64_t pathLen = strlen(path); - auto assetHandle = AM_Register_Inner(nullptr, target, 0, path + (pathLen > 16 ? pathLen - 16 : 0)); - Asset *asset = &Asset_BucketContainer.buckets[assetHandle.bucketIndex].assets[assetHandle.itemIndex]; - file.seekg(0); - file.read(static_cast<char *>(asset->ptr), fileSize); - file.close(); - asset->size = fileSize; + int64_t pathOffset = (pathLen > 16 ? pathLen - 16 : 0); + strncpy(asset.key, path + pathOffset, 16); + auto *copiedPath = Pke_New<char>(pathLen + 1); + copiedPath[pathLen] = '\0'; + strncpy(copiedPath, path, pathLen); + asset.basePath = copiedPath; + asset.state = PKE_ASSET_LOADING_STATE_LOADING; + std::packaged_task<void()> *task = Pke_New<std::packaged_task<void()>>(); + new (task) std::packaged_task<void()>( [&asset] { + AM_Load_Task(asset); + }); + asset.future = task->get_future(); + PkeThreads_Enqueue(assetThreadPool, task); return assetHandle; } -void AM_Destroy(AssetHandle assetHandle) { +void AM_Release(AssetHandle assetHandle) { assert(assetHandle != AssetHandle_MAX); - Asset *asset = &Asset_BucketContainer.buckets[assetHandle.bucketIndex].assets[assetHandle.itemIndex]; - Pke_Delete(asset->ptr, asset->size); - asset->size = 0; - asset->ptr = CAFE_BABE(void); - if (assetHandle.itemIndex == Asset_BucketContainer.pkeHandle.itemIndex) { - Asset_BucketContainer.pkeHandle.itemIndex -= 1; - } + Asset &asset = Asset_BucketContainer.buckets[assetHandle.bucketIndex].assets[assetHandle.itemIndex]; + asset.referenceCount -= 1; + assert(asset.referenceCount >= 0 && "asset was unloaded more times than it was retrieved"); } const Asset *AM_Get(AssetHandle assetHandle) { - assert(assetHandle != AssetHandle_MAX); - return &Asset_BucketContainer.buckets[assetHandle.bucketIndex].assets[assetHandle.itemIndex]; + auto validationResult = ValidateHandle(assetHandle, Asset_BucketContainer.pkeHandle, maxAssetItemsPerBucket); + assert(validationResult == 0); + auto &asset = Asset_BucketContainer.buckets[assetHandle.bucketIndex].assets[assetHandle.itemIndex]; + if (asset.state == PKE_ASSET_LOADING_STATE_LOADED) { + asset.referenceCount += 1; + return &asset; + } + if (asset.state == PKE_ASSET_LOADING_STATE_UNLOADED) { + if (asset.basePath == nullptr) { + return nullptr; + } + asset.state = PKE_ASSET_LOADING_STATE_LOADING; + std::packaged_task<void()> *task = Pke_New<std::packaged_task<void()>>(); + new (task) std::packaged_task<void()>( [&asset] { + AM_Load_Task(asset); + }); + asset.future = task->get_future(); + PkeThreads_Enqueue(assetThreadPool, task); + } + if (asset.state == PKE_ASSET_LOADING_STATE_LOADING) { + if (asset.future.valid()) { + asset.future.wait(); + } else { + char buf[256]; + buf[255] = '\0'; + snprintf(buf, 255, "[AM_Get] Attempting to retrieve asset: '%.16s' which had a state of '%hhu', but did not have a valid future and thus cannot wait for completion", asset.key, asset.state); + throw buf; + } + } + if (asset.state == PKE_ASSET_LOADING_STATE_LOADED) + asset.referenceCount += 1; + return &asset; } void AM_DebugPrint() { @@ -82,13 +132,40 @@ void AM_DebugPrint() { continue; */ printf("-Asset: 0x%016lX\n", (b << 32) + i); - printf("\tkey: %s\n", asset.key); + printf("\tkey: %.16s\n", asset.key); + if (asset.basePath != nullptr) { + printf("\tbasePath: %s\n", asset.basePath); + } else { + printf("\tbasePath: %p\n", nullptr); + } printf("\tsize: %ld\n", asset.size); printf("\tptr %p\n", asset.ptr); + printf("\tfuture: %i\n", asset.future.valid()); + printf("\treferenceCount: %i\n", asset.referenceCount); + printf("\tAssetLoadingState: %hhu\n", asset.state); + } + } +} + +void AM_GC() { + for (long b = 0; b <= Asset_BucketContainer.pkeHandle.bucketIndex; ++b) { + for (long i = 0; i < Asset_BucketContainer.pkeHandle.itemIndex; ++i) { + Asset &asset = Asset_BucketContainer.buckets[b].assets[i]; + if (asset.referenceCount > 0) + continue; + if (asset.ptr != nullptr && asset.ptr != CAFE_BABE(void)) { + Pke_Delete(asset.ptr, asset.size); + } + asset.size = 0; + asset.ptr = CAFE_BABE(void); + asset.future = std::future<void>{}; + asset.state = PKE_ASSET_LOADING_STATE_UNLOADED; } } } void AM_Teardown() { + AM_GC(); Buckets_Destroy(Asset_BucketContainer); + PkeThreads_Teardown(assetThreadPool); } diff --git a/src/asset-manager.hpp b/src/asset-manager.hpp index 001ec4f..36516b5 100644 --- a/src/asset-manager.hpp +++ b/src/asset-manager.hpp @@ -4,25 +4,34 @@ #include "macros.hpp" #include "memory.hpp" -#include <fstream> #include <cstdint> -#include <cstring> -#include <cassert> +#include <future> struct AssetHandle : public PkeHandle { }; constexpr AssetHandle AssetHandle_MAX = AssetHandle{}; +TypeSafeInt_Const_Expr(AssetLoadingState, uint8_t, 0xFF); + +const AssetLoadingState PKE_ASSET_LOADING_STATE_UNLOADED = AssetLoadingState {0}; +const AssetLoadingState PKE_ASSET_LOADING_STATE_LOADING = AssetLoadingState {1}; +const AssetLoadingState PKE_ASSET_LOADING_STATE_LOADED = AssetLoadingState {2}; +const AssetLoadingState PKE_ASSET_LOADING_STATE_FAILED = AssetLoadingState {3}; + struct Asset{ char key[16] = {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'}; + const char *basePath = nullptr; int64_t size = 0; void *ptr = nullptr; + std::future<void> future; + int8_t referenceCount = 0; + AssetLoadingState state = PKE_ASSET_LOADING_STATE_UNLOADED; }; void AM_Init(); AssetHandle AM_Register(const void *data, int64_t size, std::size_t alignment, const char *key); AssetHandle AM_Register(const char *path); -void AM_Destroy(AssetHandle assetHandle); +void AM_Release(AssetHandle assetHandle); const Asset *AM_Get(AssetHandle assetHandle); void AM_Teardown(); void AM_DebugPrint(); diff --git a/src/entities.cpp b/src/entities.cpp index c49d948..24cdd29 100644 --- a/src/entities.cpp +++ b/src/entities.cpp @@ -847,7 +847,7 @@ void EntityType_Load(EntityType &et) { // cleanup cgltf_free(gltfData); - AM_Destroy(assetHandle); + AM_Release(assetHandle); } } diff --git a/src/memory-type-defs.hpp b/src/memory-type-defs.hpp index d9d6f5e..181b89b 100644 --- a/src/memory-type-defs.hpp +++ b/src/memory-type-defs.hpp @@ -6,6 +6,11 @@ using PkeHandleBucketIndex_T = uint32_t; using PkeHandleItemIndex_T = uint32_t; +const uint8_t PKE_HANDLE_VALIDATION_VALID = 0; +const uint8_t PKE_HANDLE_VALIDATION_BUCKET_INDEX_TOO_HIGH = 1; +const uint8_t PKE_HANDLE_VALIDATION_ITEM_INDEX_TOO_HIGH = 2; +const uint8_t PKE_HANDLE_VALIDATION_VALUE_MAX = 3; + struct PkeHandle { union { struct { @@ -15,11 +20,24 @@ struct PkeHandle { uint64_t hash = 0xFFFFFFFFFFFFFFFF; }; }; +constexpr PkeHandle PkeHandle_MAX = PkeHandle{}; constexpr bool operator==(const PkeHandle &a, const PkeHandle &b) { return a.hash == b.hash; } +constexpr uint8_t ValidateHandle(PkeHandle handle, PkeHandle bucketHandle, uint64_t maxItems) { + if (handle == PkeHandle_MAX) + return PKE_HANDLE_VALIDATION_VALUE_MAX; + if (handle.bucketIndex > bucketHandle.bucketIndex) + return PKE_HANDLE_VALIDATION_BUCKET_INDEX_TOO_HIGH; + if (handle.itemIndex > maxItems) + return PKE_HANDLE_VALIDATION_ITEM_INDEX_TOO_HIGH; + if (handle.bucketIndex == bucketHandle.bucketIndex && handle.itemIndex > bucketHandle.itemIndex) + return PKE_HANDLE_VALIDATION_ITEM_INDEX_TOO_HIGH; + return PKE_HANDLE_VALIDATION_VALID; +} + struct MemBucket; #endif /* PKE_MEMORY_TYPE_DEFS_HPP */ diff --git a/src/window.cpp b/src/window.cpp index e7d4506..0c881de 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1047,10 +1047,9 @@ void CreateRenderPass() { } void CreatePresentPipeline() { + // enqueue asset loading AssetHandle vertShaderAsset = AM_Register("assets/shaders/present.vert.spv"); AssetHandle fragShaderAsset = AM_Register("assets/shaders/present.frag.spv"); - auto vertShader = UploadShader(vertShaderAsset); - auto fragShader = UploadShader(fragShaderAsset); VkPipelineShaderStageCreateInfo shaderStages[2]; for (long i = 0; i < 2; ++i) { @@ -1062,8 +1061,8 @@ void CreatePresentPipeline() { } shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shaderStages[0].module = vertShader; - shaderStages[1].module = fragShader; + // shaderStages[0].module = // see below + // shaderStages[1].module = // see below VkDynamicState dynamicStates[2] = { VK_DYNAMIC_STATE_VIEWPORT, @@ -1269,6 +1268,15 @@ void CreatePresentPipeline() { pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = {}; + /* 2023-12-22 JCB + * This happens just before needed because we want to let this + * thread run while background threads load the assets. + */ + auto vertShader = UploadShader(vertShaderAsset); + auto fragShader = UploadShader(fragShaderAsset); + shaderStages[0].module = vertShader; + shaderStages[1].module = fragShader; + vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &pipelineInfo, vkAllocator, &graphicsPipeline); if (vkResult != VK_SUCCESS) { throw "failed to create graphics pipeline."; @@ -1276,8 +1284,8 @@ void CreatePresentPipeline() { vkDestroyShaderModule(vkDevice, fragShader, vkAllocator); vkDestroyShaderModule(vkDevice, vertShader, vkAllocator); - AM_Destroy(fragShaderAsset); - AM_Destroy(vertShaderAsset); + AM_Release(fragShaderAsset); + AM_Release(vertShaderAsset); } void CreateFramebuffers() { @@ -1492,6 +1500,10 @@ void CreateGraphicsPipelines() { // pipelines { + // enqueue asset loading + AssetHandle vertShaderAssetHandle = AM_Register("assets/shaders/vert.vert.spv"); + AssetHandle textureFragShaderAssetHandle = AM_Register("assets/shaders/texture.frag.spv"); + const long vertexBindingCount = 4; long index = 0; VkVertexInputBindingDescription vertInputBD[vertexBindingCount]; @@ -1668,35 +1680,24 @@ void CreateGraphicsPipelines() { vkPipelineDepthStencilStateCreateInfo.minDepthBounds = {}; vkPipelineDepthStencilStateCreateInfo.maxDepthBounds = {}; - AssetHandle vertShaderAssetHandle = AM_Register("assets/shaders/vert.vert.spv"); - AssetHandle textureFragShaderAssetHandle = AM_Register("assets/shaders/texture.frag.spv"); const Asset *textureVertShaderAsset = AM_Get(vertShaderAssetHandle); const Asset *textureFragShaderAsset = AM_Get(textureFragShaderAssetHandle); - VkShaderModuleCreateInfo vkShaderModuleCreateInfo[2]; - vkShaderModuleCreateInfo[0].codeSize = textureVertShaderAsset->size; - vkShaderModuleCreateInfo[0].pCode = reinterpret_cast<const uint32_t *>(textureVertShaderAsset->ptr); - vkShaderModuleCreateInfo[1].codeSize = textureFragShaderAsset->size; - vkShaderModuleCreateInfo[1].pCode = reinterpret_cast<const uint32_t *>(textureFragShaderAsset->ptr); - VkShaderModule shaderModules[2]; + assert(textureVertShaderAsset != nullptr && textureVertShaderAsset->state == PKE_ASSET_LOADING_STATE_LOADED); + assert(textureFragShaderAsset != nullptr && textureFragShaderAsset->state == PKE_ASSET_LOADING_STATE_LOADED); VkPipelineShaderStageCreateInfo vkPipelineShaderStageCreateInfo[2]; for (long i = 0; i < 2; ++i) { - vkShaderModuleCreateInfo[i].sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - vkShaderModuleCreateInfo[i].pNext = nullptr; - vkShaderModuleCreateInfo[i].flags = {}; vkPipelineShaderStageCreateInfo[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vkPipelineShaderStageCreateInfo[i].pNext = nullptr; vkPipelineShaderStageCreateInfo[i].flags = {}; vkPipelineShaderStageCreateInfo[i].pName = "main"; vkPipelineShaderStageCreateInfo[i].pSpecializationInfo = nullptr; - - vkCreateShaderModule(vkDevice, &vkShaderModuleCreateInfo[i], vkAllocator, &shaderModules[i]); } - vkPipelineShaderStageCreateInfo[0].module = shaderModules[0]; - vkPipelineShaderStageCreateInfo[1].module = shaderModules[1]; vkPipelineShaderStageCreateInfo[0].stage = VK_SHADER_STAGE_VERTEX_BIT; vkPipelineShaderStageCreateInfo[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + // vkPipelineShaderStageCreateInfo[0].module = // deferred + // vkPipelineShaderStageCreateInfo[1].module = // deferred VkGraphicsPipelineCreateInfo vkGraphicsPipelineCreateInfo[2]; for (long i = 0; i < 2; ++i) { @@ -1723,14 +1724,18 @@ void CreateGraphicsPipelines() { vkGraphicsPipelineCreateInfo[0].pRasterizationState = &vkPipelineRasterizationStateCreateInfoFill; vkGraphicsPipelineCreateInfo[1].pRasterizationState = &vkPipelineRasterizationStateCreateInfoLine; + // deffered shader creation + vkPipelineShaderStageCreateInfo[0].module = UploadShader(vertShaderAssetHandle); + vkPipelineShaderStageCreateInfo[1].module = UploadShader(textureFragShaderAssetHandle); + vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 2, vkGraphicsPipelineCreateInfo, vkAllocator, pkePipelines.pipelines.arr); for (long i = 0; i < 2; ++i) { - vkDestroyShaderModule(vkDevice, shaderModules[i], vkAllocator); + vkDestroyShaderModule(vkDevice, vkPipelineShaderStageCreateInfo[i].module, vkAllocator); } - AM_Destroy(textureFragShaderAssetHandle); - AM_Destroy(vertShaderAssetHandle); + AM_Release(textureFragShaderAssetHandle); + AM_Release(vertShaderAssetHandle); } // debug texture @@ -2651,6 +2656,10 @@ void DestroyWindow() { VkShaderModule UploadShader(AssetHandle handle) { const Asset *asset = AM_Get(handle); + assert(asset != nullptr && asset->state == PKE_ASSET_LOADING_STATE_LOADED); +#ifndef NDEBUG + fprintf(stdout, "Uploading Shader: '%s'\n", asset->basePath); +#endif VkShaderModuleCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
