diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props
index 0c9b412a00..89632c9989 100644
--- a/Source/Core/DolphinLib.props
+++ b/Source/Core/DolphinLib.props
@@ -642,6 +642,7 @@
+
@@ -1191,6 +1192,7 @@
+
diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt
index 2470c5ee4d..d9fe3ff1f9 100644
--- a/Source/Core/VideoCommon/CMakeLists.txt
+++ b/Source/Core/VideoCommon/CMakeLists.txt
@@ -88,6 +88,8 @@ add_library(videocommon
TextureDecoder.h
TextureDecoder_Common.cpp
TextureDecoder_Util.h
+ TextureInfo.cpp
+ TextureInfo.h
UberShaderCommon.cpp
UberShaderCommon.h
UberShaderPixel.cpp
diff --git a/Source/Core/VideoCommon/HiresTextures.cpp b/Source/Core/VideoCommon/HiresTextures.cpp
index d55eadf022..96c9ccbf10 100644
--- a/Source/Core/VideoCommon/HiresTextures.cpp
+++ b/Source/Core/VideoCommon/HiresTextures.cpp
@@ -214,75 +214,24 @@ void HiresTexture::Prefetch()
10000);
}
-std::string HiresTexture::GenBaseName(const u8* texture, size_t texture_size, const u8* tlut,
- size_t tlut_size, u32 width, u32 height, TextureFormat format,
- bool has_mipmaps, bool dump)
+std::string HiresTexture::GenBaseName(TextureInfo& texture_info, bool dump)
{
if (!dump && s_textureMap.empty())
return "";
- // checking for min/max on paletted textures
- u32 min = 0xffff;
- u32 max = 0;
- switch (tlut_size)
- {
- case 0:
- break;
- case 16 * 2:
- for (size_t i = 0; i < texture_size; i++)
- {
- const u32 low_nibble = texture[i] & 0xf;
- const u32 high_nibble = texture[i] >> 4;
-
- min = std::min({min, low_nibble, high_nibble});
- max = std::max({max, low_nibble, high_nibble});
- }
- break;
- case 256 * 2:
- {
- for (size_t i = 0; i < texture_size; i++)
- {
- const u32 texture_byte = texture[i];
-
- min = std::min(min, texture_byte);
- max = std::max(max, texture_byte);
- }
- break;
- }
- case 16384 * 2:
- for (size_t i = 0; i < texture_size; i += sizeof(u16))
- {
- const u32 texture_halfword = Common::swap16(texture[i]) & 0x3fff;
-
- min = std::min(min, texture_halfword);
- max = std::max(max, texture_halfword);
- }
- break;
- }
- if (tlut_size > 0)
- {
- tlut_size = 2 * (max + 1 - min);
- tlut += 2 * min;
- }
-
- const u64 tex_hash = XXH64(texture, texture_size, 0);
- const u64 tlut_hash = tlut_size ? XXH64(tlut, tlut_size, 0) : 0;
-
- const std::string base_name = fmt::format("{}{}x{}{}_{:016x}", s_format_prefix, width, height,
- has_mipmaps ? "_m" : "", tex_hash);
- const std::string tlut_name = tlut_size ? fmt::format("_{:016x}", tlut_hash) : "";
- const std::string format_name = fmt::format("_{}", static_cast(format));
- const std::string full_name = base_name + tlut_name + format_name;
+ const auto texture_name_details = texture_info.CalculateTextureName();
// try to match a wildcard template
if (!dump)
{
- const std::string texture_name = fmt::format("{}_${}", base_name, format_name);
+ const std::string texture_name =
+ fmt::format("{}_${}", texture_name_details.base_name, texture_name_details.format_name);
if (s_textureMap.find(texture_name) != s_textureMap.end())
return texture_name;
}
// else generate the complete texture
+ const std::string full_name = texture_name_details.GetFullName();
if (dump || s_textureMap.find(full_name) != s_textureMap.end())
return full_name;
@@ -304,13 +253,9 @@ u32 HiresTexture::CalculateMipCount(u32 width, u32 height)
return mip_count;
}
-std::shared_ptr HiresTexture::Search(const u8* texture, size_t texture_size,
- const u8* tlut, size_t tlut_size, u32 width,
- u32 height, TextureFormat format,
- bool has_mipmaps)
+std::shared_ptr HiresTexture::Search(TextureInfo& texture_info)
{
- std::string base_filename =
- GenBaseName(texture, texture_size, tlut, tlut_size, width, height, format, has_mipmaps);
+ const std::string base_filename = GenBaseName(texture_info);
std::lock_guard lk(s_textureCacheMutex);
@@ -320,7 +265,8 @@ std::shared_ptr HiresTexture::Search(const u8* texture, size_t tex
return iter->second;
}
- std::shared_ptr ptr(Load(base_filename, width, height));
+ std::shared_ptr ptr(
+ Load(base_filename, texture_info.GetRawWidth(), texture_info.GetRawHeight()));
if (ptr && g_ActiveConfig.bCacheHiresTextures)
{
diff --git a/Source/Core/VideoCommon/HiresTextures.h b/Source/Core/VideoCommon/HiresTextures.h
index 7adabbf94e..8e16f7bdbe 100644
--- a/Source/Core/VideoCommon/HiresTextures.h
+++ b/Source/Core/VideoCommon/HiresTextures.h
@@ -11,6 +11,7 @@
#include "Common/CommonTypes.h"
#include "VideoCommon/TextureConfig.h"
+#include "VideoCommon/TextureInfo.h"
enum class TextureFormat;
@@ -25,13 +26,9 @@ public:
static void Clear();
static void Shutdown();
- static std::shared_ptr Search(const u8* texture, size_t texture_size,
- const u8* tlut, size_t tlut_size, u32 width,
- u32 height, TextureFormat format, bool has_mipmaps);
+ static std::shared_ptr Search(TextureInfo& texture_info);
- static std::string GenBaseName(const u8* texture, size_t texture_size, const u8* tlut,
- size_t tlut_size, u32 width, u32 height, TextureFormat format,
- bool has_mipmaps, bool dump = false);
+ static std::string GenBaseName(TextureInfo& texture_info, bool dump = false);
static u32 CalculateMipCount(u32 width, u32 height);
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index b791d259f1..e75d024fb6 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -261,7 +261,7 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config)
}
TextureCacheBase::TCacheEntry*
-TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, u8* palette, TLUTFormat tlutfmt)
+TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLUTFormat tlutfmt)
{
DEBUG_ASSERT(g_ActiveConfig.backend_info.bSupportsPaletteConversion);
@@ -739,7 +739,7 @@ void TextureCacheBase::TCacheEntry::DoState(PointerWrap& p)
}
TextureCacheBase::TCacheEntry*
-TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* palette,
+TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8* palette,
TLUTFormat tlutfmt)
{
// If the flag may_have_overlapping_textures is cleared, there are no overlapping EFB copies,
@@ -948,11 +948,6 @@ void TextureCacheBase::DumpTexture(TCacheEntry* entry, std::string basename, uns
entry->texture->Save(filename, level);
}
-static u32 CalculateLevelSize(u32 level_0_size, u32 level)
-{
- return std::max(level_0_size >> level, 1u);
-}
-
static void SetSamplerState(u32 index, float custom_tex_scale, bool custom_tex,
bool has_arbitrary_mips)
{
@@ -1182,23 +1177,9 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
return bound_textures[stage];
}
- const FourTexUnits& tex = bpmem.tex[stage >> 2];
- const u32 id = stage & 3;
- const u32 address = (tex.texImage3[id].image_base /* & 0x1FFFFF*/) << 5;
- u32 width = tex.texImage0[id].width + 1;
- u32 height = tex.texImage0[id].height + 1;
- const TextureFormat texformat = tex.texImage0[id].format;
- const u32 tlutaddr = tex.texTlut[id].tmem_offset << 9;
- const TLUTFormat tlutfmt = tex.texTlut[id].tlut_format;
- const bool use_mipmaps = SamplerCommon::AreBpTexMode0MipmapsEnabled(tex.texMode0[id]);
- u32 tex_levels = use_mipmaps ? ((tex.texMode1[id].max_lod + 0xf) / 0x10 + 1) : 1;
- const bool from_tmem = tex.texImage1[id].cache_manually_managed != 0;
- const u32 tmem_address_even = from_tmem ? tex.texImage1[id].tmem_even * TMEM_LINE_SIZE : 0;
- const u32 tmem_address_odd = from_tmem ? tex.texImage2[id].tmem_odd * TMEM_LINE_SIZE : 0;
+ TextureInfo texture_info = TextureInfo::FromStage(stage);
- auto entry = GetTexture(address, width, height, texformat,
- g_ActiveConfig.iSafeTextureCache_ColorSamples, tlutaddr, tlutfmt,
- use_mipmaps, tex_levels, from_tmem, tmem_address_even, tmem_address_odd);
+ auto entry = GetTexture(g_ActiveConfig.iSafeTextureCache_ColorSamples, texture_info);
if (!entry)
return nullptr;
@@ -1214,86 +1195,58 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
}
TextureCacheBase::TCacheEntry*
-TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFormat texformat,
- const int textureCacheSafetyColorSampleSize, u32 tlutaddr,
- TLUTFormat tlutfmt, bool use_mipmaps, u32 tex_levels, bool from_tmem,
- u32 tmem_address_even, u32 tmem_address_odd)
+TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, TextureInfo& texture_info)
{
- // TexelSizeInNibbles(format) * width * height / 16;
- const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat);
- const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat);
+ u32 expanded_width = texture_info.GetExpandedWidth();
+ u32 expanded_height = texture_info.GetExpandedHeight();
- unsigned int expandedWidth = Common::AlignUp(width, bsw);
- unsigned int expandedHeight = Common::AlignUp(height, bsh);
- const unsigned int nativeW = width;
- const unsigned int nativeH = height;
+ u32 width = texture_info.GetRawWidth();
+ u32 height = texture_info.GetRawHeight();
// Hash assigned to texcache entry (also used to generate filenames used for texture dumping and
// custom texture lookup)
u64 base_hash = TEXHASH_INVALID;
u64 full_hash = TEXHASH_INVALID;
- TextureAndTLUTFormat full_format(texformat, tlutfmt);
-
- const bool isPaletteTexture = IsColorIndexed(texformat);
+ TextureAndTLUTFormat full_format(texture_info.GetTextureFormat(), texture_info.GetTlutFormat());
// Reject invalid tlut format.
- if (isPaletteTexture && !IsValidTLUTFormat(tlutfmt))
+ if (texture_info.GetPaletteSize() && !IsValidTLUTFormat(texture_info.GetTlutFormat()))
return nullptr;
- const u32 texture_size =
- TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat);
- u32 bytes_per_block = (bsw * bsh * TexDecoder_GetTexelSizeInNibbles(texformat)) / 2;
- u32 additional_mips_size = 0; // not including level 0, which is texture_size
-
- // GPUs don't like when the specified mipmap count would require more than one 1x1-sized LOD in
- // the mipmap chain
- // e.g. 64x64 with 7 LODs would have the mipmap chain 64x64,32x32,16x16,8x8,4x4,2x2,1x1,0x0, so we
- // limit the mipmap count to 6 there
- tex_levels = std::min(IntLog2(std::max(width, height)) + 1, tex_levels);
-
- for (u32 level = 1; level != tex_levels; ++level)
- {
- // We still need to calculate the original size of the mips
- const u32 expanded_mip_width = Common::AlignUp(CalculateLevelSize(width, level), bsw);
- const u32 expanded_mip_height = Common::AlignUp(CalculateLevelSize(height, level), bsh);
-
- additional_mips_size +=
- TexDecoder_GetTextureSizeInBytes(expanded_mip_width, expanded_mip_height, texformat);
- }
+ u32 bytes_per_block = (texture_info.GetBlockWidth() * texture_info.GetBlockHeight() *
+ TexDecoder_GetTexelSizeInNibbles(texture_info.GetTextureFormat())) /
+ 2;
// TODO: the texture cache lookup is based on address, but a texture from tmem has no reason
// to have a unique and valid address. This could result in a regular texture and a tmem
// texture aliasing onto the same texture cache entry.
- const u8* src_data;
- if (from_tmem)
- src_data = &texMem[tmem_address_even];
- else
- src_data = Memory::GetPointer(address);
-
- if (!src_data)
+ if (!texture_info.GetData())
{
- ERROR_LOG_FMT(VIDEO, "Trying to use an invalid texture address {:#010x}", address);
+ ERROR_LOG_FMT(VIDEO, "Trying to use an invalid texture address {:#010x}",
+ texture_info.GetRawAddress());
return nullptr;
}
// If we are recording a FifoLog, keep track of what memory we read. FifoRecorder does
// its own memory modification tracking independent of the texture hashing below.
- if (OpcodeDecoder::g_record_fifo_data && !from_tmem)
+ if (OpcodeDecoder::g_record_fifo_data && !texture_info.IsFromTmem())
{
- FifoRecorder::GetInstance().UseMemory(address, texture_size + additional_mips_size,
- MemoryUpdate::TEXTURE_MAP);
+ FifoRecorder::GetInstance().UseMemory(
+ texture_info.GetRawAddress(), texture_info.GetFullLevelSize(), MemoryUpdate::TEXTURE_MAP);
}
// TODO: This doesn't hash GB tiles for preloaded RGBA8 textures (instead, it's hashing more data
// from the low tmem bank than it should)
- base_hash = Common::GetHash64(src_data, texture_size, textureCacheSafetyColorSampleSize);
+ base_hash = Common::GetHash64(texture_info.GetData(), texture_info.GetTextureSize(),
+ textureCacheSafetyColorSampleSize);
u32 palette_size = 0;
- if (isPaletteTexture)
+ if (texture_info.GetPaletteSize())
{
- palette_size = TexDecoder_GetPaletteSize(texformat);
- full_hash = base_hash ^ Common::GetHash64(&texMem[tlutaddr], palette_size,
- textureCacheSafetyColorSampleSize);
+ palette_size = *texture_info.GetPaletteSize();
+ full_hash =
+ base_hash ^ Common::GetHash64(texture_info.GetTlutAddress(), *texture_info.GetPaletteSize(),
+ textureCacheSafetyColorSampleSize);
}
else
{
@@ -1339,7 +1292,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
// For efb copies, the entry created in CopyRenderTargetToTexture always has to be used, or else
// it was
// done in vain.
- auto iter_range = textures_by_address.equal_range(address);
+ auto iter_range = textures_by_address.equal_range(texture_info.GetRawAddress());
TexAddrCache::iterator iter = iter_range.first;
TexAddrCache::iterator oldest_entry = iter;
int temp_frameCount = 0x7fffffff;
@@ -1364,13 +1317,14 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
// Do not load strided EFB copies, they are not meant to be used directly.
// Also do not directly load EFB copies, which were partly overwritten.
- if (entry->IsEfbCopy() && entry->native_width == nativeW && entry->native_height == nativeH &&
+ if (entry->IsEfbCopy() && entry->native_width == texture_info.GetRawWidth() &&
+ entry->native_height == texture_info.GetRawHeight() &&
entry->memory_stride == entry->BytesPerRow() && !entry->may_have_overlapping_textures)
{
// EFB copies have slightly different rules as EFB copy formats have different
// meanings from texture formats.
if ((base_hash == entry->hash &&
- (!isPaletteTexture || g_Config.backend_info.bSupportsPaletteConversion)) ||
+ (!texture_info.GetPaletteSize() || g_Config.backend_info.bSupportsPaletteConversion)) ||
IsPlayingBackFifologWithBrokenEFBCopies)
{
// The texture format in VRAM must match the format that the copy was created with. Some
@@ -1379,10 +1333,10 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
// GPU (e.g. IA4 and I8 or RGB565 and RGBA5). The only known game which reinteprets texels
// in this manner is Spiderman Shattered Dimensions, where it creates a copy in B8 format,
// and sets it up as a IA4 texture.
- if (!IsCompatibleTextureFormat(entry->format.texfmt, texformat))
+ if (!IsCompatibleTextureFormat(entry->format.texfmt, texture_info.GetTextureFormat()))
{
// Can we reinterpret this in VRAM?
- if (CanReinterpretTextureOnGPU(entry->format.texfmt, texformat))
+ if (CanReinterpretTextureOnGPU(entry->format.texfmt, texture_info.GetTextureFormat()))
{
// Delay the conversion until afterwards, it's possible this texture has already been
// converted.
@@ -1405,7 +1359,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
// TODO: We should check width/height/levels for EFB copies. I'm not sure what effect
// checking width/height/levels would have.
- if (!isPaletteTexture || !g_Config.backend_info.bSupportsPaletteConversion)
+ if (!texture_info.GetPaletteSize() || !g_Config.backend_info.bSupportsPaletteConversion)
return entry;
// Note that we found an unconverted EFB copy, then continue. We'll
@@ -1428,10 +1382,12 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
{
// For normal textures, all texture parameters need to match
if (!entry->IsEfbCopy() && entry->hash == full_hash && entry->format == full_format &&
- entry->native_levels >= tex_levels && entry->native_width == nativeW &&
- entry->native_height == nativeH)
+ entry->native_levels >= texture_info.GetLevelCount() &&
+ entry->native_width == texture_info.GetRawWidth() &&
+ entry->native_height == texture_info.GetRawHeight())
{
- entry = DoPartialTextureUpdates(iter->second, &texMem[tlutaddr], tlutfmt);
+ entry = DoPartialTextureUpdates(iter->second, texture_info.GetTlutAddress(),
+ texture_info.GetTlutFormat());
entry->texture->FinishedRendering();
return entry;
}
@@ -1445,7 +1401,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
// Also skip XFB copies, we might need to still scan them out
// or load them as regular textures later.
if (entry->frameCount != FRAMECOUNT_INVALID && entry->frameCount < temp_frameCount &&
- !entry->IsCopy() && !(isPaletteTexture && entry->base_hash == base_hash))
+ !entry->IsCopy() && !(texture_info.GetPaletteSize() && entry->base_hash == base_hash))
{
temp_frameCount = entry->frameCount;
oldest_entry = iter;
@@ -1455,11 +1411,13 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
if (unreinterpreted_copy != textures_by_address.end())
{
- TCacheEntry* decoded_entry = ReinterpretEntry(unreinterpreted_copy->second, texformat);
+ TCacheEntry* decoded_entry =
+ ReinterpretEntry(unreinterpreted_copy->second, texture_info.GetTextureFormat());
// It's possible to combine reinterpreted textures + palettes.
if (unreinterpreted_copy == unconverted_copy && decoded_entry)
- decoded_entry = ApplyPaletteToEntry(decoded_entry, &texMem[tlutaddr], tlutfmt);
+ decoded_entry = ApplyPaletteToEntry(decoded_entry, texture_info.GetTlutAddress(),
+ texture_info.GetTlutFormat());
if (decoded_entry)
return decoded_entry;
@@ -1467,8 +1425,8 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
if (unconverted_copy != textures_by_address.end())
{
- TCacheEntry* decoded_entry =
- ApplyPaletteToEntry(unconverted_copy->second, &texMem[tlutaddr], tlutfmt);
+ TCacheEntry* decoded_entry = ApplyPaletteToEntry(
+ unconverted_copy->second, texture_info.GetTlutAddress(), texture_info.GetTlutFormat());
if (decoded_entry)
{
@@ -1483,7 +1441,8 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
// Example: Tales of Symphonia (GC) uses over 500 small textures in menus, but only around 70
// different ones
if (textureCacheSafetyColorSampleSize == 0 ||
- std::max(texture_size, palette_size) <= (u32)textureCacheSafetyColorSampleSize * 8)
+ std::max(texture_info.GetTextureSize(), palette_size) <=
+ (u32)textureCacheSafetyColorSampleSize * 8)
{
auto hash_range = textures_by_hash.equal_range(full_hash);
TexHashCache::iterator hash_iter = hash_range.first;
@@ -1491,10 +1450,12 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
{
TCacheEntry* entry = hash_iter->second;
// All parameters, except the address, need to match here
- if (entry->format == full_format && entry->native_levels >= tex_levels &&
- entry->native_width == nativeW && entry->native_height == nativeH)
+ if (entry->format == full_format && entry->native_levels >= texture_info.GetLevelCount() &&
+ entry->native_width == texture_info.GetRawWidth() &&
+ entry->native_height == texture_info.GetRawHeight())
{
- entry = DoPartialTextureUpdates(hash_iter->second, &texMem[tlutaddr], tlutfmt);
+ entry = DoPartialTextureUpdates(hash_iter->second, texture_info.GetTlutAddress(),
+ texture_info.GetTlutFormat());
entry->texture->FinishedRendering();
return entry;
}
@@ -1512,8 +1473,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
std::shared_ptr hires_tex;
if (g_ActiveConfig.bHiresTextures)
{
- hires_tex = HiresTexture::Search(src_data, texture_size, &texMem[tlutaddr], palette_size, width,
- height, texformat, use_mipmaps);
+ hires_tex = HiresTexture::Search(texture_info);
if (hires_tex)
{
@@ -1523,21 +1483,22 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
width = level.width;
height = level.height;
}
- expandedWidth = level.width;
- expandedHeight = level.height;
+ expanded_width = level.width;
+ expanded_height = level.height;
}
}
// how many levels the allocated texture shall have
- const u32 texLevels = hires_tex ? (u32)hires_tex->m_levels.size() : tex_levels;
+ const u32 texLevels = hires_tex ? (u32)hires_tex->m_levels.size() : texture_info.GetLevelCount();
// We can decode on the GPU if it is a supported format and the flag is enabled.
// Currently we don't decode RGBA8 textures from Tmem, as that would require copying from both
// banks, and if we're doing an copy we may as well just do the whole thing on the CPU, since
// there's no conversion between formats. In the future this could be extended with a separate
// shader, however.
- const bool decode_on_gpu = !hires_tex && g_ActiveConfig.UseGPUTextureDecoding() &&
- !(from_tmem && texformat == TextureFormat::RGBA8);
+ const bool decode_on_gpu =
+ !hires_tex && g_ActiveConfig.UseGPUTextureDecoding() &&
+ !(texture_info.IsFromTmem() && texture_info.GetTextureFormat() == TextureFormat::RGBA8);
// create the entry/texture
const TextureConfig config(width, height, texLevels, 1, 1,
@@ -1547,7 +1508,6 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
return nullptr;
ArbitraryMipmapDetector arbitrary_mip_detector;
- const u8* tlut = &texMem[tlutaddr];
if (hires_tex)
{
const auto& level = hires_tex->m_levels[0];
@@ -1561,11 +1521,13 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
if (!hires_tex)
{
if (!decode_on_gpu ||
- !DecodeTextureOnGPU(entry, 0, src_data, texture_size, texformat, width, height,
- expandedWidth, expandedHeight, bytes_per_block * (expandedWidth / bsw),
- tlut, tlutfmt))
+ !DecodeTextureOnGPU(entry, 0, texture_info.GetData(), texture_info.GetTextureSize(),
+ texture_info.GetTextureFormat(), width, height, expanded_width,
+ expanded_height,
+ bytes_per_block * (expanded_width / texture_info.GetBlockWidth()),
+ texture_info.GetTlutAddress(), texture_info.GetTlutFormat()))
{
- size_t decoded_texture_size = expandedWidth * sizeof(u32) * expandedHeight;
+ size_t decoded_texture_size = expanded_width * sizeof(u32) * expanded_height;
// Allocate memory for all levels at once
size_t total_texture_size = decoded_texture_size;
@@ -1574,7 +1536,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
size_t mip_downsample_buffer_size = decoded_texture_size * 5 / 16;
size_t prev_level_size = decoded_texture_size;
- for (u32 i = 1; i < tex_levels; ++i)
+ for (u32 i = 1; i < texture_info.GetLevelCount(); ++i)
{
prev_level_size /= 4;
total_texture_size += prev_level_size;
@@ -1585,35 +1547,39 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
CheckTempSize(total_texture_size);
dst_buffer = temp;
- if (!(texformat == TextureFormat::RGBA8 && from_tmem))
+ if (!(texture_info.GetTextureFormat() == TextureFormat::RGBA8 && texture_info.IsFromTmem()))
{
- TexDecoder_Decode(dst_buffer, src_data, expandedWidth, expandedHeight, texformat, tlut,
- tlutfmt);
+ TexDecoder_Decode(dst_buffer, texture_info.GetData(), expanded_width, expanded_height,
+ texture_info.GetTextureFormat(), texture_info.GetTlutAddress(),
+ texture_info.GetTlutFormat());
}
else
{
- u8* src_data_gb = &texMem[tmem_address_odd];
- TexDecoder_DecodeRGBA8FromTmem(dst_buffer, src_data, src_data_gb, expandedWidth,
- expandedHeight);
+ TexDecoder_DecodeRGBA8FromTmem(dst_buffer, texture_info.GetData(),
+ texture_info.GetTmemOddAddress(), expanded_width,
+ expanded_height);
}
- entry->texture->Load(0, width, height, expandedWidth, dst_buffer, decoded_texture_size);
+ entry->texture->Load(0, width, height, expanded_width, dst_buffer, decoded_texture_size);
- arbitrary_mip_detector.AddLevel(width, height, expandedWidth, dst_buffer);
+ arbitrary_mip_detector.AddLevel(width, height, expanded_width, dst_buffer);
dst_buffer += decoded_texture_size;
}
}
- iter = textures_by_address.emplace(address, entry);
+ iter = textures_by_address.emplace(texture_info.GetRawAddress(), entry);
if (textureCacheSafetyColorSampleSize == 0 ||
- std::max(texture_size, palette_size) <= (u32)textureCacheSafetyColorSampleSize * 8)
+ std::max(texture_info.GetTextureSize(), palette_size) <=
+ (u32)textureCacheSafetyColorSampleSize * 8)
{
entry->textures_by_hash_iter = textures_by_hash.emplace(full_hash, entry);
}
- entry->SetGeneralParameters(address, texture_size, full_format, false);
- entry->SetDimensions(nativeW, nativeH, tex_levels);
+ entry->SetGeneralParameters(texture_info.GetRawAddress(), texture_info.GetTextureSize(),
+ full_format, false);
+ entry->SetDimensions(texture_info.GetRawWidth(), texture_info.GetRawHeight(),
+ texture_info.GetLevelCount());
entry->SetHashes(base_hash, full_hash);
entry->is_custom_tex = hires_tex != nullptr;
entry->memory_stride = entry->BytesPerRow();
@@ -1622,8 +1588,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
std::string basename;
if (g_ActiveConfig.bDumpTextures && !hires_tex)
{
- basename = HiresTexture::GenBaseName(src_data, texture_size, &texMem[tlutaddr], palette_size,
- width, height, texformat, use_mipmaps, true);
+ basename = HiresTexture::GenBaseName(texture_info, true);
}
if (hires_tex)
@@ -1637,46 +1602,34 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
}
else
{
- // load mips - TODO: Loading mipmaps from tmem is untested!
- src_data += texture_size;
-
- const u8* ptr_even = nullptr;
- const u8* ptr_odd = nullptr;
- if (from_tmem)
- {
- ptr_even = &texMem[tmem_address_even + texture_size];
- ptr_odd = &texMem[tmem_address_odd];
- }
-
for (u32 level = 1; level != texLevels; ++level)
{
- const u32 mip_width = CalculateLevelSize(width, level);
- const u32 mip_height = CalculateLevelSize(height, level);
- const u32 expanded_mip_width = Common::AlignUp(mip_width, bsw);
- const u32 expanded_mip_height = Common::AlignUp(mip_height, bsh);
-
- const u8*& mip_src_data = from_tmem ? ((level % 2) ? ptr_odd : ptr_even) : src_data;
- const u32 mip_size =
- TexDecoder_GetTextureSizeInBytes(expanded_mip_width, expanded_mip_height, texformat);
+ auto mip_level = texture_info.GetMipMapLevel(level - 1);
+ if (!mip_level)
+ continue;
if (!decode_on_gpu ||
- !DecodeTextureOnGPU(entry, level, mip_src_data, mip_size, texformat, mip_width,
- mip_height, expanded_mip_width, expanded_mip_height,
- bytes_per_block * (expanded_mip_width / bsw), tlut, tlutfmt))
+ !DecodeTextureOnGPU(
+ entry, level, mip_level->GetData(), mip_level->GetTextureSize(),
+ texture_info.GetTextureFormat(), mip_level->GetRawWidth(), mip_level->GetRawHeight(),
+ mip_level->GetExpandedWidth(), mip_level->GetExpandedHeight(),
+ bytes_per_block * (mip_level->GetExpandedWidth() / texture_info.GetBlockWidth()),
+ texture_info.GetTlutAddress(), texture_info.GetTlutFormat()))
{
// No need to call CheckTempSize here, as the whole buffer is preallocated at the beginning
- const u32 decoded_mip_size = expanded_mip_width * sizeof(u32) * expanded_mip_height;
- TexDecoder_Decode(dst_buffer, mip_src_data, expanded_mip_width, expanded_mip_height,
- texformat, tlut, tlutfmt);
- entry->texture->Load(level, mip_width, mip_height, expanded_mip_width, dst_buffer,
- decoded_mip_size);
+ const u32 decoded_mip_size =
+ mip_level->GetExpandedWidth() * sizeof(u32) * mip_level->GetExpandedHeight();
+ TexDecoder_Decode(dst_buffer, mip_level->GetData(), mip_level->GetExpandedWidth(),
+ mip_level->GetExpandedHeight(), texture_info.GetTextureFormat(),
+ texture_info.GetTlutAddress(), texture_info.GetTlutFormat());
+ entry->texture->Load(level, mip_level->GetRawWidth(), mip_level->GetRawHeight(),
+ mip_level->GetExpandedWidth(), dst_buffer, decoded_mip_size);
- arbitrary_mip_detector.AddLevel(mip_width, mip_height, expanded_mip_width, dst_buffer);
+ arbitrary_mip_detector.AddLevel(mip_level->GetRawWidth(), mip_level->GetRawHeight(),
+ mip_level->GetExpandedWidth(), dst_buffer);
dst_buffer += decoded_mip_size;
}
-
- mip_src_data += mip_size;
}
}
@@ -1694,7 +1647,8 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
INCSTAT(g_stats.num_textures_uploaded);
SETSTAT(g_stats.num_textures_alive, static_cast(textures_by_address.size()));
- entry = DoPartialTextureUpdates(iter->second, &texMem[tlutaddr], tlutfmt);
+ entry = DoPartialTextureUpdates(iter->second, texture_info.GetTlutAddress(),
+ texture_info.GetTlutFormat());
// This should only be needed if the texture was updated, or used GPU decoding.
entry->texture->FinishedRendering();
diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h
index 599f1c2a79..7aef40f080 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.h
+++ b/Source/Core/VideoCommon/TextureCacheBase.h
@@ -21,6 +21,7 @@
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/TextureConfig.h"
#include "VideoCommon/TextureDecoder.h"
+#include "VideoCommon/TextureInfo.h"
class AbstractFramebuffer;
class AbstractStagingTexture;
@@ -216,11 +217,7 @@ public:
TCacheEntry* Load(const u32 stage);
static void InvalidateAllBindPoints() { valid_bind_points.reset(); }
static bool IsValidBindPoint(u32 i) { return valid_bind_points.test(i); }
- TCacheEntry* GetTexture(u32 address, u32 width, u32 height, const TextureFormat texformat,
- const int textureCacheSafetyColorSampleSize, u32 tlutaddr = 0,
- TLUTFormat tlutfmt = TLUTFormat::IA8, bool use_mipmaps = false,
- u32 tex_levels = 1, bool from_tmem = false, u32 tmem_address_even = 0,
- u32 tmem_address_odd = 0);
+ TCacheEntry* GetTexture(const int textureCacheSafetyColorSampleSize, TextureInfo& texture_info);
TCacheEntry* GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
MathUtil::Rectangle* display_rect);
@@ -286,11 +283,11 @@ private:
TCacheEntry* GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride, u64 hash);
- TCacheEntry* ApplyPaletteToEntry(TCacheEntry* entry, u8* palette, TLUTFormat tlutfmt);
+ TCacheEntry* ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLUTFormat tlutfmt);
TCacheEntry* ReinterpretEntry(const TCacheEntry* existing_entry, TextureFormat new_format);
- TCacheEntry* DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* palette,
+ TCacheEntry* DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8* palette,
TLUTFormat tlutfmt);
void StitchXFBCopy(TCacheEntry* entry_to_update);
diff --git a/Source/Core/VideoCommon/TextureInfo.cpp b/Source/Core/VideoCommon/TextureInfo.cpp
new file mode 100644
index 0000000000..6980a25cef
--- /dev/null
+++ b/Source/Core/VideoCommon/TextureInfo.cpp
@@ -0,0 +1,310 @@
+// Copyright 2021 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "VideoCommon/TextureInfo.h"
+
+#include
+
+#include "Common/Align.h"
+#include "Core/HW/Memmap.h"
+#include "VideoCommon/BPMemory.h"
+#include "VideoCommon/SamplerCommon.h"
+#include "VideoCommon/TextureDecoder.h"
+
+TextureInfo TextureInfo::FromStage(u32 stage)
+{
+ const FourTexUnits& tex = bpmem.tex[stage >> 2];
+ const u32 id = stage & 3;
+
+ const auto texture_format = tex.texImage0[id].format;
+ const auto tlut_format = tex.texTlut[id].tlut_format;
+
+ const auto width = tex.texImage0[id].width + 1;
+ const auto height = tex.texImage0[id].height + 1;
+
+ const u32 address = (tex.texImage3[id].image_base /* & 0x1FFFFF*/) << 5;
+
+ const u32 tlutaddr = tex.texTlut[id].tmem_offset << 9;
+ const u8* tlut_ptr = &texMem[tlutaddr];
+
+ std::optional mip_count;
+ const bool has_mipmaps = SamplerCommon::AreBpTexMode0MipmapsEnabled(tex.texMode0[id]);
+ if (has_mipmaps)
+ {
+ mip_count = (tex.texMode1[id].max_lod + 0xf) / 0x10;
+ }
+
+ const bool from_tmem = tex.texImage1[id].cache_manually_managed != 0;
+ const u32 tmem_address_even = from_tmem ? tex.texImage1[id].tmem_even * TMEM_LINE_SIZE : 0;
+ const u32 tmem_address_odd = from_tmem ? tex.texImage2[id].tmem_odd * TMEM_LINE_SIZE : 0;
+
+ if (from_tmem)
+ {
+ return TextureInfo(&texMem[tmem_address_even], tlut_ptr, address, texture_format, tlut_format,
+ width, height, true, &texMem[tmem_address_odd], &texMem[tmem_address_even],
+ mip_count);
+ }
+
+ return TextureInfo(Memory::GetPointer(address), tlut_ptr, address, texture_format, tlut_format,
+ width, height, false, nullptr, nullptr, mip_count);
+}
+
+TextureInfo::TextureInfo(const u8* ptr, const u8* tlut_ptr, u32 address,
+ TextureFormat texture_format, TLUTFormat tlut_format, u32 width,
+ u32 height, bool from_tmem, const u8* tmem_odd, const u8* tmem_even,
+ std::optional mip_count)
+ : m_ptr(ptr), m_tlut_ptr(tlut_ptr), m_address(address), m_texture_format(texture_format),
+ m_tlut_format(tlut_format), m_raw_width(width), m_raw_height(height), m_from_tmem(from_tmem),
+ m_tmem_odd(tmem_odd)
+{
+ const bool is_palette_texture = IsColorIndexed(m_texture_format);
+ if (is_palette_texture)
+ m_palette_size = TexDecoder_GetPaletteSize(m_texture_format);
+
+ // TexelSizeInNibbles(format) * width * height / 16;
+ m_block_width = TexDecoder_GetBlockWidthInTexels(m_texture_format);
+ m_block_height = TexDecoder_GetBlockHeightInTexels(m_texture_format);
+
+ m_expanded_width = Common::AlignUp(m_raw_width, m_block_width);
+ m_expanded_height = Common::AlignUp(m_raw_height, m_block_height);
+
+ m_texture_size =
+ TexDecoder_GetTextureSizeInBytes(m_expanded_width, m_expanded_height, m_texture_format);
+
+ if (mip_count)
+ {
+ const u32 raw_mip_count = *mip_count;
+
+ // GPUs don't like when the specified mipmap count would require more than one 1x1-sized LOD in
+ // the mipmap chain
+ // e.g. 64x64 with 7 LODs would have the mipmap chain 64x64,32x32,16x16,8x8,4x4,2x2,1x1,0x0, so
+ // we limit the mipmap count to 6 there
+ const u32 limited_mip_count =
+ std::min(IntLog2(std::max(width, height)) + 1, raw_mip_count + 1) - 1;
+
+ // load mips - TODO: Loading mipmaps from tmem is untested!
+ const u8* src_data = m_ptr + GetTextureSize();
+
+ for (u32 i = 0; i < limited_mip_count; i++)
+ {
+ MipLevel mip_level(i + 1, *this, m_from_tmem, src_data, tmem_even, tmem_odd);
+ m_mip_levels.push_back(std::move(mip_level));
+ }
+ }
+}
+
+TextureInfo::NameDetails TextureInfo::CalculateTextureName()
+{
+ if (!m_ptr)
+ return NameDetails{};
+
+ const u8* tlut = m_tlut_ptr;
+ size_t tlut_size = m_palette_size ? *m_palette_size : 0;
+
+ // checking for min/max on paletted textures
+ u32 min = 0xffff;
+ u32 max = 0;
+ switch (tlut_size)
+ {
+ case 0:
+ break;
+ case 16 * 2:
+ for (size_t i = 0; i < m_texture_size; i++)
+ {
+ const u32 low_nibble = m_ptr[i] & 0xf;
+ const u32 high_nibble = m_ptr[i] >> 4;
+
+ min = std::min({min, low_nibble, high_nibble});
+ max = std::max({max, low_nibble, high_nibble});
+ }
+ break;
+ case 256 * 2:
+ {
+ for (size_t i = 0; i < m_texture_size; i++)
+ {
+ const u32 texture_byte = m_ptr[i];
+
+ min = std::min(min, texture_byte);
+ max = std::max(max, texture_byte);
+ }
+ break;
+ }
+ case 16384 * 2:
+ for (size_t i = 0; i < m_texture_size; i += sizeof(u16))
+ {
+ const u32 texture_halfword = Common::swap16(m_ptr[i]) & 0x3fff;
+
+ min = std::min(min, texture_halfword);
+ max = std::max(max, texture_halfword);
+ }
+ break;
+ }
+ if (tlut_size > 0)
+ {
+ tlut_size = 2 * (max + 1 - min);
+ tlut += 2 * min;
+ }
+
+ const u64 tex_hash = XXH64(m_ptr, m_texture_size, 0);
+ const u64 tlut_hash = tlut_size ? XXH64(tlut, tlut_size, 0) : 0;
+
+ NameDetails result;
+ result.base_name = fmt::format("{}{}x{}{}_{:016x}", format_prefix, m_raw_width, m_raw_height,
+ m_mip_levels.empty() ? "" : "_m", tex_hash);
+ result.tlut_name = tlut_size ? fmt::format("_{:016x}", tlut_hash) : "";
+ result.format_name = fmt::format("_{}", static_cast(m_texture_format));
+
+ return result;
+}
+
+const u8* TextureInfo::GetData() const
+{
+ return m_ptr;
+}
+
+const u8* TextureInfo::GetTlutAddress() const
+{
+ return m_tlut_ptr;
+}
+
+u32 TextureInfo::GetRawAddress() const
+{
+ return m_address;
+}
+
+bool TextureInfo::IsFromTmem() const
+{
+ return m_from_tmem;
+}
+
+const u8* TextureInfo::GetTmemOddAddress() const
+{
+ return m_tmem_odd;
+}
+
+TextureFormat TextureInfo::GetTextureFormat() const
+{
+ return m_texture_format;
+}
+
+TLUTFormat TextureInfo::GetTlutFormat() const
+{
+ return m_tlut_format;
+}
+
+std::optional TextureInfo::GetPaletteSize() const
+{
+ return m_palette_size;
+}
+
+u32 TextureInfo::GetTextureSize() const
+{
+ return m_texture_size;
+}
+
+u32 TextureInfo::GetBlockWidth() const
+{
+ return m_block_width;
+}
+
+u32 TextureInfo::GetBlockHeight() const
+{
+ return m_block_height;
+}
+
+u32 TextureInfo::GetExpandedWidth() const
+{
+ return m_expanded_width;
+}
+
+u32 TextureInfo::GetExpandedHeight() const
+{
+ return m_expanded_height;
+}
+
+u32 TextureInfo::GetRawWidth() const
+{
+ return m_raw_width;
+}
+
+u32 TextureInfo::GetRawHeight() const
+{
+ return m_raw_height;
+}
+
+bool TextureInfo::HasMipMaps() const
+{
+ return !m_mip_levels.empty();
+}
+
+u32 TextureInfo::GetLevelCount() const
+{
+ return static_cast(m_mip_levels.size()) + 1;
+}
+
+const TextureInfo::MipLevel* TextureInfo::GetMipMapLevel(u32 level) const
+{
+ if (level < m_mip_levels.size())
+ return &m_mip_levels[level];
+
+ return nullptr;
+}
+
+TextureInfo::MipLevel::MipLevel(u32 level, const TextureInfo& parent, bool from_tmem,
+ const u8*& src_data, const u8*& ptr_even, const u8*& ptr_odd)
+{
+ m_raw_width = std::max(parent.GetRawWidth() >> level, 1u);
+ m_raw_height = std::max(parent.GetRawHeight() >> level, 1u);
+ m_expanded_width = Common::AlignUp(m_raw_width, parent.GetBlockWidth());
+ m_expanded_height = Common::AlignUp(m_raw_height, parent.GetBlockHeight());
+
+ // load mips - TODO: Loading mipmaps from tmem is untested!
+
+ m_texture_size = TexDecoder_GetTextureSizeInBytes(m_expanded_width, m_expanded_height,
+ parent.GetTextureFormat());
+
+ const u8*& ptr = from_tmem ? ((level % 2) ? ptr_odd : ptr_even) : src_data;
+ m_ptr = ptr;
+ ptr += m_texture_size;
+}
+
+u32 TextureInfo::GetFullLevelSize() const
+{
+ u32 all_mips_size = 0;
+ for (const auto& mip_map : m_mip_levels)
+ {
+ all_mips_size += mip_map.GetTextureSize();
+ }
+ return m_texture_size + all_mips_size;
+}
+
+const u8* TextureInfo::MipLevel::GetData() const
+{
+ return m_ptr;
+}
+
+u32 TextureInfo::MipLevel::GetTextureSize() const
+{
+ return m_texture_size;
+}
+
+u32 TextureInfo::MipLevel::GetExpandedWidth() const
+{
+ return m_expanded_width;
+}
+
+u32 TextureInfo::MipLevel::GetExpandedHeight() const
+{
+ return m_expanded_height;
+}
+
+u32 TextureInfo::MipLevel::GetRawWidth() const
+{
+ return m_raw_width;
+}
+
+u32 TextureInfo::MipLevel::GetRawHeight() const
+{
+ return m_raw_height;
+}
diff --git a/Source/Core/VideoCommon/TextureInfo.h b/Source/Core/VideoCommon/TextureInfo.h
new file mode 100644
index 0000000000..71e4b58dd9
--- /dev/null
+++ b/Source/Core/VideoCommon/TextureInfo.h
@@ -0,0 +1,117 @@
+// Copyright 2021 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "Common/CommonTypes.h"
+
+enum class TextureFormat;
+enum class TLUTFormat;
+
+class TextureInfo
+{
+public:
+ static TextureInfo FromStage(u32 stage);
+ TextureInfo(const u8* ptr, const u8* tlut_ptr, u32 address, TextureFormat texture_format,
+ TLUTFormat tlut_format, u32 width, u32 height, bool from_tmem, const u8* tmem_odd,
+ const u8* tmem_even, std::optional mip_count);
+
+ struct NameDetails
+ {
+ std::string base_name;
+ std::string tlut_name;
+ std::string format_name;
+
+ std::string GetFullName() const { return base_name + tlut_name + format_name; }
+ };
+ NameDetails CalculateTextureName();
+
+ const u8* GetData() const;
+ const u8* GetTlutAddress() const;
+
+ u32 GetRawAddress() const;
+
+ bool IsFromTmem() const;
+ const u8* GetTmemOddAddress() const;
+
+ TextureFormat GetTextureFormat() const;
+ TLUTFormat GetTlutFormat() const;
+
+ std::optional GetPaletteSize() const;
+ u32 GetTextureSize() const;
+
+ u32 GetBlockWidth() const;
+ u32 GetBlockHeight() const;
+
+ u32 GetExpandedWidth() const;
+ u32 GetExpandedHeight() const;
+
+ u32 GetRawWidth() const;
+ u32 GetRawHeight() const;
+
+ class MipLevel
+ {
+ public:
+ MipLevel(u32 level, const TextureInfo& parent, bool from_tmem, const u8*& src_data,
+ const u8*& ptr_even, const u8*& ptr_odd);
+
+ const u8* GetData() const;
+
+ u32 GetTextureSize() const;
+
+ u32 GetExpandedWidth() const;
+ u32 GetExpandedHeight() const;
+
+ u32 GetRawWidth() const;
+ u32 GetRawHeight() const;
+
+ private:
+ const u8* m_ptr;
+
+ u32 m_texture_size = 0;
+
+ u32 m_expanded_width;
+ u32 m_raw_width;
+
+ u32 m_expanded_height;
+ u32 m_raw_height;
+ };
+
+ bool HasMipMaps() const;
+ u32 GetLevelCount() const;
+ const MipLevel* GetMipMapLevel(u32 level) const;
+ u32 GetFullLevelSize() const;
+
+ static constexpr std::string_view format_prefix{"tex1_"};
+
+private:
+ const u8* m_ptr;
+ const u8* m_tlut_ptr;
+
+ u32 m_address;
+
+ bool m_from_tmem;
+ const u8* m_tmem_odd;
+
+ TextureFormat m_texture_format;
+ TLUTFormat m_tlut_format;
+
+ std::vector m_mip_levels;
+
+ u32 m_texture_size = 0;
+ std::optional m_palette_size;
+
+ u32 m_block_width;
+ u32 m_expanded_width;
+ u32 m_raw_width;
+
+ u32 m_block_height;
+ u32 m_expanded_height;
+ u32 m_raw_height;
+};