mirror of
https://github.com/encounter/aurora.git
synced 2026-07-09 18:19:33 -07:00
Fix texture eviction & cleanup
This commit is contained in:
@@ -43,6 +43,10 @@ extern "C" {
|
||||
|
||||
#define GX_LOAD_AURORA_TLUT 0x0031
|
||||
|
||||
#define GX_LOAD_AURORA_DESTROY_TEXOBJ 0x0032
|
||||
|
||||
#define GX_LOAD_AURORA_DESTROY_TLUT 0x0033
|
||||
|
||||
|
||||
/*
|
||||
* Debug marker stuff
|
||||
|
||||
@@ -63,7 +63,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
#ifdef TARGET_PC
|
||||
u32 dummy[18];
|
||||
u32 dummy[16];
|
||||
#else
|
||||
u32 dummy[8];
|
||||
#endif
|
||||
@@ -71,7 +71,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
#ifdef TARGET_PC
|
||||
u32 dummy[12];
|
||||
u32 dummy[10];
|
||||
#else
|
||||
u32 dummy[3];
|
||||
#endif
|
||||
|
||||
+10
-31
@@ -1,44 +1,23 @@
|
||||
#include "gx.hpp"
|
||||
#include "__gx.h"
|
||||
#include "dolphin/gx/GXAurora.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void GXDestroyTexObj(GXTexObj* obj_) {
|
||||
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||
if (obj->texObjId == 0) {
|
||||
obj->texObjId = 1;
|
||||
} else {
|
||||
++obj->texObjId;
|
||||
if (obj->texObjId == 0) {
|
||||
obj->texObjId = 1;
|
||||
}
|
||||
}
|
||||
if (obj->texDataVersion == 0) {
|
||||
obj->texDataVersion = 1;
|
||||
} else {
|
||||
++obj->texDataVersion;
|
||||
if (obj->texDataVersion == 0) {
|
||||
obj->texDataVersion = 1;
|
||||
}
|
||||
if (obj->texObjId != 0) {
|
||||
GX_WRITE_AURORA(GX_LOAD_AURORA_DESTROY_TEXOBJ);
|
||||
GX_WRITE_U32(obj->texObjId);
|
||||
}
|
||||
obj->texObjId = 0;
|
||||
}
|
||||
|
||||
void GXDestroyTlutObj(GXTlutObj* obj_) {
|
||||
auto* obj = reinterpret_cast<GXTlutObj_*>(obj_);
|
||||
if (obj->tlutObjId == 0) {
|
||||
obj->tlutObjId = 1;
|
||||
} else {
|
||||
++obj->tlutObjId;
|
||||
if (obj->tlutObjId == 0) {
|
||||
obj->tlutObjId = 1;
|
||||
}
|
||||
}
|
||||
if (obj->tlutDataVersion == 0) {
|
||||
obj->tlutDataVersion = 1;
|
||||
} else {
|
||||
++obj->tlutDataVersion;
|
||||
if (obj->tlutDataVersion == 0) {
|
||||
obj->tlutDataVersion = 1;
|
||||
}
|
||||
if (obj->tlutObjId != 0) {
|
||||
GX_WRITE_AURORA(GX_LOAD_AURORA_DESTROY_TLUT);
|
||||
GX_WRITE_U32(obj->tlutObjId);
|
||||
}
|
||||
obj->tlutObjId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ GXFifoObj* GXInit(void* base, u32 size) {
|
||||
GXClearVtxDesc();
|
||||
GXInvalidateVtxCache();
|
||||
GXSetLineWidth(6, GX_TO_ZERO);
|
||||
// GXSetPointSize(6, GX_TO_ZERO);
|
||||
GXSetPointSize(6, GX_TO_ZERO);
|
||||
GXEnableTexOffsets(GX_TEXCOORD0, GX_DISABLE, GX_DISABLE);
|
||||
GXEnableTexOffsets(GX_TEXCOORD1, GX_DISABLE, GX_DISABLE);
|
||||
GXEnableTexOffsets(GX_TEXCOORD2, GX_DISABLE, GX_DISABLE);
|
||||
@@ -159,9 +159,9 @@ GXFifoObj* GXInit(void* base, u32 size) {
|
||||
GXLoadTexMtxImm(identity_mtx, GX_PTIDENTITY, GX_MTX3x4);
|
||||
|
||||
GXSetViewport(0.0f, 0.0f, rmode->fbWidth, rmode->xfbHeight, 0.0f, 1.0f);
|
||||
// GXSetCoPlanar(GX_DISABLE);
|
||||
GXSetCoPlanar(GX_DISABLE);
|
||||
GXSetCullMode(GX_CULL_BACK);
|
||||
// GXSetClipMode(GX_CLIP_ENABLE);
|
||||
GXSetClipMode(GX_CLIP_ENABLE);
|
||||
GXSetScissor(0, 0, rmode->fbWidth, rmode->efbHeight);
|
||||
// GXSetScissorBoxOffset(0, 0);
|
||||
|
||||
@@ -228,7 +228,7 @@ GXFifoObj* GXInit(void* base, u32 size) {
|
||||
GXSetIndTexCoordScale(GX_INDTEXSTAGE3, GX_ITS_1, GX_ITS_1);
|
||||
|
||||
GXSetFog(GX_FOG_NONE, 0.0f, 1.0f, 0.1f, 1.0f, black);
|
||||
// GXSetFogRangeAdj(GX_DISABLE, 0, nullptr);
|
||||
GXSetFogRangeAdj(GX_DISABLE, 0, nullptr);
|
||||
GXSetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
|
||||
GXSetColorUpdate(GX_ENABLE);
|
||||
GXSetAlphaUpdate(GX_ENABLE);
|
||||
@@ -237,8 +237,8 @@ GXFifoObj* GXInit(void* base, u32 size) {
|
||||
GXSetDither(GX_ENABLE);
|
||||
GXSetDstAlpha(GX_DISABLE, 0);
|
||||
GXSetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
|
||||
// GXSetFieldMask(GX_ENABLE, GX_ENABLE);
|
||||
// GXSetFieldMode(rmode->field_rendering, ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
|
||||
GXSetFieldMask(GX_ENABLE, GX_ENABLE);
|
||||
GXSetFieldMode(rmode->field_rendering, ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
|
||||
|
||||
GXSetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight);
|
||||
GXSetDispCopyDst(rmode->fbWidth, rmode->efbHeight);
|
||||
|
||||
@@ -24,30 +24,21 @@ u32 sNextTlutObjId = 1;
|
||||
|
||||
u32 next_tex_obj_id() {
|
||||
if (sNextTexObjId == 0) {
|
||||
++sNextTexObjId;
|
||||
FATAL("texObj ID overflow");
|
||||
}
|
||||
return sNextTexObjId++;
|
||||
}
|
||||
|
||||
u32 next_tlut_obj_id() {
|
||||
if (sNextTlutObjId == 0) {
|
||||
++sNextTlutObjId;
|
||||
FATAL("tlutObj ID overflow");
|
||||
}
|
||||
return sNextTlutObjId++;
|
||||
}
|
||||
|
||||
void configure_tex_image_ptr(GXTexObj_& obj, const void* data) {
|
||||
obj.data = data;
|
||||
obj.imagePtr64 = reinterpret_cast<u64>(data);
|
||||
SET_REG_FIELD(0, obj.image3, 21, 0, static_cast<u32>((obj.imagePtr64 >> 5) & 0x01FFFFFF));
|
||||
}
|
||||
|
||||
void update_tex_upload_size(GXTexObj_& obj) {
|
||||
obj.dataSize = aurora::gfx::texture_replacement::compute_texture_upload_size(obj);
|
||||
}
|
||||
|
||||
int __cntlzw(unsigned int val) {
|
||||
if (val == 0) return 32; // PowerPC returns 32 if the input is 0
|
||||
if (val == 0)
|
||||
return 32; // PowerPC returns 32 if the input is 0
|
||||
#ifdef _MSC_VER
|
||||
unsigned long idx;
|
||||
_BitScanReverse(&idx, val);
|
||||
@@ -83,17 +74,18 @@ void init_texobj_common(GXTexObj_& obj, const void* data, u16 width, u16 height,
|
||||
SET_REG_FIELD(0, obj.image0, 10, 10, static_cast<u32>(height - 1));
|
||||
SET_REG_FIELD(0, obj.image0, 4, 20, format & 0xF);
|
||||
|
||||
configure_tex_image_ptr(obj, data);
|
||||
update_tex_upload_size(obj);
|
||||
obj.data = data;
|
||||
obj.dataSize = aurora::gfx::texture_replacement::compute_texture_upload_size(obj);
|
||||
}
|
||||
|
||||
void emit_loaded_texobj_metadata(const GXTexObj_& obj, GXTexMapID id) {
|
||||
GX_WRITE_AURORA(GX_LOAD_AURORA_TEXOBJ);
|
||||
GX_WRITE_U8(static_cast<u8>(id));
|
||||
GX_WRITE_U64(obj.imagePtr64);
|
||||
GX_WRITE_U64(reinterpret_cast<u64>(obj.data));
|
||||
GX_WRITE_U32(obj.width());
|
||||
GX_WRITE_U32(obj.height());
|
||||
GX_WRITE_U32(obj.raw_format());
|
||||
GX_WRITE_U32(static_cast<u32>(obj.tlut));
|
||||
GX_WRITE_U8(static_cast<u8>(obj.has_mips()));
|
||||
GX_WRITE_U32(obj.texObjId);
|
||||
GX_WRITE_U32(obj.texDataVersion);
|
||||
@@ -102,7 +94,7 @@ void emit_loaded_texobj_metadata(const GXTexObj_& obj, GXTexMapID id) {
|
||||
void emit_loaded_tlut_metadata(const GXTlutObj_& obj, u32 idx) {
|
||||
GX_WRITE_AURORA(GX_LOAD_AURORA_TLUT);
|
||||
GX_WRITE_U8(static_cast<u8>(idx));
|
||||
GX_WRITE_U64(obj.tlutPtr64);
|
||||
GX_WRITE_U64(reinterpret_cast<u64>(obj.data));
|
||||
GX_WRITE_U32(static_cast<u32>(obj.format));
|
||||
GX_WRITE_U16(obj.numEntries);
|
||||
GX_WRITE_U32(obj.tlutObjId);
|
||||
@@ -147,9 +139,9 @@ void GXInitTexObjLOD(GXTexObj* obj_, GXTexFilter minFilt, GXTexFilter magFilt, f
|
||||
|
||||
void GXInitTexObjData(GXTexObj* obj_, const void* data) {
|
||||
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||
configure_tex_image_ptr(*obj, data);
|
||||
obj->data = data;
|
||||
obj->dataSize = aurora::gfx::texture_replacement::compute_texture_upload_size(*obj);
|
||||
++obj->texDataVersion;
|
||||
update_tex_upload_size(*obj);
|
||||
}
|
||||
|
||||
void GXInitTexObjWrapMode(GXTexObj* obj_, GXTexWrapMode wrapS, GXTexWrapMode wrapT) {
|
||||
@@ -281,12 +273,10 @@ void GXInitTlutObj(GXTlutObj* obj_, const void* data, GXTlutFmt format, u16 entr
|
||||
obj->data = data;
|
||||
obj->format = format;
|
||||
obj->numEntries = entries;
|
||||
obj->tlutPtr64 = reinterpret_cast<u64>(data);
|
||||
obj->tlutObjId = next_tlut_obj_id();
|
||||
obj->tlutDataVersion = 1;
|
||||
|
||||
SET_REG_FIELD(0, obj->tlut, 2, 10, format);
|
||||
SET_REG_FIELD(0, obj->loadTlut0, 21, 0, static_cast<u32>((obj->tlutPtr64 >> 5) & 0x01FFFFFF));
|
||||
SET_REG_FIELD(0, obj->loadTlut0, 8, 24, 0x64);
|
||||
aurora::gfx::texture_replacement::register_tlut(obj_, data, format, entries);
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include <dolphin/gx.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace aurora::gfx {
|
||||
@@ -10,7 +12,7 @@ struct TextureUpload {
|
||||
wgpu::Extent3D size;
|
||||
|
||||
TextureUpload(wgpu::TexelCopyBufferLayout layout, wgpu::TexelCopyTextureInfo tex, wgpu::Extent3D size) noexcept
|
||||
: layout(layout), tex(tex), size(size) {}
|
||||
: layout(layout), tex(std::move(tex)), size(size) {}
|
||||
};
|
||||
extern std::vector<TextureUpload> g_textureUploads;
|
||||
|
||||
@@ -58,10 +60,9 @@ struct GXTexObj_ {
|
||||
u32 mHeight = 0;
|
||||
u32 mFormat = aurora::gfx::InvalidTextureFormat;
|
||||
GXTlut tlut = GX_TLUT0;
|
||||
u8 flags = 0;
|
||||
u64 imagePtr64 = 0;
|
||||
u32 texObjId = 0;
|
||||
u32 texDataVersion = 0;
|
||||
u8 flags = 0;
|
||||
|
||||
static constexpr u32 get_bits(u32 reg, u32 size, u32 shift) noexcept { return (reg >> shift) & ((1u << size) - 1); }
|
||||
|
||||
@@ -91,10 +92,8 @@ struct GXTlutObj_ {
|
||||
u32 tlut = 0;
|
||||
u32 loadTlut0 = 0;
|
||||
u16 numEntries = 0;
|
||||
u16 _pad = 0;
|
||||
const void* data = nullptr;
|
||||
GXTlutFmt format = GX_TL_IA8;
|
||||
u64 tlutPtr64 = 0;
|
||||
u32 tlutObjId = 0;
|
||||
u32 tlutDataVersion = 0;
|
||||
};
|
||||
@@ -106,7 +105,7 @@ struct TextureBind {
|
||||
GXTexObj_ texObj;
|
||||
|
||||
TextureBind() noexcept = default;
|
||||
TextureBind(GXTexObj_ obj, TextureHandle handle) noexcept : ref(std::move(handle)), texObj(std::move(obj)) {}
|
||||
TextureBind(const GXTexObj_& obj, TextureHandle handle) noexcept : ref(std::move(handle)), texObj(obj) {}
|
||||
void reset() noexcept { ref.reset(); }
|
||||
[[nodiscard]] wgpu::SamplerDescriptor get_descriptor() const noexcept;
|
||||
operator bool() const noexcept { return ref.operator bool(); }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "command_processor.hpp"
|
||||
|
||||
#include "../gfx/common.hpp"
|
||||
#include "../gfx/texture_replacement.hpp"
|
||||
#include "gx.hpp"
|
||||
#include "gx_fmt.hpp"
|
||||
#include "pipeline.hpp"
|
||||
@@ -783,7 +784,7 @@ static void handle_bp(u32 value, bool bigEndian) {
|
||||
if (idx < MaxTluts) {
|
||||
auto& slot = g_gxState.loadedTluts[idx];
|
||||
slot.loadTlut0 = g_gxState.bpRegCache[0x64];
|
||||
slot.entries = static_cast<u16>(bp_get(value, 10, 10) + 1);
|
||||
slot.numEntries = static_cast<u16>(bp_get(value, 10, 10) + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1086,17 +1087,15 @@ static void handle_bp(u32 value, bool bigEndian) {
|
||||
case TexBpRegMapping::Kind::Image0:
|
||||
slot.image0 = value;
|
||||
break;
|
||||
case TexBpRegMapping::Kind::Image1:
|
||||
slot.image1 = value;
|
||||
break;
|
||||
case TexBpRegMapping::Kind::Image2:
|
||||
slot.image2 = value;
|
||||
break;
|
||||
case TexBpRegMapping::Kind::Image3:
|
||||
slot.image3 = value;
|
||||
break;
|
||||
case TexBpRegMapping::Kind::Tlut:
|
||||
slot.tlut = value;
|
||||
// TLUT region's TMEM offset
|
||||
break;
|
||||
case TexBpRegMapping::Kind::Image1:
|
||||
case TexBpRegMapping::Kind::Image2:
|
||||
// GXTexRegion regs
|
||||
break;
|
||||
}
|
||||
g_gxState.stateDirty = true;
|
||||
@@ -1669,26 +1668,32 @@ void handle_aurora(const u8* data, u32& pos, u32 size, bool bigEndian) {
|
||||
g_gxState.stateDirty = true;
|
||||
}
|
||||
} else if (subCmd == GX_LOAD_AURORA_TEXOBJ) {
|
||||
CHECK(pos + 30 <= size, "GX_LOAD_AURORA_TEXOBJ read overrun");
|
||||
CHECK(pos + 34 <= size, "GX_LOAD_AURORA_TEXOBJ read overrun");
|
||||
const auto texMapId = data[pos];
|
||||
pos += 1;
|
||||
CHECK(texMapId < MaxTextures, "invalid texture map id {}", texMapId);
|
||||
auto& slot = g_gxState.loadedTextures[texMapId];
|
||||
slot.imageData = reinterpret_cast<const void*>(read_u64(data + pos, bigEndian));
|
||||
slot.data = reinterpret_cast<const void*>(read_u64(data + pos, bigEndian));
|
||||
pos += 8;
|
||||
slot.width = read_u32(data + pos, bigEndian);
|
||||
slot.mWidth = read_u32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
slot.height = read_u32(data + pos, bigEndian);
|
||||
slot.mHeight = read_u32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
slot.format = static_cast<GXTexFmt>(read_u32(data + pos, bigEndian));
|
||||
slot.mFormat = static_cast<GXTexFmt>(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
slot.hasMips = data[pos] != 0;
|
||||
slot.tlut = static_cast<GXTlut>(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
if (data[pos] != 0) {
|
||||
slot.flags |= 1u;
|
||||
} else {
|
||||
slot.flags &= ~1u;
|
||||
}
|
||||
pos += 1;
|
||||
slot.texObjId = read_u32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
slot.texDataVersion = read_u32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
slot.valid = true;
|
||||
slot.dataSize = gfx::texture_replacement::compute_texture_upload_size(slot);
|
||||
g_gxState.stateDirty = true;
|
||||
} else if (subCmd == GX_LOAD_AURORA_TLUT) {
|
||||
CHECK(pos + 23 <= size, "GX_LOAD_AURORA_TLUT read overrun");
|
||||
@@ -1700,14 +1705,21 @@ void handle_aurora(const u8* data, u32& pos, u32 size, bool bigEndian) {
|
||||
pos += 8;
|
||||
slot.format = static_cast<GXTlutFmt>(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
slot.entries = read_u16(data + pos, bigEndian);
|
||||
slot.numEntries = read_u16(data + pos, bigEndian);
|
||||
pos += 2;
|
||||
slot.tlutObjId = read_u32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
slot.tlutDataVersion = read_u32(data + pos, bigEndian);
|
||||
pos += 4;
|
||||
slot.valid = true;
|
||||
g_gxState.stateDirty = true;
|
||||
} else if (subCmd == GX_LOAD_AURORA_DESTROY_TEXOBJ) {
|
||||
CHECK(pos + 4 <= size, "GX_LOAD_AURORA_DESTROY_TEXOBJ read overrun");
|
||||
evict_texture_object(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
} else if (subCmd == GX_LOAD_AURORA_DESTROY_TLUT) {
|
||||
CHECK(pos + 4 <= size, "GX_LOAD_AURORA_DESTROY_TLUT read overrun");
|
||||
evict_tlut_object(read_u32(data + pos, bigEndian));
|
||||
pos += 4;
|
||||
} else if (subCmd == GX_LOAD_AURORA_DEBUG_GROUP_PUSH) {
|
||||
auto label = read_string(data, pos, size, bigEndian);
|
||||
gfx::push_debug_group(std::move(label));
|
||||
|
||||
+135
-183
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "pipeline.hpp"
|
||||
#include "../webgpu/gpu.hpp"
|
||||
#include "../window.hpp"
|
||||
#include "../internal.hpp"
|
||||
#include "../gfx/common.hpp"
|
||||
#include "../gfx/tex_palette_conv.hpp"
|
||||
@@ -14,7 +13,6 @@
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <cfloat>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
@@ -33,55 +31,8 @@ static wgpu::Texture sEmptyTexture;
|
||||
static wgpu::TextureView sEmptyTextureView;
|
||||
|
||||
namespace {
|
||||
struct StaticTextureKey {
|
||||
u32 texObjId = 0;
|
||||
u32 texDataVersion = 0;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 format = 0;
|
||||
u32 mipCount = 0;
|
||||
|
||||
bool operator==(const StaticTextureKey& rhs) const = default;
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, const StaticTextureKey& key) {
|
||||
return H::combine(std::move(h), key.texObjId, key.texDataVersion, key.width, key.height, key.format, key.mipCount);
|
||||
}
|
||||
};
|
||||
|
||||
struct StaticPaletteKey {
|
||||
u32 texObjId = 0;
|
||||
u32 texDataVersion = 0;
|
||||
u32 tlutObjId = 0;
|
||||
u32 tlutDataVersion = 0;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 format = 0;
|
||||
u32 mipCount = 0;
|
||||
|
||||
bool operator==(const StaticPaletteKey& rhs) const = default;
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, const StaticPaletteKey& key) {
|
||||
return H::combine(std::move(h), key.texObjId, key.texDataVersion, key.tlutObjId, key.tlutDataVersion, key.width,
|
||||
key.height, key.format, key.mipCount);
|
||||
}
|
||||
};
|
||||
|
||||
struct TlutTextureKey {
|
||||
u32 tlutObjId = 0;
|
||||
u32 tlutDataVersion = 0;
|
||||
u32 format = 0;
|
||||
u16 entries = 0;
|
||||
|
||||
bool operator==(const TlutTextureKey& rhs) const = default;
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, const TlutTextureKey& key) {
|
||||
return H::combine(std::move(h), key.tlutObjId, key.tlutDataVersion, key.format, key.entries);
|
||||
}
|
||||
};
|
||||
|
||||
struct DynamicPaletteKey {
|
||||
const void* sourceIdentity = nullptr;
|
||||
u32 tlutObjId = 0;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 format = 0;
|
||||
@@ -89,7 +40,7 @@ struct DynamicPaletteKey {
|
||||
bool operator==(const DynamicPaletteKey& rhs) const = default;
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, const DynamicPaletteKey& key) {
|
||||
return H::combine(std::move(h), key.sourceIdentity, key.tlutObjId, key.width, key.height, key.format);
|
||||
return H::combine(std::move(h), key.sourceIdentity, key.width, key.height, key.format);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,23 +50,68 @@ struct DynamicPaletteEntry {
|
||||
u32 tlutDataVersion = 0;
|
||||
};
|
||||
|
||||
struct MissingPaletteKey {
|
||||
u32 texObjId = 0;
|
||||
struct CachedTextureEntry {
|
||||
gfx::TextureHandle handle;
|
||||
u32 texDataVersion = 0;
|
||||
u32 tlut = 0;
|
||||
|
||||
bool operator==(const MissingPaletteKey& rhs) const = default;
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, const MissingPaletteKey& key) {
|
||||
return H::combine(std::move(h), key.texObjId, key.texDataVersion, key.tlut);
|
||||
}
|
||||
u32 tlutObjId = 0;
|
||||
u32 tlutDataVersion = 0;
|
||||
};
|
||||
|
||||
absl::flat_hash_map<StaticTextureKey, gfx::TextureHandle> s_staticTextureCache;
|
||||
absl::flat_hash_map<StaticPaletteKey, gfx::TextureHandle> s_staticPaletteCache;
|
||||
absl::flat_hash_map<TlutTextureKey, gfx::TextureHandle> s_tlutTextureCache;
|
||||
absl::flat_hash_map<DynamicPaletteKey, DynamicPaletteEntry> s_dynamicPaletteCache;
|
||||
absl::flat_hash_set<MissingPaletteKey> s_missingPaletteWarnings;
|
||||
struct CachedTlutTextureEntry {
|
||||
gfx::TextureHandle handle;
|
||||
u32 tlutDataVersion = 0;
|
||||
};
|
||||
|
||||
struct TlutObjectCache {
|
||||
CachedTlutTextureEntry tlutTexture;
|
||||
absl::flat_hash_map<DynamicPaletteKey, DynamicPaletteEntry> dynamicPaletteTextures;
|
||||
absl::flat_hash_set<u32> staticTextureUsers;
|
||||
};
|
||||
|
||||
absl::flat_hash_map<u32, CachedTextureEntry> s_textureObjectCaches;
|
||||
absl::flat_hash_map<u32, TlutObjectCache> s_tlutObjectCaches;
|
||||
|
||||
DynamicPaletteKey make_dynamic_palette_key(const GXTexObj_& obj, const GXState::CopyTextureRef& source) {
|
||||
return {
|
||||
.sourceIdentity = source.handle.get(),
|
||||
.width = obj.width(),
|
||||
.height = obj.height(),
|
||||
.format = obj.format(),
|
||||
};
|
||||
}
|
||||
|
||||
void clear_texture_dependency(u32 texObjId, u32 tlutObjId) {
|
||||
if (texObjId == 0 || tlutObjId == 0) {
|
||||
return;
|
||||
}
|
||||
if (auto it = s_tlutObjectCaches.find(tlutObjId); it != s_tlutObjectCaches.end()) {
|
||||
it->second.staticTextureUsers.erase(texObjId);
|
||||
if (!it->second.tlutTexture.handle && it->second.dynamicPaletteTextures.empty() &&
|
||||
it->second.staticTextureUsers.empty()) {
|
||||
s_tlutObjectCaches.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void store_cached_texture(const GXTexObj_& obj, gfx::TextureHandle handle, u32 tlutObjId = 0, u32 tlutDataVersion = 0) {
|
||||
if (obj.texObjId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& entry = s_textureObjectCaches[obj.texObjId];
|
||||
if (entry.tlutObjId != tlutObjId) {
|
||||
clear_texture_dependency(obj.texObjId, entry.tlutObjId);
|
||||
}
|
||||
|
||||
entry.handle = std::move(handle);
|
||||
entry.texDataVersion = obj.texDataVersion;
|
||||
entry.tlutObjId = tlutObjId;
|
||||
entry.tlutDataVersion = tlutDataVersion;
|
||||
|
||||
if (tlutObjId != 0) {
|
||||
s_tlutObjectCaches[tlutObjId].staticTextureUsers.insert(obj.texObjId);
|
||||
}
|
||||
}
|
||||
|
||||
u32 mip_count_for(const GXTexObj_& obj) {
|
||||
if (!obj.has_mips()) {
|
||||
@@ -124,49 +120,27 @@ u32 mip_count_for(const GXTexObj_& obj) {
|
||||
return std::max<u32>(static_cast<u32>(obj.max_lod()) + 1, 1u);
|
||||
}
|
||||
|
||||
GXTexObj_ materialize_tex_obj(const GXState::LoadedTextureSlot& slot) {
|
||||
GXTexObj_ obj{};
|
||||
obj.mode0 = slot.mode0;
|
||||
obj.mode1 = slot.mode1;
|
||||
obj.image0 = slot.image0;
|
||||
obj.image3 = slot.image3;
|
||||
obj.data = slot.imageData;
|
||||
obj.imagePtr64 = reinterpret_cast<u64>(slot.imageData);
|
||||
obj.mWidth = slot.width;
|
||||
obj.mHeight = slot.height;
|
||||
obj.tlut = static_cast<GXTlut>(GXTexObj_::get_bits(slot.tlut, 10, 0));
|
||||
if (slot.hasMips) {
|
||||
obj.flags |= 1;
|
||||
}
|
||||
if (slot.format != obj.raw_format()) {
|
||||
obj.mFormat = slot.format;
|
||||
}
|
||||
obj.texObjId = slot.texObjId;
|
||||
obj.texDataVersion = slot.texDataVersion;
|
||||
obj.dataSize = gfx::texture_replacement::compute_texture_upload_size(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
gfx::TextureHandle get_tlut_texture(const GXState::LoadedTlutSlot& tlut) {
|
||||
const TlutTextureKey key{
|
||||
.tlutObjId = tlut.tlutObjId,
|
||||
.tlutDataVersion = tlut.tlutDataVersion,
|
||||
.format = static_cast<u32>(tlut.format),
|
||||
.entries = tlut.entries,
|
||||
};
|
||||
if (const auto it = s_tlutTextureCache.find(key); it != s_tlutTextureCache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const auto texFmt = gfx::tlut_texture_format(tlut.format);
|
||||
if (texFmt == gfx::InvalidTextureFormat) {
|
||||
FATAL("invalid tlut format {}", static_cast<int>(tlut.format));
|
||||
gfx::TextureHandle get_tlut_texture(const GXTlutObj_& tlut) {
|
||||
if (tlut.tlutObjId != 0) {
|
||||
auto& cache = s_tlutObjectCaches[tlut.tlutObjId];
|
||||
if (cache.tlutTexture.handle && cache.tlutTexture.tlutDataVersion == tlut.tlutDataVersion) {
|
||||
return cache.tlutTexture.handle;
|
||||
}
|
||||
cache.dynamicPaletteTextures.clear();
|
||||
for (const u32 texObjId : cache.staticTextureUsers) {
|
||||
s_textureObjectCaches.erase(texObjId);
|
||||
}
|
||||
cache.staticTextureUsers.clear();
|
||||
}
|
||||
|
||||
const auto handle = gfx::new_static_texture_2d(
|
||||
tlut.entries, 1, 1, texFmt, {static_cast<const u8*>(tlut.data), static_cast<size_t>(tlut.entries) * sizeof(u16)},
|
||||
true, "Loaded TLUT");
|
||||
s_tlutTextureCache.emplace(key, handle);
|
||||
tlut.numEntries, 1, 1, gfx::tlut_texture_format(tlut.format),
|
||||
{static_cast<const u8*>(tlut.data), static_cast<size_t>(tlut.numEntries) * sizeof(u16)}, true, "Loaded TLUT");
|
||||
if (tlut.tlutObjId != 0) {
|
||||
auto& cache = s_tlutObjectCaches[tlut.tlutObjId];
|
||||
cache.tlutTexture.handle = handle;
|
||||
cache.tlutTexture.tlutDataVersion = tlut.tlutDataVersion;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -179,84 +153,58 @@ gfx::TextureHandle resolve_static_texture(const GXTexObj_& obj) {
|
||||
return *replacement;
|
||||
}
|
||||
|
||||
const StaticTextureKey key{
|
||||
.texObjId = obj.texObjId,
|
||||
.texDataVersion = obj.texDataVersion,
|
||||
.width = obj.width(),
|
||||
.height = obj.height(),
|
||||
.format = obj.format(),
|
||||
.mipCount = mip_count_for(obj),
|
||||
};
|
||||
if (const auto it = s_staticTextureCache.find(key); it != s_staticTextureCache.end()) {
|
||||
return it->second;
|
||||
if (obj.texObjId != 0) {
|
||||
if (const auto it = s_textureObjectCaches.find(obj.texObjId); it != s_textureObjectCaches.end()) {
|
||||
const auto& entry = it->second;
|
||||
if (entry.handle && entry.texDataVersion == obj.texDataVersion && entry.tlutObjId == 0) {
|
||||
return entry.handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto handle =
|
||||
gfx::new_static_texture_2d(obj.width(), obj.height(), mip_count_for(obj), obj.format(),
|
||||
{static_cast<const u8*>(obj.data), obj.dataSize}, false, "GX Static Texture");
|
||||
s_staticTextureCache.emplace(key, handle);
|
||||
store_cached_texture(obj, handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
gfx::TextureHandle resolve_static_palette_texture(const GXTexObj_& obj, const GXState::LoadedTlutSlot& tlut) {
|
||||
gfx::TextureHandle resolve_static_palette_texture(const GXTexObj_& obj, const GXTlutObj_& tlut) {
|
||||
if (const auto replacement = try_resolve_replacement_handle(obj); replacement.has_value()) {
|
||||
return *replacement;
|
||||
}
|
||||
|
||||
const StaticPaletteKey key{
|
||||
.texObjId = obj.texObjId,
|
||||
.texDataVersion = obj.texDataVersion,
|
||||
.tlutObjId = tlut.tlutObjId,
|
||||
.tlutDataVersion = tlut.tlutDataVersion,
|
||||
.width = obj.width(),
|
||||
.height = obj.height(),
|
||||
.format = obj.format(),
|
||||
.mipCount = mip_count_for(obj),
|
||||
};
|
||||
if (const auto it = s_staticPaletteCache.find(key); it != s_staticPaletteCache.end()) {
|
||||
return it->second;
|
||||
if (obj.texObjId != 0) {
|
||||
if (const auto it = s_textureObjectCaches.find(obj.texObjId); it != s_textureObjectCaches.end()) {
|
||||
const auto& entry = it->second;
|
||||
if (entry.handle && entry.texDataVersion == obj.texDataVersion && entry.tlutObjId == tlut.tlutObjId &&
|
||||
entry.tlutDataVersion == tlut.tlutDataVersion) {
|
||||
return entry.handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto decoded = gfx::convert_texture_palette(
|
||||
obj.format(), obj.width(), obj.height(), mip_count_for(obj), {static_cast<const u8*>(obj.data), obj.dataSize},
|
||||
tlut.format, tlut.entries, {static_cast<const u8*>(tlut.data), static_cast<size_t>(tlut.entries) * 2});
|
||||
tlut.format, tlut.numEntries, {static_cast<const u8*>(tlut.data), static_cast<size_t>(tlut.numEntries) * 2});
|
||||
if (decoded.empty()) {
|
||||
return {};
|
||||
}
|
||||
const auto handle = gfx::new_static_texture_2d(obj.width(), obj.height(), mip_count_for(obj), GX_TF_RGBA8_PC,
|
||||
{decoded.data(), decoded.size()}, false, "GX Static Palette Texture");
|
||||
s_staticPaletteCache.emplace(key, handle);
|
||||
store_cached_texture(obj, handle, tlut.tlutObjId, tlut.tlutDataVersion);
|
||||
return handle;
|
||||
}
|
||||
|
||||
void warn_missing_palette_state(const GXTexObj_& obj, const char* reason) {
|
||||
const MissingPaletteKey key{
|
||||
.texObjId = obj.texObjId,
|
||||
.texDataVersion = obj.texDataVersion,
|
||||
.tlut = static_cast<u32>(obj.tlut),
|
||||
};
|
||||
if (s_missingPaletteWarnings.insert(key).second) {
|
||||
Log.warn("palette texture {} (fmt {}, tlut {}) unresolved at draw time: {}", obj.texObjId, obj.format(),
|
||||
static_cast<u32>(obj.tlut), reason);
|
||||
}
|
||||
}
|
||||
|
||||
gfx::TextureHandle resolve_dynamic_palette_texture(const GXTexObj_& obj, const GXState::CopyTextureRef& source,
|
||||
const GXState::LoadedTlutSlot& tlut) {
|
||||
const DynamicPaletteKey key{
|
||||
.sourceIdentity = source.handle.get(),
|
||||
.tlutObjId = tlut.tlutObjId,
|
||||
.width = obj.width(),
|
||||
.height = obj.height(),
|
||||
.format = obj.format(),
|
||||
};
|
||||
|
||||
auto& entry = s_dynamicPaletteCache[key];
|
||||
const GXTlutObj_& tlut) {
|
||||
const auto tlutHandle = get_tlut_texture(tlut);
|
||||
auto& tlutCache = s_tlutObjectCaches[tlut.tlutObjId];
|
||||
auto& entry = tlutCache.dynamicPaletteTextures[make_dynamic_palette_key(obj, source)];
|
||||
if (!entry.handle) {
|
||||
entry.handle = gfx::new_conv_texture(obj.width(), obj.height(), GX_TF_RGBA8, "GX Dynamic Palette Texture");
|
||||
}
|
||||
if (entry.sourceRevision != source.revision || entry.tlutDataVersion != tlut.tlutDataVersion) {
|
||||
const auto tlutHandle = get_tlut_texture(tlut);
|
||||
gfx::queue_palette_conv({
|
||||
.variant = obj.format() == GX_TF_C4 ? gfx::tex_palette_conv::Variant::FromFloat4
|
||||
: gfx::tex_palette_conv::Variant::FromFloat8,
|
||||
@@ -283,10 +231,28 @@ u32 resolved_format_for_handle(const gfx::TextureHandle& handle) {
|
||||
|
||||
const gfx::TextureBind& get_texture(GXTexMapID id) noexcept { return g_gxState.textures[static_cast<size_t>(id)]; }
|
||||
|
||||
void evict_texture_object(u32 texObjId) noexcept {
|
||||
if (const auto it = s_textureObjectCaches.find(texObjId); it != s_textureObjectCaches.end()) {
|
||||
clear_texture_dependency(texObjId, it->second.tlutObjId);
|
||||
s_textureObjectCaches.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void evict_tlut_object(u32 tlutObjId) noexcept {
|
||||
if (const auto it = s_tlutObjectCaches.find(tlutObjId); it != s_tlutObjectCaches.end()) {
|
||||
for (const u32 texObjId : it->second.staticTextureUsers) {
|
||||
s_textureObjectCaches.erase(texObjId);
|
||||
}
|
||||
s_tlutObjectCaches.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void clear_copy_texture_cache() noexcept {
|
||||
g_gxState.copyTextures.clear();
|
||||
g_gxState.copyTextureCache.clear();
|
||||
s_dynamicPaletteCache.clear();
|
||||
for (auto& [_, cache] : s_tlutObjectCaches) {
|
||||
cache.dynamicPaletteTextures.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void resolve_sampled_textures(const ShaderInfo& info) noexcept {
|
||||
@@ -295,45 +261,34 @@ void resolve_sampled_textures(const ShaderInfo& info) noexcept {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& slot = g_gxState.loadedTextures[i];
|
||||
CHECK(slot.valid, "sampled texture {} has not been loaded", i);
|
||||
|
||||
GXTexObj_ obj = materialize_tex_obj(slot);
|
||||
GXTexObj_ obj = g_gxState.loadedTextures[i];
|
||||
gfx::TextureHandle handle;
|
||||
const auto copyIt = g_gxState.copyTextures.find(obj.data);
|
||||
const GXState::CopyTextureRef* copyRef = copyIt != g_gxState.copyTextures.end() ? ©It->second : nullptr;
|
||||
if (obj.data == nullptr && copyRef == nullptr) {
|
||||
CHECK(false, "sampled texture {} has no image data metadata", i);
|
||||
} else if (is_palette_format(obj.format())) {
|
||||
if (is_palette_format(obj.format())) {
|
||||
const auto tlutIdx = static_cast<size_t>(obj.tlut);
|
||||
if (tlutIdx >= g_gxState.loadedTluts.size()) {
|
||||
warn_missing_palette_state(obj, "invalid TLUT slot");
|
||||
} else {
|
||||
if (tlutIdx < g_gxState.loadedTluts.size()) {
|
||||
const auto& tlut = g_gxState.loadedTluts[tlutIdx];
|
||||
if (!tlut.valid || tlut.data == nullptr) {
|
||||
warn_missing_palette_state(obj, "TLUT not loaded");
|
||||
} else if (copyRef != nullptr) {
|
||||
handle = resolve_dynamic_palette_texture(obj, *copyRef, tlut);
|
||||
} else {
|
||||
handle = resolve_static_palette_texture(obj, tlut);
|
||||
if (!handle) {
|
||||
warn_missing_palette_state(obj, "palette decode unsupported or failed");
|
||||
if (tlut.data != nullptr) {
|
||||
if (copyRef != nullptr) {
|
||||
handle = resolve_dynamic_palette_texture(obj, *copyRef, tlut);
|
||||
} else {
|
||||
handle = resolve_static_palette_texture(obj, tlut);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (copyRef != nullptr) {
|
||||
handle = copyRef->handle;
|
||||
} else {
|
||||
} else if (obj.data != nullptr) {
|
||||
handle = resolve_static_texture(obj);
|
||||
}
|
||||
|
||||
GXTexObj_ boundObj = obj;
|
||||
if (handle) {
|
||||
boundObj.mWidth = handle->size.width;
|
||||
boundObj.mHeight = handle->size.height;
|
||||
obj.mWidth = handle->size.width;
|
||||
obj.mHeight = handle->size.height;
|
||||
}
|
||||
boundObj.mFormat = resolved_format_for_handle(handle);
|
||||
g_gxState.textures[i] = gfx::TextureBind(boundObj, handle);
|
||||
obj.mFormat = resolved_format_for_handle(handle);
|
||||
g_gxState.textures[i] = gfx::TextureBind{obj, std::move(handle)};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -946,11 +901,8 @@ void shutdown() noexcept {
|
||||
std::lock_guard lock{g_gxCachedShadersMutex};
|
||||
g_gxCachedShaders.clear();
|
||||
}
|
||||
s_staticTextureCache.clear();
|
||||
s_staticPaletteCache.clear();
|
||||
s_tlutTextureCache.clear();
|
||||
s_dynamicPaletteCache.clear();
|
||||
s_missingPaletteWarnings.clear();
|
||||
s_textureObjectCaches.clear();
|
||||
s_tlutObjectCaches.clear();
|
||||
g_gxState.loadedTextures.fill({});
|
||||
g_gxState.loadedTluts.fill({});
|
||||
clear_copy_texture_cache();
|
||||
|
||||
+5
-32
@@ -280,33 +280,6 @@ inline bool operator==(const AttrArray& lhs, const AttrArray& rhs) {
|
||||
inline bool operator!=(const AttrArray& lhs, const AttrArray& rhs) { return !(lhs == rhs); }
|
||||
|
||||
struct GXState {
|
||||
struct LoadedTextureSlot {
|
||||
u32 mode0 = 0;
|
||||
u32 mode1 = 0;
|
||||
u32 image0 = 0;
|
||||
u32 image1 = 0;
|
||||
u32 image2 = 0;
|
||||
u32 image3 = 0;
|
||||
u32 tlut = 0;
|
||||
const void* imageData = nullptr;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
GXTexFmt format = GX_TF_I4;
|
||||
GXBool hasMips = false;
|
||||
u32 texObjId = 0;
|
||||
u32 texDataVersion = 0;
|
||||
bool valid = false;
|
||||
};
|
||||
struct LoadedTlutSlot {
|
||||
u32 tlut = 0;
|
||||
u32 loadTlut0 = 0;
|
||||
const void* data = nullptr;
|
||||
GXTlutFmt format = GX_TL_IA8;
|
||||
u16 entries = 0;
|
||||
u32 tlutObjId = 0;
|
||||
u32 tlutDataVersion = 0;
|
||||
bool valid = false;
|
||||
};
|
||||
struct CopyTextureRef {
|
||||
gfx::TextureHandle handle;
|
||||
u32 revision = 0;
|
||||
@@ -343,8 +316,8 @@ struct GXState {
|
||||
std::array<Light, GX::MaxLights> lights;
|
||||
std::array<TevStage, MaxTevStages> tevStages;
|
||||
std::array<gfx::TextureBind, MaxTextures> textures;
|
||||
std::array<LoadedTextureSlot, MaxTextures> loadedTextures;
|
||||
std::array<LoadedTlutSlot, MaxTluts> loadedTluts;
|
||||
std::array<GXTexObj_, MaxTextures> loadedTextures;
|
||||
std::array<GXTlutObj_, MaxTluts> loadedTluts;
|
||||
std::array<Mat3x4<float>, MaxTexMtx> texMtxs;
|
||||
std::array<Mat3x4<float>, MaxPTTexMtx> ptTexMtxs;
|
||||
std::array<TcgConfig, MaxTexCoord> tcgs;
|
||||
@@ -398,9 +371,7 @@ struct GXState {
|
||||
return regs;
|
||||
}();
|
||||
|
||||
void clearVtxSizeCache() {
|
||||
lastVtxFmt = GX_MAX_VTXFMT;
|
||||
}
|
||||
void clearVtxSizeCache() { lastVtxFmt = GX_MAX_VTXFMT; }
|
||||
};
|
||||
extern GXState g_gxState;
|
||||
struct ShaderInfo;
|
||||
@@ -408,6 +379,8 @@ struct ShaderInfo;
|
||||
void initialize() noexcept;
|
||||
void shutdown() noexcept;
|
||||
void clear_copy_texture_cache() noexcept;
|
||||
void evict_texture_object(u32 texObjId) noexcept;
|
||||
void evict_tlut_object(u32 tlutObjId) noexcept;
|
||||
const gfx::TextureBind& get_texture(GXTexMapID id) noexcept;
|
||||
void resolve_sampled_textures(const ShaderInfo& info) noexcept;
|
||||
|
||||
|
||||
+39
-16
@@ -1269,17 +1269,14 @@ TEST_F(GXFifoTest, LoadTexObj_EncodesSdkBpBurstAndAuroraMetadata) {
|
||||
decode_fifo(bytes);
|
||||
|
||||
const auto& slot = gxState().loadedTextures[GX_TEXMAP2];
|
||||
EXPECT_TRUE(slot.valid);
|
||||
EXPECT_EQ(slot.imageData, image);
|
||||
EXPECT_EQ(slot.width, 8u);
|
||||
EXPECT_EQ(slot.height, 8u);
|
||||
EXPECT_EQ(slot.format, GX_TF_RGB5A3);
|
||||
EXPECT_FALSE(slot.hasMips);
|
||||
EXPECT_EQ(slot.data, image);
|
||||
EXPECT_EQ(slot.width(), 8u);
|
||||
EXPECT_EQ(slot.height(), 8u);
|
||||
EXPECT_EQ(slot.format(), GX_TF_RGB5A3);
|
||||
EXPECT_FALSE(slot.has_mips());
|
||||
EXPECT_EQ(slot.mode0 >> 24, 0x82u);
|
||||
EXPECT_EQ(slot.mode1 >> 24, 0x86u);
|
||||
EXPECT_EQ(slot.image0 >> 24, 0x8Au);
|
||||
EXPECT_EQ(slot.image1 >> 24, 0x8Eu);
|
||||
EXPECT_EQ(slot.image2 >> 24, 0x92u);
|
||||
EXPECT_EQ(slot.image3 >> 24, 0x96u);
|
||||
EXPECT_NE(slot.texObjId, 0u);
|
||||
EXPECT_EQ(slot.texDataVersion, 1u);
|
||||
@@ -1312,22 +1309,48 @@ TEST_F(GXFifoTest, LoadTexObjCiAndTlut_PopulatesTextureAndTlutSlots) {
|
||||
decode_fifo(bytes);
|
||||
|
||||
const auto& texSlot = gxState().loadedTextures[GX_TEXMAP1];
|
||||
EXPECT_TRUE(texSlot.valid);
|
||||
EXPECT_EQ(texSlot.imageData, image);
|
||||
EXPECT_EQ(texSlot.width, 8u);
|
||||
EXPECT_EQ(texSlot.height, 8u);
|
||||
EXPECT_EQ(texSlot.format, GX_TF_C4);
|
||||
EXPECT_EQ(texSlot.tlut & 0x3FFu, GX_TLUT3);
|
||||
EXPECT_EQ(texSlot.data, image);
|
||||
EXPECT_EQ(texSlot.width(), 8u);
|
||||
EXPECT_EQ(texSlot.height(), 8u);
|
||||
EXPECT_EQ(texSlot.format(), GX_TF_C4);
|
||||
EXPECT_EQ(texSlot.tlut, GX_TLUT3);
|
||||
|
||||
const auto& tlutSlot = gxState().loadedTluts[GX_TLUT3];
|
||||
EXPECT_TRUE(tlutSlot.valid);
|
||||
EXPECT_EQ(tlutSlot.data, palette);
|
||||
EXPECT_EQ(tlutSlot.format, GX_TL_RGB565);
|
||||
EXPECT_EQ(tlutSlot.entries, 16u);
|
||||
EXPECT_EQ(tlutSlot.numEntries, 16u);
|
||||
EXPECT_NE(tlutSlot.tlutObjId, 0u);
|
||||
EXPECT_EQ(tlutSlot.tlutDataVersion, 1u);
|
||||
}
|
||||
|
||||
TEST_F(GXFifoTest, DestroyTexObj_EmitsAuroraDestroyCommandAndClearsIdentity) {
|
||||
alignas(32) u8 image[64]{};
|
||||
GXTexObj obj{};
|
||||
GXInitTexObj(&obj, image, 8, 8, GX_TF_RGB5A3, GX_REPEAT, GX_REPEAT, GX_FALSE);
|
||||
GXDestroyTexObj(&obj);
|
||||
auto bytes = capture_fifo();
|
||||
|
||||
EXPECT_TRUE(has_aurora_cmd(bytes, GX_LOAD_AURORA_DESTROY_TEXOBJ));
|
||||
EXPECT_EQ(reinterpret_cast<const GXTexObj_*>(&obj)->texObjId, 0u);
|
||||
|
||||
reset_gx_state();
|
||||
decode_fifo(bytes);
|
||||
}
|
||||
|
||||
TEST_F(GXFifoTest, DestroyTlutObj_EmitsAuroraDestroyCommandAndClearsIdentity) {
|
||||
alignas(32) u16 palette[16]{};
|
||||
GXTlutObj obj{};
|
||||
GXInitTlutObj(&obj, palette, GX_TL_RGB565, 16);
|
||||
GXDestroyTlutObj(&obj);
|
||||
auto bytes = capture_fifo();
|
||||
|
||||
EXPECT_TRUE(has_aurora_cmd(bytes, GX_LOAD_AURORA_DESTROY_TLUT));
|
||||
EXPECT_EQ(reinterpret_cast<const GXTlutObj_*>(&obj)->tlutObjId, 0u);
|
||||
|
||||
reset_gx_state();
|
||||
decode_fifo(bytes);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BP genMode (requires __GXSetDirtyState() flush)
|
||||
// ============================================================================
|
||||
|
||||
@@ -60,6 +60,8 @@ std::vector<TextureUpload> g_textureUploads;
|
||||
// --- get_texture ---
|
||||
namespace aurora::gx {
|
||||
const gfx::TextureBind& get_texture(GXTexMapID id) noexcept { return g_gxState.textures[id]; }
|
||||
void evict_texture_object(u32) noexcept {}
|
||||
void evict_tlut_object(u32) noexcept {}
|
||||
void shutdown() noexcept {}
|
||||
} // namespace aurora::gx
|
||||
|
||||
|
||||
Reference in New Issue
Block a user