Merge pull request #9681 from iwubcode/texture-info

VideoCommon: move all texture calculations to a "TextureInfo" class
This commit is contained in:
Mat M 2021-05-13 06:44:08 -04:00 committed by GitHub
commit d034c830ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 551 additions and 226 deletions

View File

@ -643,6 +643,7 @@
<ClInclude Include="VideoCommon\TextureConverterShaderGen.h" /> <ClInclude Include="VideoCommon\TextureConverterShaderGen.h" />
<ClInclude Include="VideoCommon\TextureDecoder_Util.h" /> <ClInclude Include="VideoCommon\TextureDecoder_Util.h" />
<ClInclude Include="VideoCommon\TextureDecoder.h" /> <ClInclude Include="VideoCommon\TextureDecoder.h" />
<ClInclude Include="VideoCommon\TextureInfo.h" />
<ClInclude Include="VideoCommon\UberShaderCommon.h" /> <ClInclude Include="VideoCommon\UberShaderCommon.h" />
<ClInclude Include="VideoCommon\UberShaderPixel.h" /> <ClInclude Include="VideoCommon\UberShaderPixel.h" />
<ClInclude Include="VideoCommon\UberShaderVertex.h" /> <ClInclude Include="VideoCommon\UberShaderVertex.h" />
@ -1192,6 +1193,7 @@
<ClCompile Include="VideoCommon\TextureConversionShader.cpp" /> <ClCompile Include="VideoCommon\TextureConversionShader.cpp" />
<ClCompile Include="VideoCommon\TextureConverterShaderGen.cpp" /> <ClCompile Include="VideoCommon\TextureConverterShaderGen.cpp" />
<ClCompile Include="VideoCommon\TextureDecoder_Common.cpp" /> <ClCompile Include="VideoCommon\TextureDecoder_Common.cpp" />
<ClCompile Include="VideoCommon\TextureInfo.cpp" />
<ClCompile Include="VideoCommon\UberShaderCommon.cpp" /> <ClCompile Include="VideoCommon\UberShaderCommon.cpp" />
<ClCompile Include="VideoCommon\UberShaderPixel.cpp" /> <ClCompile Include="VideoCommon\UberShaderPixel.cpp" />
<ClCompile Include="VideoCommon\UberShaderVertex.cpp" /> <ClCompile Include="VideoCommon\UberShaderVertex.cpp" />

View File

@ -88,6 +88,8 @@ add_library(videocommon
TextureDecoder.h TextureDecoder.h
TextureDecoder_Common.cpp TextureDecoder_Common.cpp
TextureDecoder_Util.h TextureDecoder_Util.h
TextureInfo.cpp
TextureInfo.h
UberShaderCommon.cpp UberShaderCommon.cpp
UberShaderCommon.h UberShaderCommon.h
UberShaderPixel.cpp UberShaderPixel.cpp

View File

@ -214,75 +214,24 @@ void HiresTexture::Prefetch()
10000); 10000);
} }
std::string HiresTexture::GenBaseName(const u8* texture, size_t texture_size, const u8* tlut, std::string HiresTexture::GenBaseName(TextureInfo& texture_info, bool dump)
size_t tlut_size, u32 width, u32 height, TextureFormat format,
bool has_mipmaps, bool dump)
{ {
if (!dump && s_textureMap.empty()) if (!dump && s_textureMap.empty())
return ""; return "";
// checking for min/max on paletted textures const auto texture_name_details = texture_info.CalculateTextureName();
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<int>(format));
const std::string full_name = base_name + tlut_name + format_name;
// try to match a wildcard template // try to match a wildcard template
if (!dump) 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()) if (s_textureMap.find(texture_name) != s_textureMap.end())
return texture_name; return texture_name;
} }
// else generate the complete texture // else generate the complete texture
const std::string full_name = texture_name_details.GetFullName();
if (dump || s_textureMap.find(full_name) != s_textureMap.end()) if (dump || s_textureMap.find(full_name) != s_textureMap.end())
return full_name; return full_name;
@ -304,13 +253,9 @@ u32 HiresTexture::CalculateMipCount(u32 width, u32 height)
return mip_count; return mip_count;
} }
std::shared_ptr<HiresTexture> HiresTexture::Search(const u8* texture, size_t texture_size, std::shared_ptr<HiresTexture> HiresTexture::Search(TextureInfo& texture_info)
const u8* tlut, size_t tlut_size, u32 width,
u32 height, TextureFormat format,
bool has_mipmaps)
{ {
std::string base_filename = const std::string base_filename = GenBaseName(texture_info);
GenBaseName(texture, texture_size, tlut, tlut_size, width, height, format, has_mipmaps);
std::lock_guard<std::mutex> lk(s_textureCacheMutex); std::lock_guard<std::mutex> lk(s_textureCacheMutex);
@ -320,7 +265,8 @@ std::shared_ptr<HiresTexture> HiresTexture::Search(const u8* texture, size_t tex
return iter->second; return iter->second;
} }
std::shared_ptr<HiresTexture> ptr(Load(base_filename, width, height)); std::shared_ptr<HiresTexture> ptr(
Load(base_filename, texture_info.GetRawWidth(), texture_info.GetRawHeight()));
if (ptr && g_ActiveConfig.bCacheHiresTextures) if (ptr && g_ActiveConfig.bCacheHiresTextures)
{ {

View File

@ -11,6 +11,7 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "VideoCommon/TextureConfig.h" #include "VideoCommon/TextureConfig.h"
#include "VideoCommon/TextureInfo.h"
enum class TextureFormat; enum class TextureFormat;
@ -25,13 +26,9 @@ public:
static void Clear(); static void Clear();
static void Shutdown(); static void Shutdown();
static std::shared_ptr<HiresTexture> Search(const u8* texture, size_t texture_size, static std::shared_ptr<HiresTexture> Search(TextureInfo& texture_info);
const u8* tlut, size_t tlut_size, u32 width,
u32 height, TextureFormat format, bool has_mipmaps);
static std::string GenBaseName(const u8* texture, size_t texture_size, const u8* tlut, static std::string GenBaseName(TextureInfo& texture_info, bool dump = false);
size_t tlut_size, u32 width, u32 height, TextureFormat format,
bool has_mipmaps, bool dump = false);
static u32 CalculateMipCount(u32 width, u32 height); static u32 CalculateMipCount(u32 width, u32 height);

View File

@ -261,7 +261,7 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config)
} }
TextureCacheBase::TCacheEntry* 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); DEBUG_ASSERT(g_ActiveConfig.backend_info.bSupportsPaletteConversion);
@ -759,7 +759,7 @@ void TextureCacheBase::TCacheEntry::DoState(PointerWrap& p)
} }
TextureCacheBase::TCacheEntry* TextureCacheBase::TCacheEntry*
TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* palette, TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8* palette,
TLUTFormat tlutfmt) TLUTFormat tlutfmt)
{ {
// If the flag may_have_overlapping_textures is cleared, there are no overlapping EFB copies, // If the flag may_have_overlapping_textures is cleared, there are no overlapping EFB copies,
@ -968,11 +968,6 @@ void TextureCacheBase::DumpTexture(TCacheEntry* entry, std::string basename, uns
entry->texture->Save(filename, level); 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, static void SetSamplerState(u32 index, float custom_tex_scale, bool custom_tex,
bool has_arbitrary_mips) bool has_arbitrary_mips)
{ {
@ -1202,23 +1197,9 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
return bound_textures[stage]; return bound_textures[stage];
} }
const FourTexUnits& tex = bpmem.tex[stage >> 2]; TextureInfo texture_info = TextureInfo::FromStage(stage);
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;
auto entry = GetTexture(address, width, height, texformat, auto entry = GetTexture(g_ActiveConfig.iSafeTextureCache_ColorSamples, texture_info);
g_ActiveConfig.iSafeTextureCache_ColorSamples, tlutaddr, tlutfmt,
use_mipmaps, tex_levels, from_tmem, tmem_address_even, tmem_address_odd);
if (!entry) if (!entry)
return nullptr; return nullptr;
@ -1234,85 +1215,57 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
} }
TextureCacheBase::TCacheEntry* TextureCacheBase::TCacheEntry*
TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFormat texformat, TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, TextureInfo& texture_info)
const int textureCacheSafetyColorSampleSize, u32 tlutaddr,
TLUTFormat tlutfmt, bool use_mipmaps, u32 tex_levels, bool from_tmem,
u32 tmem_address_even, u32 tmem_address_odd)
{ {
// TexelSizeInNibbles(format) * width * height / 16; u32 expanded_width = texture_info.GetExpandedWidth();
const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat); u32 expanded_height = texture_info.GetExpandedHeight();
const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat);
unsigned int expandedWidth = Common::AlignUp(width, bsw); u32 width = texture_info.GetRawWidth();
unsigned int expandedHeight = Common::AlignUp(height, bsh); u32 height = texture_info.GetRawHeight();
const unsigned int nativeW = width;
const unsigned int nativeH = height;
// Hash assigned to texcache entry (also used to generate filenames used for texture dumping and // Hash assigned to texcache entry (also used to generate filenames used for texture dumping and
// custom texture lookup) // custom texture lookup)
u64 base_hash = TEXHASH_INVALID; u64 base_hash = TEXHASH_INVALID;
u64 full_hash = TEXHASH_INVALID; u64 full_hash = TEXHASH_INVALID;
TextureAndTLUTFormat full_format(texformat, tlutfmt); TextureAndTLUTFormat full_format(texture_info.GetTextureFormat(), texture_info.GetTlutFormat());
const bool isPaletteTexture = IsColorIndexed(texformat);
// Reject invalid tlut format. // Reject invalid tlut format.
if (isPaletteTexture && !IsValidTLUTFormat(tlutfmt)) if (texture_info.GetPaletteSize() && !IsValidTLUTFormat(texture_info.GetTlutFormat()))
return nullptr; return nullptr;
const u32 texture_size = u32 bytes_per_block = (texture_info.GetBlockWidth() * texture_info.GetBlockHeight() *
TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); TexDecoder_GetTexelSizeInNibbles(texture_info.GetTextureFormat())) /
u32 bytes_per_block = (bsw * bsh * TexDecoder_GetTexelSizeInNibbles(texformat)) / 2; 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<u32>(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);
}
// TODO: the texture cache lookup is based on address, but a texture from tmem has no reason // 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 // 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. // texture aliasing onto the same texture cache entry.
const u8* src_data; if (!texture_info.GetData())
if (from_tmem)
src_data = &texMem[tmem_address_even];
else
src_data = Memory::GetPointer(address);
if (!src_data)
{ {
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; return nullptr;
} }
// If we are recording a FifoLog, keep track of what memory we read. FifoRecorder does // 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. // 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, FifoRecorder::GetInstance().UseMemory(
MemoryUpdate::TEXTURE_MAP); 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 // 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) // 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; u32 palette_size = 0;
if (isPaletteTexture) if (texture_info.GetPaletteSize())
{ {
palette_size = TexDecoder_GetPaletteSize(texformat); palette_size = *texture_info.GetPaletteSize();
full_hash = base_hash ^ Common::GetHash64(&texMem[tlutaddr], palette_size, full_hash =
base_hash ^ Common::GetHash64(texture_info.GetTlutAddress(), *texture_info.GetPaletteSize(),
textureCacheSafetyColorSampleSize); textureCacheSafetyColorSampleSize);
} }
else else
@ -1359,7 +1312,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 // For efb copies, the entry created in CopyRenderTargetToTexture always has to be used, or else
// it was // it was
// done in vain. // 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 iter = iter_range.first;
TexAddrCache::iterator oldest_entry = iter; TexAddrCache::iterator oldest_entry = iter;
int temp_frameCount = 0x7fffffff; int temp_frameCount = 0x7fffffff;
@ -1384,13 +1337,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. // 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. // 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) entry->memory_stride == entry->BytesPerRow() && !entry->may_have_overlapping_textures)
{ {
// EFB copies have slightly different rules as EFB copy formats have different // EFB copies have slightly different rules as EFB copy formats have different
// meanings from texture formats. // meanings from texture formats.
if ((base_hash == entry->hash && if ((base_hash == entry->hash &&
(!isPaletteTexture || g_Config.backend_info.bSupportsPaletteConversion)) || (!texture_info.GetPaletteSize() || g_Config.backend_info.bSupportsPaletteConversion)) ||
IsPlayingBackFifologWithBrokenEFBCopies) IsPlayingBackFifologWithBrokenEFBCopies)
{ {
// The texture format in VRAM must match the format that the copy was created with. Some // The texture format in VRAM must match the format that the copy was created with. Some
@ -1399,10 +1353,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 // 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, // in this manner is Spiderman Shattered Dimensions, where it creates a copy in B8 format,
// and sets it up as a IA4 texture. // 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? // 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 // Delay the conversion until afterwards, it's possible this texture has already been
// converted. // converted.
@ -1425,7 +1379,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 // TODO: We should check width/height/levels for EFB copies. I'm not sure what effect
// checking width/height/levels would have. // checking width/height/levels would have.
if (!isPaletteTexture || !g_Config.backend_info.bSupportsPaletteConversion) if (!texture_info.GetPaletteSize() || !g_Config.backend_info.bSupportsPaletteConversion)
return entry; return entry;
// Note that we found an unconverted EFB copy, then continue. We'll // Note that we found an unconverted EFB copy, then continue. We'll
@ -1448,10 +1402,12 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
{ {
// For normal textures, all texture parameters need to match // For normal textures, all texture parameters need to match
if (!entry->IsEfbCopy() && entry->hash == full_hash && entry->format == full_format && if (!entry->IsEfbCopy() && entry->hash == full_hash && entry->format == full_format &&
entry->native_levels >= tex_levels && entry->native_width == nativeW && entry->native_levels >= texture_info.GetLevelCount() &&
entry->native_height == nativeH) 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(); entry->texture->FinishedRendering();
return entry; return entry;
} }
@ -1465,7 +1421,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
// Also skip XFB copies, we might need to still scan them out // Also skip XFB copies, we might need to still scan them out
// or load them as regular textures later. // or load them as regular textures later.
if (entry->frameCount != FRAMECOUNT_INVALID && entry->frameCount < temp_frameCount && 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; temp_frameCount = entry->frameCount;
oldest_entry = iter; oldest_entry = iter;
@ -1475,11 +1431,13 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
if (unreinterpreted_copy != textures_by_address.end()) 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. // It's possible to combine reinterpreted textures + palettes.
if (unreinterpreted_copy == unconverted_copy && decoded_entry) 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) if (decoded_entry)
return decoded_entry; return decoded_entry;
@ -1487,8 +1445,8 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
if (unconverted_copy != textures_by_address.end()) if (unconverted_copy != textures_by_address.end())
{ {
TCacheEntry* decoded_entry = TCacheEntry* decoded_entry = ApplyPaletteToEntry(
ApplyPaletteToEntry(unconverted_copy->second, &texMem[tlutaddr], tlutfmt); unconverted_copy->second, texture_info.GetTlutAddress(), texture_info.GetTlutFormat());
if (decoded_entry) if (decoded_entry)
{ {
@ -1503,7 +1461,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 // Example: Tales of Symphonia (GC) uses over 500 small textures in menus, but only around 70
// different ones // different ones
if (textureCacheSafetyColorSampleSize == 0 || 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); auto hash_range = textures_by_hash.equal_range(full_hash);
TexHashCache::iterator hash_iter = hash_range.first; TexHashCache::iterator hash_iter = hash_range.first;
@ -1511,10 +1470,12 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
{ {
TCacheEntry* entry = hash_iter->second; TCacheEntry* entry = hash_iter->second;
// All parameters, except the address, need to match here // All parameters, except the address, need to match here
if (entry->format == full_format && entry->native_levels >= tex_levels && if (entry->format == full_format && entry->native_levels >= texture_info.GetLevelCount() &&
entry->native_width == nativeW && entry->native_height == nativeH) 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(); entry->texture->FinishedRendering();
return entry; return entry;
} }
@ -1532,8 +1493,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
std::shared_ptr<HiresTexture> hires_tex; std::shared_ptr<HiresTexture> hires_tex;
if (g_ActiveConfig.bHiresTextures) if (g_ActiveConfig.bHiresTextures)
{ {
hires_tex = HiresTexture::Search(src_data, texture_size, &texMem[tlutaddr], palette_size, width, hires_tex = HiresTexture::Search(texture_info);
height, texformat, use_mipmaps);
if (hires_tex) if (hires_tex)
{ {
@ -1543,21 +1503,22 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
width = level.width; width = level.width;
height = level.height; height = level.height;
} }
expandedWidth = level.width; expanded_width = level.width;
expandedHeight = level.height; expanded_height = level.height;
} }
} }
// how many levels the allocated texture shall have // 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. // 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 // 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 // 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 // there's no conversion between formats. In the future this could be extended with a separate
// shader, however. // shader, however.
const bool decode_on_gpu = !hires_tex && g_ActiveConfig.UseGPUTextureDecoding() && const bool decode_on_gpu =
!(from_tmem && texformat == TextureFormat::RGBA8); !hires_tex && g_ActiveConfig.UseGPUTextureDecoding() &&
!(texture_info.IsFromTmem() && texture_info.GetTextureFormat() == TextureFormat::RGBA8);
// create the entry/texture // create the entry/texture
const TextureConfig config(width, height, texLevels, 1, 1, const TextureConfig config(width, height, texLevels, 1, 1,
@ -1567,7 +1528,6 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
return nullptr; return nullptr;
ArbitraryMipmapDetector arbitrary_mip_detector; ArbitraryMipmapDetector arbitrary_mip_detector;
const u8* tlut = &texMem[tlutaddr];
if (hires_tex) if (hires_tex)
{ {
const auto& level = hires_tex->m_levels[0]; const auto& level = hires_tex->m_levels[0];
@ -1581,11 +1541,13 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
if (!hires_tex) if (!hires_tex)
{ {
if (!decode_on_gpu || if (!decode_on_gpu ||
!DecodeTextureOnGPU(entry, 0, src_data, texture_size, texformat, width, height, !DecodeTextureOnGPU(entry, 0, texture_info.GetData(), texture_info.GetTextureSize(),
expandedWidth, expandedHeight, bytes_per_block * (expandedWidth / bsw), texture_info.GetTextureFormat(), width, height, expanded_width,
tlut, tlutfmt)) 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 // Allocate memory for all levels at once
size_t total_texture_size = decoded_texture_size; size_t total_texture_size = decoded_texture_size;
@ -1594,7 +1556,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
size_t mip_downsample_buffer_size = decoded_texture_size * 5 / 16; size_t mip_downsample_buffer_size = decoded_texture_size * 5 / 16;
size_t prev_level_size = decoded_texture_size; 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; prev_level_size /= 4;
total_texture_size += prev_level_size; total_texture_size += prev_level_size;
@ -1605,35 +1567,39 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
CheckTempSize(total_texture_size); CheckTempSize(total_texture_size);
dst_buffer = temp; 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, TexDecoder_Decode(dst_buffer, texture_info.GetData(), expanded_width, expanded_height,
tlutfmt); texture_info.GetTextureFormat(), texture_info.GetTlutAddress(),
texture_info.GetTlutFormat());
} }
else else
{ {
u8* src_data_gb = &texMem[tmem_address_odd]; TexDecoder_DecodeRGBA8FromTmem(dst_buffer, texture_info.GetData(),
TexDecoder_DecodeRGBA8FromTmem(dst_buffer, src_data, src_data_gb, expandedWidth, texture_info.GetTmemOddAddress(), expanded_width,
expandedHeight); 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; dst_buffer += decoded_texture_size;
} }
} }
iter = textures_by_address.emplace(address, entry); iter = textures_by_address.emplace(texture_info.GetRawAddress(), entry);
if (textureCacheSafetyColorSampleSize == 0 || 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->textures_by_hash_iter = textures_by_hash.emplace(full_hash, entry);
} }
entry->SetGeneralParameters(address, texture_size, full_format, false); entry->SetGeneralParameters(texture_info.GetRawAddress(), texture_info.GetTextureSize(),
entry->SetDimensions(nativeW, nativeH, tex_levels); full_format, false);
entry->SetDimensions(texture_info.GetRawWidth(), texture_info.GetRawHeight(),
texture_info.GetLevelCount());
entry->SetHashes(base_hash, full_hash); entry->SetHashes(base_hash, full_hash);
entry->is_custom_tex = hires_tex != nullptr; entry->is_custom_tex = hires_tex != nullptr;
entry->memory_stride = entry->BytesPerRow(); entry->memory_stride = entry->BytesPerRow();
@ -1642,8 +1608,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
std::string basename; std::string basename;
if (g_ActiveConfig.bDumpTextures && !hires_tex) if (g_ActiveConfig.bDumpTextures && !hires_tex)
{ {
basename = HiresTexture::GenBaseName(src_data, texture_size, &texMem[tlutaddr], palette_size, basename = HiresTexture::GenBaseName(texture_info, true);
width, height, texformat, use_mipmaps, true);
} }
if (hires_tex) if (hires_tex)
@ -1657,46 +1622,34 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
} }
else 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) for (u32 level = 1; level != texLevels; ++level)
{ {
const u32 mip_width = CalculateLevelSize(width, level); auto mip_level = texture_info.GetMipMapLevel(level - 1);
const u32 mip_height = CalculateLevelSize(height, level); if (!mip_level)
const u32 expanded_mip_width = Common::AlignUp(mip_width, bsw); continue;
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);
if (!decode_on_gpu || if (!decode_on_gpu ||
!DecodeTextureOnGPU(entry, level, mip_src_data, mip_size, texformat, mip_width, !DecodeTextureOnGPU(
mip_height, expanded_mip_width, expanded_mip_height, entry, level, mip_level->GetData(), mip_level->GetTextureSize(),
bytes_per_block * (expanded_mip_width / bsw), tlut, tlutfmt)) 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 // 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; const u32 decoded_mip_size =
TexDecoder_Decode(dst_buffer, mip_src_data, expanded_mip_width, expanded_mip_height, mip_level->GetExpandedWidth() * sizeof(u32) * mip_level->GetExpandedHeight();
texformat, tlut, tlutfmt); TexDecoder_Decode(dst_buffer, mip_level->GetData(), mip_level->GetExpandedWidth(),
entry->texture->Load(level, mip_width, mip_height, expanded_mip_width, dst_buffer, mip_level->GetExpandedHeight(), texture_info.GetTextureFormat(),
decoded_mip_size); 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; dst_buffer += decoded_mip_size;
} }
mip_src_data += mip_size;
} }
} }
@ -1714,7 +1667,8 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo
INCSTAT(g_stats.num_textures_uploaded); INCSTAT(g_stats.num_textures_uploaded);
SETSTAT(g_stats.num_textures_alive, static_cast<int>(textures_by_address.size())); SETSTAT(g_stats.num_textures_alive, static_cast<int>(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. // This should only be needed if the texture was updated, or used GPU decoding.
entry->texture->FinishedRendering(); entry->texture->FinishedRendering();

View File

@ -21,6 +21,7 @@
#include "VideoCommon/BPMemory.h" #include "VideoCommon/BPMemory.h"
#include "VideoCommon/TextureConfig.h" #include "VideoCommon/TextureConfig.h"
#include "VideoCommon/TextureDecoder.h" #include "VideoCommon/TextureDecoder.h"
#include "VideoCommon/TextureInfo.h"
class AbstractFramebuffer; class AbstractFramebuffer;
class AbstractStagingTexture; class AbstractStagingTexture;
@ -216,11 +217,7 @@ public:
TCacheEntry* Load(const u32 stage); TCacheEntry* Load(const u32 stage);
static void InvalidateAllBindPoints() { valid_bind_points.reset(); } static void InvalidateAllBindPoints() { valid_bind_points.reset(); }
static bool IsValidBindPoint(u32 i) { return valid_bind_points.test(i); } static bool IsValidBindPoint(u32 i) { return valid_bind_points.test(i); }
TCacheEntry* GetTexture(u32 address, u32 width, u32 height, const TextureFormat texformat, TCacheEntry* GetTexture(const int textureCacheSafetyColorSampleSize, TextureInfo& texture_info);
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* GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, TCacheEntry* GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
MathUtil::Rectangle<int>* display_rect); MathUtil::Rectangle<int>* display_rect);
@ -286,11 +283,11 @@ private:
TCacheEntry* GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride); TCacheEntry* GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride);
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* 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); TLUTFormat tlutfmt);
void StitchXFBCopy(TCacheEntry* entry_to_update); void StitchXFBCopy(TCacheEntry* entry_to_update);

View File

@ -0,0 +1,310 @@
// Copyright 2021 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "VideoCommon/TextureInfo.h"
#include <xxhash.h>
#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<u32> 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<u32> 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<u32>(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<int>(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<u32> 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<u32>(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;
}

View File

@ -0,0 +1,117 @@
// Copyright 2021 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#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<u32> 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<u32> 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<MipLevel> m_mip_levels;
u32 m_texture_size = 0;
std::optional<u32> 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;
};