ADD: Upload Intermediate Assets to GPU, closes #18
All checks were successful
Tests / test (push) Successful in 3m2s

This commit is contained in:
Sebastian Böckelmann 2026-04-21 09:05:09 +02:00
parent b71aa63b71
commit 89e98d0f88
9 changed files with 137 additions and 16 deletions

View File

@ -29,7 +29,7 @@ FetchContent_Declare(
GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.7.1 GIT_TAG v3.7.1
) )
FetchContent_MakeAvailable(Catch2)
add_executable(LayoutEngineTests add_executable(LayoutEngineTests
tests/layout/LayoutEngineTest.cpp tests/layout/LayoutEngineTest.cpp
@ -38,7 +38,18 @@ target_include_directories(LayoutEngineTests PRIVATE
src src
lib/glm lib/glm
) )
target_link_libraries(LayoutEngineTests PRIVATE Catch2::Catch2WithMain)
include(FetchContent)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.17.0
)
FetchContent_MakeAvailable(Catch2 spdlog)
target_link_libraries(LayoutEngineTests PRIVATE Catch2::Catch2WithMain spdlog::spdlog)
include(CTest) include(CTest)
include(Catch) include(Catch)
@ -322,6 +333,7 @@ if(BUILD_GAME)
glad glad
OpenGL::GL OpenGL::GL
${FREETYPE_LIBRARIES} ${FREETYPE_LIBRARIES}
spdlog::spdlog
) )
endif() endif()

View File

@ -12,6 +12,7 @@
#include "json.hpp" #include "json.hpp"
#include "OBJLoader.h" #include "OBJLoader.h"
#include "spdlog/spdlog.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace nlohmann; using namespace nlohmann;
@ -122,6 +123,34 @@ std::shared_ptr<UiTheme> AssetManager::loadUiTheme(const std::string &themeName,
} }
} }
void AssetManager::insertLoadedTexture(const std::string &name, const Texture2D &texture) {
if (textures.contains(name)) {
spdlog::warn("Texture '{}' already exists, skipping insert", name);
return;
}
textures[name] = std::make_shared<ModelTexture>(texture.id);
spdlog::debug("Texture '{}' inserted successfully", name);
}
void AssetManager::insertTexturedModel(const std::string &name, const TexturedModel &textured_model) {
if (models.contains(name)) {
spdlog::warn("Model '{}' already exists, skipping insert", name);
return;
}
models[name] = std::make_shared<TexturedModel>(textured_model);
spdlog::debug("Model '{}' inserted successfully", name);
}
void AssetManager::insertModelStages(const std::string &name, ModelStages modelStages) {
if (modelsWithStages.contains(name)) {
spdlog::warn("Asset '{}' already has stages, skipping insert", name);
return;
}
modelsWithStages[name] = std::make_shared<ModelStages>(modelStages);
}
json AssetManager::readJsonFile(const std::string &path) { json AssetManager::readJsonFile(const std::string &path) {

View File

@ -10,6 +10,7 @@
#include "json.hpp" #include "json.hpp"
#include "../model/TexturedModel.h" #include "../model/TexturedModel.h"
#include "Loader.h" #include "Loader.h"
#include "Texture2D.h"
#include "../../core/gui/text/UiTheme.h" #include "../../core/gui/text/UiTheme.h"
#include "../model/ModelStages.h" #include "../model/ModelStages.h"
@ -28,6 +29,11 @@ public:
static std::shared_ptr<ModelTexture> getTexture(const std::string& texturePath); static std::shared_ptr<ModelTexture> getTexture(const std::string& texturePath);
static std::shared_ptr<UiTheme> getUiTheme(const std::string& themeName); static std::shared_ptr<UiTheme> getUiTheme(const std::string& themeName);
static std::shared_ptr<UiTheme> loadUiTheme(const std::string& themeName, const std::string& themePath); static std::shared_ptr<UiTheme> loadUiTheme(const std::string& themeName, const std::string& themePath);
static void insertLoadedTexture(const std::string& name, const Texture2D& texture);
static void insertTexturedModel(const std::string & name, const TexturedModel & textured_model);
static void insertModelStages(const std::string &name, ModelStages modelStages);
private: private:
static inline std::unordered_map<std::string, std::shared_ptr<TexturedModel>> models; static inline std::unordered_map<std::string, std::shared_ptr<TexturedModel>> models;
static inline std::unordered_map<std::string, std::shared_ptr<ModelStages>> modelsWithStages; static inline std::unordered_map<std::string, std::shared_ptr<ModelStages>> modelsWithStages;

View File

@ -158,3 +158,21 @@ RawModelData OBJLoader::loadModel(const std::string &modelPath) {
} }
return {"", subModels}; return {"", subModels};
} }
TexturedModel OBJLoader::uploadToGPU(RawModelData &rawModelData) {
Loader loader = Loader::instance();
std::vector<SubModel> subModels;
for (const auto&[vertices, normals, textureCoords, indices, textures] : rawModelData.subModels) {
RawModel rawModel = loader.loadToVAO(vertices, normals, textureCoords, indices);
RawTextureData textureData = textures[0].textureData;
const Texture2D texture = TextureLoader::uploadToGPU(textureData);
auto modelTexture = ModelTexture(texture.id);
auto subModel = SubModel(std::make_shared<RawModel>(rawModel), std::make_shared<ModelTexture>(modelTexture));
subModels.push_back(subModel);
}
return TexturedModel(subModels);
}

View File

@ -16,6 +16,7 @@ class OBJLoader {
public: public:
static std::shared_ptr<TexturedModel> loadModel(const std::string &modelPath, const std::string &texturePath, Loader &loader); static std::shared_ptr<TexturedModel> loadModel(const std::string &modelPath, const std::string &texturePath, Loader &loader);
static RawModelData loadModel(const std::string &modelPath); static RawModelData loadModel(const std::string &modelPath);
static TexturedModel uploadToGPU(RawModelData &rawModelData);
}; };

View File

@ -18,7 +18,7 @@ public:
static Texture2D loadTexture(const std::string& path, bool flipVertically= true); static Texture2D loadTexture(const std::string& path, bool flipVertically= true);
static RawTextureData loadRawTextureData(const std::string &path, bool flipVertically = true); static RawTextureData loadRawTextureData(const std::string &path, bool flipVertically = true);
static TextureData loadTextureData(const std::string &path, bool flipVertically = true, TextureType textureType); static TextureData loadTextureData(const std::string &path, bool flipVertically = true, TextureType textureType);
static Texture2D uploadToGPU(RawTextureData& rawTextureData); static Texture2D uploadToGPU(RawTextureData &rawTextureData);
static Texture2D uploadToGPU(TextureData& textureData); static Texture2D uploadToGPU(TextureData& textureData);
static void free(Texture2D& texture); static void free(Texture2D& texture);
}; };

View File

@ -53,15 +53,7 @@ void AssetLoader::stop() {
} }
} }
std::vector<IntermediateAsset> AssetLoader::processUploadQueue(int maxPerFrame) {
std::vector<IntermediateAsset> results;
std::lock_guard lock(readyMutex);
for (int i = 0; i < maxPerFrame && !readyQueue.empty(); ++i) {
results.push_back(std::move(readyQueue.front()));
readyQueue.pop();
}
return results;
}
LoadingProgress AssetLoader::getProgress() const { LoadingProgress AssetLoader::getProgress() const {
return {total.load(), loaded.load()}; return {total.load(), loaded.load()};
@ -84,8 +76,8 @@ void AssetLoader::loadingThreadFunc() {
pendingQueue.pop(); pendingQueue.pop();
} }
IntermediateAsset result = std::visit([](auto& req) -> IntermediateAsset { IntermediateAsset result = std::visit([]<typename T0>(T0& req) -> IntermediateAsset {
using T = std::decay_t<decltype(req)>; using T = std::decay_t<T0>;
if constexpr (std::is_same_v<T, TextureRequest>) { if constexpr (std::is_same_v<T, TextureRequest>) {
return processTextureRequest(req); return processTextureRequest(req);
} else if constexpr (std::is_same_v<T, ModelRequest>) { } else if constexpr (std::is_same_v<T, ModelRequest>) {
@ -98,7 +90,6 @@ void AssetLoader::loadingThreadFunc() {
{ {
std::lock_guard lock(readyMutex); std::lock_guard lock(readyMutex);
readyQueue.push(std::move(result)); readyQueue.push(std::move(result));
++loaded;
} }
} }
} }
@ -151,6 +142,61 @@ nlohmann::json AssetLoader::loadJson(const std::string &path) {
return nlohmann::json::object(); return nlohmann::json::object();
} }
void AssetLoader::processUploadQueue(int maxPerFrame) {
auto uploadQueue = determineUploadQueue(maxPerFrame);
for (const auto& intermediateAsset : uploadQueue) {
std::visit([]<typename T0>(T0& rawData) -> IntermediateAsset {
using T = std::decay_t<T0>;
if constexpr (std::is_same_v<T, RawTextureData>) {
return processIntermediateTextureAsset(rawData);
} else if constexpr (std::is_same_v<T, RawModelData>) {
return processIntermediateModelAsset(rawData);
} else {
return processIntermediateStagedModelAsset(rawData);
}
}, intermediateAsset);
++loaded;
}
}
std::vector<IntermediateAsset> AssetLoader::determineUploadQueue(const int maxPerFrame) {
std::vector<IntermediateAsset> results;
std::lock_guard lock(readyMutex);
for (int i = 0; i < maxPerFrame && !readyQueue.empty(); ++i) {
results.push_back(std::move(readyQueue.front()));
readyQueue.pop();
}
return results;
}
void AssetLoader::processIntermediateTextureAsset(RawTextureData &textureData) {
const Texture2D texture = TextureLoader::uploadToGPU(textureData);
AssetManager::insertLoadedTexture(textureData.name, texture);
}
void AssetLoader::processIntermediateModelAsset(RawModelData &modelData) {
const TexturedModel texturedModel = OBJLoader::uploadToGPU(modelData);
AssetManager::insertTexturedModel(modelData.name, texturedModel);
}
void AssetLoader::processIntermediateStagedModelAsset(const RawStagedModelData &stagedModelData) {
ModelStages modelStages;
for (const auto&[stageName, conditionKey, minValue, maxValue, modelData] : stagedModelData.stages) {
RawModelData rawModelData = modelData;
processIntermediateModelAsset(rawModelData);
const auto model = AssetManager::getModel(stageName);
const auto condition = ModelStageCondition(conditionKey, minValue, maxValue);
auto modelStageConfiguration = ModelStageConfiguration(model, stageName, condition);
modelStages.addModelStage(modelStageConfiguration);
}
AssetManager::insertModelStages(stagedModelData.name, modelStages);
}

View File

@ -59,7 +59,7 @@ public:
void start(); void start();
void stop(); void stop();
std::vector<IntermediateAsset> processUploadQueue(int maxPerFrame); void processUploadQueue(int maxPerFrame);
LoadingProgress getProgress() const; LoadingProgress getProgress() const;
@ -70,6 +70,11 @@ private:
static RawModelData processModelRequest(const ModelRequest& request); static RawModelData processModelRequest(const ModelRequest& request);
static RawStagedModelData processStagedModelRequest(const StagedModelRequest& request); static RawStagedModelData processStagedModelRequest(const StagedModelRequest& request);
static nlohmann::json loadJson(const std::string& path); static nlohmann::json loadJson(const std::string& path);
std::vector<IntermediateAsset> determineUploadQueue(int maxPerFrame);
static void processIntermediateTextureAsset(RawTextureData &textureData);
static void processIntermediateModelAsset(RawModelData &modelData);
static void processIntermediateStagedModelAsset(const RawStagedModelData &stagedModelData);
// Render-Thread schreibt, Loading Thread liest // Render-Thread schreibt, Loading Thread liest
std::queue<AssetRequest> pendingQueue; std::queue<AssetRequest> pendingQueue;

View File

@ -2,6 +2,7 @@
#include "engine/core/Application.h" #include "engine/core/Application.h"
#include "game/DicewarsApp.h" #include "game/DicewarsApp.h"
#include "spdlog/spdlog-inl.h"
// TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter. // TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
Application* CreateApplication() Application* CreateApplication()
@ -9,6 +10,9 @@ Application* CreateApplication()
return new DicewarsApp(); return new DicewarsApp();
} }
int main() { int main() {
spdlog::set_level(spdlog::level::debug); // oder info für Release
spdlog::set_pattern("[%H:%M:%S] [%^%l%$] %v");
auto app = CreateApplication(); auto app = CreateApplication();
app->run(); app->run();
delete app; delete app;