summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/asset-manager.cpp155
-rw-r--r--src/asset-manager.hpp17
-rw-r--r--src/entities.cpp2
-rw-r--r--src/memory-type-defs.hpp18
-rw-r--r--src/window.cpp57
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;