Shaders assets and loading fix

- a = { ... } definitions replaced with a { ... }
- GetOverlappedResult replaced with waiting for event in Windows async file loading
- FileHandles and AssetHandles stored in heap for container element adresses consistence
- Shader assets examples
This commit is contained in:
legatt 2025-08-24 14:56:17 +03:00
parent 9a258f7443
commit 7ad2ec4dc8
13 changed files with 182 additions and 77 deletions

View File

@ -11,12 +11,12 @@ struct WorldBenchmark : testing::Test
{
for (int16_t i = 0; i < 1000; i++)
{
ee_economy::Container resources = {.capacity = 100.0};
ee_economy::Container resources {.capacity = 100.0};
resources.add(ee_economy::RESOURCE_TYPE::COAL, 100.0);
entt::entity state = world.registerState(i, resources);
ee_economy::EnterpriseDefaults miner = {
.container = {.capacity = 100.0},
ee_economy::EnterpriseDefaults miner {
.container {.capacity = 100.0},
.resourceTarget = ee_economy::RESOURCE_TYPE::GOLD,
.baseEfficiency = 1.0
};

View File

@ -7,7 +7,7 @@ TEST(WorldTest, RegisterState)
{
ee_ecs::World world;
ee_economy::Container res = {.capacity = 100.0};
ee_economy::Container res {.capacity = 100.0};
res.add(ee_economy::RESOURCE_TYPE::COAL, 98.0);
entt::entity state = world.registerState(10, res);
@ -37,8 +37,8 @@ TEST(WorldTest, SpawnMiner)
world.getRegistry().emplace<ee_economy::State>(state, (int16_t)0);
world.getRegistry().emplace<ee_economy::Container>(state, 100.0);
ee_economy::EnterpriseDefaults defaults = {
.container = {.capacity = 100.0 },
ee_economy::EnterpriseDefaults defaults {
.container {.capacity = 100.0 },
.resourceTarget = ee_economy::RESOURCE_TYPE::COAL,
.baseEfficiency = 1.0,
};
@ -62,8 +62,8 @@ TEST(WorldTest, SpawnDeliverer)
world.getRegistry().emplace<ee_economy::State>(state, (int16_t)0);
world.getRegistry().emplace<ee_economy::Container>(state, 100.0);
ee_economy::EnterpriseDefaults defaults = {
.container = {.capacity = 100.0 },
ee_economy::EnterpriseDefaults defaults {
.container {.capacity = 100.0 },
.resourceTarget = ee_economy::RESOURCE_TYPE::COAL,
.baseEfficiency = 1.0,
};

Binary file not shown.

Binary file not shown.

View File

@ -13,3 +13,4 @@ struct AssetBinary : AssetData
AssetBinary(uint8_t* &source, size_t sourceSize);
~AssetBinary();
};
typedef AssetBinary AssetShader;

View File

@ -27,6 +27,7 @@ public:
BINARY,
SHADER,
MATERIAL,
IMAGE
} type = BINARY;
@ -82,6 +83,8 @@ public:
void Request(const AssetStreamCommand &command);
void Request(const AssetStreamCommand::Type &type);
void LoadShaders();
void LoadAsync(AssetInfo::Type type, const std::filesystem::path &path);
AssetHandle& Find(const std::filesystem::path &path);
void Redirect(AssetHandle &handle, void *resource);
@ -96,7 +99,7 @@ private:
IFileSystem *diskSystem;
std::vector<AssetInfo> activeAssets;
std::unordered_map<std::filesystem::path, AssetHandle> assetsMapping;
std::unordered_map<std::filesystem::path, AssetHandle*> assetsMapping;
std::vector<size_t> freeAssetSlots;
std::vector<AssetLoadingEntry> loadingEntries;
@ -111,7 +114,7 @@ private:
template <Asset T> T& AssetManager::Get(const AssetHandle &handle)
{
std::atomic<void*> result;
AssetStreamCommand command = {
AssetStreamCommand command {
.type = AssetStreamCommand::GET,
.target = handle,
.out = &result

View File

@ -46,11 +46,13 @@ public:
virtual FileHandle& Open(const std::filesystem::path &path, const uint32_t flags) = 0;
virtual void ReadAsync(FileHandle &file, const uint32_t bufferSize, const uint32_t flags) = 0;
virtual bool PollReady(FileHandle &file) = 0;
virtual void Close(const FileHandle &file) = 0;
virtual void Close(FileHandle &file) = 0;
~IFileSystem();
protected:
std::vector<FileHandle> activeFiles;
std::vector<FileHandle> failedFiles;
std::vector<FileHandle*> activeFiles;
std::vector<FileHandle*> failedFiles;
std::vector<size_t> freeFileSlots;
};
@ -60,7 +62,7 @@ public:
FileHandle& Open(const std::filesystem::path &path, const uint32_t flags) override;
void ReadAsync(FileHandle &file, const uint32_t bufferSize, uint32_t flags) override;
bool PollReady(FileHandle &file) override;
void Close(const FileHandle &file) override;
void Close(FileHandle &file) override;
private:

View File

@ -1,6 +1,7 @@
#include "AssetManager.hpp"
#include "allocator.hpp"
#include "Logger.h"
#include <stdexcept>
AssetInfo::AssetInfo(void *resource)
{
@ -81,7 +82,7 @@ void AssetManager::Request(const AssetStreamCommand::Type &type)
void AssetManager::LoadAsync(AssetInfo::Type type, const std::filesystem::path &path)
{
AssetStreamCommand command = {
AssetStreamCommand command {
.type = AssetStreamCommand::LOAD,
.targetPath = path,
.targetType = type
@ -92,7 +93,7 @@ void AssetManager::LoadAsync(AssetInfo::Type type, const std::filesystem::path &
AssetHandle& AssetManager::Find(const std::filesystem::path &path)
{
std::atomic<void*> result;
AssetStreamCommand command = {
AssetStreamCommand command {
.type = AssetStreamCommand::FIND,
.targetPath = path,
.out = &result
@ -104,7 +105,7 @@ AssetHandle& AssetManager::Find(const std::filesystem::path &path)
void AssetManager::Start()
{
if (isRunning) throw std::exception("Asset Streaming can be started only once");
if (isRunning) throw std::logic_error("Asset Streaming can be started only once");
isRunning.store(true);
streamingThread = std::jthread([this](std::stop_token stop) {
@ -156,7 +157,7 @@ void AssetManager::Loop()
//TODO: functions for each type
switch (asset.type)
{
case AssetInfo::SHADER:
case AssetInfo::MATERIAL:
{
break;
}
@ -166,6 +167,7 @@ void AssetManager::Loop()
break;
}
case AssetInfo::SHADER:
case AssetInfo::BINARY:
default:
{
@ -176,8 +178,13 @@ void AssetManager::Loop()
}
}
asset.isReady.store(true);
entry.fileOwner->Close(*entry.file);
#ifndef NDEBUG
auto path = entry.file->debugPath.c_str();
LOG(INFO, "Asset loaded: [%s]", path);
#endif
entry.file = nullptr;
asset.isReady.store(true);
}
}
@ -192,7 +199,7 @@ void AssetManager::ProcessCommand(AssetStreamCommand &command)
{
case AssetStreamCommand::LOAD:
{
LOG(INFO, "LOAD command");
LOG(INFO, "LOAD command: [%s]", command.targetPath.c_str());
/* TODO: process path to select FS */
/* TODO: select between async load and sync job task */
@ -202,7 +209,7 @@ void AssetManager::ProcessCommand(AssetStreamCommand &command)
diskSystem->ReadAsync(file, static_cast<uint32_t>(file.size), FILE_READ_SEQUENTIAL);
AssetHandle handle;
AssetHandle* handle = Allocate<AssetHandle>();
AssetInfo asset(nullptr);
asset.type = command.targetType;
@ -218,11 +225,11 @@ void AssetManager::ProcessCommand(AssetStreamCommand &command)
activeAssets.push_back(std::move(asset));
assetPos = activeAssets.size() - 1;
}
handle.id = assetPos;
handle->id = assetPos;
assetsMapping[command.targetPath] = handle;
AssetLoadingEntry loadingEntry = {
.handle = handle,
AssetLoadingEntry loadingEntry {
.handle = *handle,
.file = &file,
.fileOwner = diskSystem
};
@ -234,12 +241,12 @@ void AssetManager::ProcessCommand(AssetStreamCommand &command)
case AssetStreamCommand::FIND:
{
std::unordered_map<std::filesystem::path, AssetHandle>::iterator mapping = assetsMapping.find(command.targetPath);
std::unordered_map<std::filesystem::path, AssetHandle*>::iterator mapping = assetsMapping.find(command.targetPath);
if (mapping == assetsMapping.end())
break;
command.out->store(&mapping->second);
command.out->store(mapping->second);
command.type = AssetStreamCommand::COMPLETED;
break;
@ -280,6 +287,20 @@ void AssetManager::Release()
}
activeAssets.clear();
for (auto &mapping : assetsMapping)
Deallocate(mapping.second);
assetsMapping.clear();
LOG(INFO, "Asset Streaming is released");
isRunning.store(false);
}
void AssetManager::LoadShaders()
{
std::filesystem::path shadersRoot = "assets/shaders";
for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(shadersRoot))
{
if (entry.is_directory()) continue;
LoadAsync(AssetInfo::SHADER, entry.path());
}
}

View File

@ -1,15 +1,24 @@
#include "FileSystem.hpp"
#include "Logger.h"
#include "allocator.hpp"
#include <stdexcept>
IFileSystem::~IFileSystem()
{
for (FileHandle* handle : activeFiles)
Deallocate(handle);
for (FileHandle* handle : failedFiles)
Deallocate(handle);
}
#ifdef _WIN32
FileHandle& NativeFileSystem::Open(const std::filesystem::path &path, const uint32_t flags)
{
FileHandle result;
FileHandle* result = Allocate<FileHandle>();
#ifndef NDEBUG
result.debugPath = path;
result->debugPath = path;
#endif
DWORD createFileMode = 0;
@ -33,59 +42,83 @@ FileHandle& NativeFileSystem::Open(const std::filesystem::path &path, const uint
{
LOG(ERROR, "Failed to open file %s. Error: %d", path.c_str(), GetLastError());
failedFiles.push_back(result);
return failedFiles[result.id];
return *result;
}
result.win32Handle = file;
result.size = GetFileSize(file, NULL);
if (result.size == INVALID_FILE_SIZE)
result->win32Handle = file;
result->size = GetFileSize(file, NULL);
if (result->size == INVALID_FILE_SIZE)
LOG(ERROR, "Failed to get size of file %s. Error: %d", path.c_str(), GetLastError());
if (freeFileSlots.size() > 0)
{
activeFiles[freeFileSlots.back()] = result;
result.id = freeFileSlots.back();
size_t pos = freeFileSlots.back();
result->id = pos;
activeFiles[pos] = result;
freeFileSlots.pop_back();
}
else
{
result->id = activeFiles.size();
activeFiles.push_back(result);
result.id = activeFiles.size() - 1;
}
return activeFiles[result.id];
return *result;
}
void NativeFileSystem::ReadAsync(FileHandle &file, const uint32_t bufferSize, const uint32_t flags)
{
file.overlapped = {};
file.overlapped = {
.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)
};
file.buffer = AllocateArray<uint8_t>(bufferSize);
ReadFile(
BOOL isSync = ReadFile(
file.win32Handle,
file.buffer,
bufferSize,
NULL,
&file.overlapped
);
if (isSync || GetLastError() != ERROR_IO_PENDING)
LOG(ERROR, "File reading is synchronious while asynchronious is awaited");
}
bool NativeFileSystem::PollReady(FileHandle &file)
{
DWORD transferredSize = 0L;
BOOL status = GetOverlappedResult(
file.win32Handle,
&file.overlapped,
&transferredSize,
FALSE
);
if (!status && GetLastError() != ERROR_IO_INCOMPLETE)
{
LOG(ERROR, "Error in file status. Code: %d", GetLastError());
return false;
}
// DWORD transferredSize = 0L;
// BOOL status = GetOverlappedResult(
// file.win32Handle,
// &file.overlapped,
// &transferredSize,
// FALSE
// );
if (file.size == transferredSize)
// DWORD error = GetLastError();
// if (!status && error != ERROR_IO_INCOMPLETE)
// {
// LOG(CRITICAL, "Error in reading file status. Code: %d", error);
// throw std::runtime_error("Error in reading file status");
// }
// if (file.size == transferredSize)
// {
// file.data = file.buffer;
// }
// else
// {
// /* TODO: reserve size for data, shift and move */
// }
// file.buffer = nullptr;
DWORD status = WaitForSingleObject(file.overlapped.hEvent, 0);
switch (status)
{
case WAIT_OBJECT_0:
{
if (file.size == file.bufferSize)
{
file.data = file.buffer;
}
@ -93,18 +126,42 @@ bool NativeFileSystem::PollReady(FileHandle &file)
{
/* TODO: reserve size for data, shift and move */
}
file.buffer = nullptr;
return status;
break;
}
void NativeFileSystem::Close(const FileHandle &file)
case WAIT_ABANDONED:
case WAIT_TIMEOUT:
return false;
default:
{
LOG(CRITICAL, "Failed to get file status. Error: %d", GetLastError());
throw std::runtime_error("Failed to get file status");
}
}
return true;
}
void NativeFileSystem::Close(FileHandle &file)
{
if (file.buffer != nullptr)
{
DeallocateArray(file.buffer);
file.buffer = nullptr;
}
if (file.data != nullptr)
{
DeallocateArray(file.data);
file.data = nullptr;
}
if (file.win32Handle != INVALID_HANDLE_VALUE)
{
CloseHandle(file.win32Handle);
activeFiles[file.id].win32Handle = INVALID_HANDLE_VALUE;
file.win32Handle = INVALID_HANDLE_VALUE;
}
freeFileSlots.push_back(file.id);
}

View File

@ -6,8 +6,8 @@
#include "Logger.h"
#include <allocator.hpp>
#include <algorithm>
#include <exception>
#include <set>
#include <stdexcept>
Renderer::Renderer(SDL_Window *window) : isRunning(false)
{
@ -228,7 +228,7 @@ void Renderer::SetupDevice()
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
priority += GPU_DISCRETE_PRIORITY;
for (VkSurfaceFormatKHR format : surfaceInfoBuffer.availableFormats)
for (const VkSurfaceFormatKHR &format : surfaceInfoBuffer.availableFormats)
{
surfaceInfoBuffer.format = format;
if (format.format == VK_FORMAT_B8G8R8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
@ -239,7 +239,7 @@ void Renderer::SetupDevice()
}
surfaceInfoBuffer.presentMode = VK_PRESENT_MODE_FIFO_KHR;
for (VkPresentModeKHR presentMode : surfaceInfoBuffer.availablePresentModes)
for (const VkPresentModeKHR &presentMode : surfaceInfoBuffer.availablePresentModes)
{
if (presentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)
{
@ -265,7 +265,7 @@ void Renderer::SetupDevice()
// Define queues
std::vector<VkDeviceQueueCreateInfo> queuesCreateConfigs;
std::set<uint32_t> uniqueQueueFamilies = {
std::set<uint32_t> uniqueQueueFamilies {
queueFamilies.graphics,
queueFamilies.present
};
@ -284,11 +284,11 @@ void Renderer::SetupDevice()
}
// Create logical device
std::vector<const char*> deviceExtensions = {
std::vector<const char*> deviceExtensions {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
VkDeviceCreateInfo deviceInfo = {
VkDeviceCreateInfo deviceInfo {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = nullptr,
.queueCreateInfoCount = static_cast<uint32_t>(queuesCreateConfigs.size()),
@ -355,7 +355,7 @@ void Renderer::SetupSwapchain()
if (queueFamilies.graphics != queueFamilies.present)
{
uint32_t queueFamiliesShare[] = {queueFamilies.graphics, queueFamilies.present};
uint32_t queueFamiliesShare[] {queueFamilies.graphics, queueFamilies.present};
swapchainCreateConfig.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swapchainCreateConfig.queueFamilyIndexCount = 2;
swapchainCreateConfig.pQueueFamilyIndices = queueFamiliesShare;
@ -386,13 +386,13 @@ void Renderer::SetupSwapchain()
.image = frames[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = surfaceInfo.format.format,
.components = {
.components {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY
},
.subresourceRange = {
.subresourceRange {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
@ -413,7 +413,7 @@ void Renderer::Start()
if (isRunning)
{
LOG(CRITICAL, "Renderer can be started only once");
throw std::exception("Renderer can be started only once");
throw std::logic_error("Renderer can be started only once");
}
isRunning.store(true);
@ -493,7 +493,7 @@ void Renderer::Request(const RenderCommand::Type &type)
void Renderer::Release()
{
for (VkImageView frameView : frameViews)
for (VkImageView &frameView : frameViews)
{
vkDestroyImageView(device, frameView, nullptr);
}

View File

@ -43,6 +43,7 @@ int SDL_main(int argc, char **argv)
// Setup assets
AssetManager assetManager;
assetManager.Start();
assetManager.LoadShaders();
// Setup Renderer
Renderer renderer(window);

View File

@ -0,0 +1,8 @@
#version 330
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(1.0, 0.0, 0.0, 1.0);
}

View File

@ -0,0 +1,12 @@
#version 330
vec2 positions[3] = vec2[](
vec2(-1.0, 1.0),
vec2(0.0, -1.0),
vec2(1.0, 1.0)
);
void main()
{
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
}