mirror of
https://github.com/encounter/aurora.git
synced 2026-07-09 18:19:33 -07:00
Runtime texture replacement API
This commit is contained in:
@@ -6,5 +6,4 @@ set_target_properties(aurora_dvd PROPERTIES FOLDER "aurora")
|
||||
|
||||
target_compile_definitions(aurora_dvd PUBLIC AURORA TARGET_PC)
|
||||
target_include_directories(aurora_dvd PUBLIC include)
|
||||
target_link_libraries(aurora_dvd PUBLIC nod::nod ${AURORA_SDL3_TARGET})
|
||||
target_link_libraries(aurora_dvd PRIVATE fmt::fmt)
|
||||
target_link_libraries(aurora_dvd PUBLIC fmt::fmt nod::nod ${AURORA_SDL3_TARGET})
|
||||
|
||||
@@ -90,7 +90,6 @@ typedef struct {
|
||||
bool startFullscreen;
|
||||
bool allowJoystickBackgroundEvents;
|
||||
bool pauseOnFocusLost;
|
||||
bool allowTextureReplacements;
|
||||
bool allowTextureDumps;
|
||||
bool allowCpuAdapter;
|
||||
int32_t windowPosX;
|
||||
|
||||
@@ -31,8 +31,6 @@ typedef struct {
|
||||
const AuroraStats* aurora_get_stats();
|
||||
|
||||
void aurora_enable_vsync(bool enabled);
|
||||
void aurora_set_texture_replacements_enabled(bool enabled);
|
||||
void aurora_reload_texture_replacements();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#ifndef AURORA_HD_TEXTURE_HPP
|
||||
#define AURORA_HD_TEXTURE_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace aurora::gfx {
|
||||
|
||||
// HD texture replacement: dusk's tphd layer registers a pixel-pointer ->
|
||||
// decoded HD buffer mapping at arc-load time; init_texobj_common swaps in
|
||||
// the HD bytes when the game calls GXInitTexObj with a registered pointer.
|
||||
struct HdReplacement {
|
||||
std::vector<uint8_t> bytes;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t gxFormat;
|
||||
uint32_t mipCount = 1;
|
||||
};
|
||||
|
||||
void hd_register_replacement(const void* gcDataPtr, HdReplacement replacement) noexcept;
|
||||
const HdReplacement* hd_lookup_replacement(const void* gcDataPtr) noexcept;
|
||||
void hd_clear_replacements() noexcept;
|
||||
|
||||
struct HdArcRange {
|
||||
const void* begin;
|
||||
size_t size;
|
||||
std::string label;
|
||||
};
|
||||
|
||||
void hd_register_arc_range(const void* begin, size_t size, std::string_view label) noexcept;
|
||||
const HdArcRange* hd_find_arc_range(const void* ptr, size_t* out_remaining) noexcept;
|
||||
void hd_clear_arc_ranges() noexcept;
|
||||
|
||||
} // namespace aurora::gfx
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef AURORA_TEXTURE_HPP
|
||||
#define AURORA_TEXTURE_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace aurora::texture {
|
||||
|
||||
struct TextureSourceKey {
|
||||
uint64_t textureHash = 0;
|
||||
uint64_t tlutHash = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t format = 0;
|
||||
bool hasTlut = false;
|
||||
|
||||
bool operator==(const TextureSourceKey&) const = default;
|
||||
};
|
||||
|
||||
struct TexturePointerKey {
|
||||
const void* data = nullptr;
|
||||
|
||||
bool operator==(const TexturePointerKey&) const = default;
|
||||
};
|
||||
|
||||
using ReplacementKey = std::variant<TexturePointerKey, TextureSourceKey>;
|
||||
|
||||
struct RawTextureReplacement {
|
||||
std::span<const uint8_t> bytes;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t mipCount = 1;
|
||||
uint32_t gxFormat = 0;
|
||||
std::string_view label = {};
|
||||
};
|
||||
|
||||
struct ReplacementOptions {
|
||||
int32_t priority = 0;
|
||||
};
|
||||
|
||||
struct ReplacementRegistration {
|
||||
uint64_t id = 0;
|
||||
ReplacementKey key;
|
||||
};
|
||||
|
||||
struct ReplacementGroup {
|
||||
std::vector<ReplacementRegistration> registrations;
|
||||
};
|
||||
|
||||
ReplacementRegistration register_replacement(ReplacementKey key, RawTextureReplacement replacement,
|
||||
ReplacementOptions options = {});
|
||||
void unregister_replacement(const ReplacementRegistration& registration);
|
||||
void unregister_replacements(std::span<const ReplacementRegistration> registrations);
|
||||
void unregister_replacements(const ReplacementGroup& group);
|
||||
void unregister_replacements(const ReplacementKey& key);
|
||||
void clear_replacements();
|
||||
|
||||
ReplacementGroup load_replacement_directory(const std::filesystem::path& root, ReplacementOptions options = {});
|
||||
void reload_replacement_directory(const std::filesystem::path& root, ReplacementGroup& group,
|
||||
ReplacementOptions options = {});
|
||||
|
||||
} // namespace aurora::texture
|
||||
|
||||
#endif
|
||||
@@ -4,68 +4,11 @@
|
||||
#include "../../gfx/texture.hpp"
|
||||
#include "../../gfx/texture_replacement.hpp"
|
||||
#include "dolphin/gx/GXAurora.h"
|
||||
#include <aurora/hd_texture.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
namespace aurora::gfx {
|
||||
namespace {
|
||||
std::unordered_map<const void*, HdReplacement> s_hdReplacements;
|
||||
std::shared_mutex s_hdReplacementsMutex;
|
||||
|
||||
std::vector<HdArcRange> s_hdArcRanges;
|
||||
std::shared_mutex s_hdArcRangesMutex;
|
||||
}
|
||||
|
||||
void hd_register_replacement(const void* gcDataPtr, HdReplacement replacement) noexcept {
|
||||
if (gcDataPtr == nullptr) return;
|
||||
std::unique_lock lk(s_hdReplacementsMutex);
|
||||
s_hdReplacements[gcDataPtr] = std::move(replacement);
|
||||
}
|
||||
|
||||
const HdReplacement* hd_lookup_replacement(const void* gcDataPtr) noexcept {
|
||||
if (gcDataPtr == nullptr) return nullptr;
|
||||
std::shared_lock lk(s_hdReplacementsMutex);
|
||||
auto it = s_hdReplacements.find(gcDataPtr);
|
||||
return (it == s_hdReplacements.end()) ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
void hd_clear_replacements() noexcept {
|
||||
std::unique_lock lk(s_hdReplacementsMutex);
|
||||
s_hdReplacements.clear();
|
||||
}
|
||||
|
||||
void hd_register_arc_range(const void* begin, size_t size, std::string_view label) noexcept {
|
||||
if (begin == nullptr || size == 0) return;
|
||||
std::unique_lock lk(s_hdArcRangesMutex);
|
||||
s_hdArcRanges.push_back(HdArcRange{begin, size, std::string(label)});
|
||||
}
|
||||
|
||||
const HdArcRange* hd_find_arc_range(const void* ptr, size_t* out_remaining) noexcept {
|
||||
if (ptr == nullptr) return nullptr;
|
||||
const auto p = reinterpret_cast<uintptr_t>(ptr);
|
||||
std::shared_lock lk(s_hdArcRangesMutex);
|
||||
for (const auto& r : s_hdArcRanges) {
|
||||
const auto begin = reinterpret_cast<uintptr_t>(r.begin);
|
||||
const auto end = begin + r.size;
|
||||
if (p >= begin && p < end) {
|
||||
if (out_remaining) *out_remaining = static_cast<size_t>(end - p);
|
||||
return &r;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void hd_clear_arc_ranges() noexcept {
|
||||
std::unique_lock lk(s_hdArcRangesMutex);
|
||||
s_hdArcRanges.clear();
|
||||
}
|
||||
} // namespace aurora::gfx
|
||||
|
||||
namespace {
|
||||
constexpr u8 GXTexMode0Ids[8] = {0x80, 0x81, 0x82, 0x83, 0xA0, 0xA1, 0xA2, 0xA3};
|
||||
constexpr u8 GXTexMode1Ids[8] = {0x84, 0x85, 0x86, 0x87, 0xA4, 0xA5, 0xA6, 0xA7};
|
||||
@@ -107,15 +50,6 @@ int __cntlzw(unsigned int val) {
|
||||
|
||||
void init_texobj_common(GXTexObj_& obj, const void* data, u16 width, u16 height, u32 format, GXTexWrapMode wrapS,
|
||||
GXTexWrapMode wrapT, GXBool mipmap) {
|
||||
// HD texture replacement
|
||||
const auto* repl = aurora::gfx::hd_lookup_replacement(data);
|
||||
if (repl != nullptr) {
|
||||
data = repl->bytes.data();
|
||||
width = static_cast<u16>(repl->width);
|
||||
height = static_cast<u16>(repl->height);
|
||||
format = repl->gxFormat;
|
||||
mipmap = (repl->mipCount > 1) ? GX_TRUE : GX_FALSE;
|
||||
}
|
||||
obj = {};
|
||||
obj.mWidth = width;
|
||||
obj.mHeight = height;
|
||||
|
||||
@@ -1089,10 +1089,3 @@ void pop_debug_group() {
|
||||
}
|
||||
|
||||
const AuroraStats* aurora_get_stats() { return &aurora::gfx::g_stats; }
|
||||
|
||||
void aurora_set_texture_replacements_enabled(const bool enabled) {
|
||||
aurora::g_config.allowTextureReplacements = enabled;
|
||||
aurora::gfx::texture_replacement::reload();
|
||||
}
|
||||
|
||||
void aurora_reload_texture_replacements() { aurora::gfx::texture_replacement::reload(); }
|
||||
|
||||
+469
-310
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,6 @@
|
||||
namespace aurora::gfx::texture_replacement {
|
||||
void initialize() noexcept;
|
||||
void shutdown() noexcept;
|
||||
void reload() noexcept;
|
||||
void register_tlut(const GXTlutObj* obj, const void* data, GXTlutFmt format, uint16_t entries) noexcept;
|
||||
void load_tlut(const GXTlutObj* obj, uint32_t idx) noexcept;
|
||||
std::optional<TextureHandle> find_replacement(const GXTexObj_& obj) noexcept;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "command_processor.hpp"
|
||||
|
||||
#include "../gfx/common.hpp"
|
||||
#include "../gfx/texture_replacement.hpp"
|
||||
#include "dolphin/gx/GXAurora.h"
|
||||
#include "gx.hpp"
|
||||
#include "gx_fmt.hpp"
|
||||
|
||||
+16
-4
@@ -15,6 +15,7 @@
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <tracy/Tracy.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <cfloat>
|
||||
#include <mutex>
|
||||
@@ -79,6 +80,14 @@ struct TlutObjectCache {
|
||||
|
||||
absl::flat_hash_map<u32, CachedTextureEntry> s_textureObjectCaches;
|
||||
absl::flat_hash_map<u32, TlutObjectCache> s_tlutObjectCaches;
|
||||
std::atomic_bool s_staticTextureCacheClearPending = false;
|
||||
|
||||
void do_clear_static_texture_cache() noexcept {
|
||||
s_textureObjectCaches.clear();
|
||||
for (auto& [_, cache] : s_tlutObjectCaches) {
|
||||
cache.staticTextureUsers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
DynamicPaletteKey make_dynamic_palette_key(const GXTexObj_& obj, const GXState::CopyTextureRef& source) {
|
||||
return {
|
||||
@@ -148,6 +157,9 @@ gfx::TextureHandle get_tlut_texture(const GXTlutObj_& tlut) {
|
||||
|
||||
gfx::TextureHandle resolve_static_texture(const GXTexObj_& obj) {
|
||||
ZoneScoped;
|
||||
if (s_staticTextureCacheClearPending.exchange(false, std::memory_order_acq_rel)) {
|
||||
do_clear_static_texture_cache();
|
||||
}
|
||||
|
||||
if (obj.texObjId != 0) {
|
||||
if (const auto it = s_textureObjectCaches.find(obj.texObjId); it != s_textureObjectCaches.end()) {
|
||||
@@ -180,6 +192,9 @@ gfx::TextureHandle resolve_static_texture(const GXTexObj_& obj) {
|
||||
|
||||
gfx::TextureHandle resolve_static_palette_texture(const GXTexObj_& obj, const GXTlutObj_& tlut) {
|
||||
ZoneScoped;
|
||||
if (s_staticTextureCacheClearPending.exchange(false, std::memory_order_acq_rel)) {
|
||||
do_clear_static_texture_cache();
|
||||
}
|
||||
|
||||
if (obj.texObjId != 0) {
|
||||
if (const auto it = s_textureObjectCaches.find(obj.texObjId); it != s_textureObjectCaches.end()) {
|
||||
@@ -371,10 +386,7 @@ void clear_copy_texture_cache() noexcept {
|
||||
}
|
||||
|
||||
void clear_static_texture_cache() noexcept {
|
||||
s_textureObjectCaches.clear();
|
||||
for (auto& [_, cache] : s_tlutObjectCaches) {
|
||||
cache.staticTextureUsers.clear();
|
||||
}
|
||||
s_staticTextureCacheClearPending.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
void evict_copy_texture(const void* dest) noexcept {
|
||||
|
||||
Reference in New Issue
Block a user