Files
luma/src/assets.cpp
T
khodowany bb96aaf95d
publish / publish (push) Successful in 15m37s
Formatting
2026-09-14 23:56:06 -05:00

129 lines
4.6 KiB
C++

#include <luma/assets.hpp>
#include <cassert>
#include <condition_variable>
#include <cstdint>
#include <expected>
#include <exception>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>
#include <modulus.hpp>
#include <modulus/log.hpp>
#include <luma/detail/errors.hpp>
struct luma::asset_cache::in_flight_load {
std::condition_variable ready{};
std::thread::id owner{};
std::expected<std::shared_ptr<luma::texture>, modulus::error> result{};
std::bool_t done{false};
};
luma::asset_cache::asset_cache(luma::renderer_resources &resources)
: resources_{&resources} {
assert(resources_ != nullptr);
}
auto luma::asset_cache::texture(std::string const &path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> {
assert(resources_ != nullptr);
if (path.empty()) {
return luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_invalid_argument, "texture path must not be empty");
}
auto generation = std::uint64_t{};
auto loading = std::shared_ptr<luma::asset_cache::in_flight_load>{};
{
auto lock = std::unique_lock{mutex_};
if (auto found = textures_.find(path); found != textures_.end()) {
assert(found->second != nullptr);
return modulus::success(found->second);
}
if (auto found = loading_.find(path); found != loading_.end()) {
loading = found->second;
assert(loading != nullptr);
if (loading->owner == std::this_thread::get_id()) {
return luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_unavailable, "texture load reentered for path: " + path);
}
loading->ready.wait(lock, [&loading] {
return loading->done;
});
if (auto cached = textures_.find(path); cached != textures_.end()) {
assert(cached->second != nullptr);
return modulus::success(cached->second);
}
if (!loading->result) {
return modulus::fail<std::shared_ptr<luma::texture>>(loading->result.error());
}
assert(*loading->result != nullptr);
return modulus::success(*loading->result);
}
loading = std::make_shared<luma::asset_cache::in_flight_load>();
loading->owner = std::this_thread::get_id();
loading_.emplace(path, loading);
generation = generation_;
}
auto loaded = std::expected<std::shared_ptr<luma::texture>, modulus::error>{};
modulus::log_debug("luma texture loading: " + path);
try {
loaded = resources_->load_texture(path);
} catch (std::exception const &error) {
loaded = luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_internal, std::string{"texture load failed: "} + error.what());
} catch (...) {
loaded = luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_internal, "texture load failed with an unknown exception");
}
if (!loaded) {
modulus::log_debug("luma texture load failed: " + path);
auto lock = std::scoped_lock{mutex_};
loading->result = modulus::fail<std::shared_ptr<luma::texture>>(loaded.error());
loading->done = true;
loading_.erase(path);
loading->ready.notify_all();
return loaded;
}
if (*loaded == nullptr) {
modulus::log_debug("luma texture load returned null: " + path);
auto result = luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_not_found, "texture load returned no texture: " + path);
auto lock = std::scoped_lock{mutex_};
loading->result = modulus::fail<std::shared_ptr<luma::texture>>(result.error());
loading->done = true;
loading_.erase(path);
loading->ready.notify_all();
return result;
}
auto texture = *loaded;
auto lock = std::scoped_lock{mutex_};
if (auto found = textures_.find(path); found != textures_.end()) {
assert(found->second != nullptr);
loading->result = modulus::success(found->second);
loading->done = true;
loading_.erase(path);
loading->ready.notify_all();
return modulus::success(found->second);
}
if (generation != generation_) {
loading->result = modulus::success(texture);
loading->done = true;
loading_.erase(path);
loading->ready.notify_all();
return modulus::success(std::move(texture));
}
auto inserted = textures_.emplace(path, std::move(texture));
assert(inserted.first->second != nullptr);
loading->result = modulus::success(inserted.first->second);
loading->done = true;
loading_.erase(path);
loading->ready.notify_all();
return modulus::success(inserted.first->second);
}
auto luma::asset_cache::clear() -> void {
assert(resources_ != nullptr);
auto lock = std::scoped_lock{mutex_};
++generation_;
textures_.clear();
}