VideoCommon: move all texture calculations to a "TextureInfo" class. This ever so slightly improves readability and allows for the full texture name to be generated outside of the hires texture cache
This commit is contained in:
parent
1f26b694dc
commit
182dfc38e6
|
@ -642,6 +642,7 @@
|
|||
<ClInclude Include="VideoCommon\TextureConverterShaderGen.h" />
|
||||
<ClInclude Include="VideoCommon\TextureDecoder_Util.h" />
|
||||
<ClInclude Include="VideoCommon\TextureDecoder.h" />
|
||||
<ClInclude Include="VideoCommon\TextureInfo.h" />
|
||||
<ClInclude Include="VideoCommon\UberShaderCommon.h" />
|
||||
<ClInclude Include="VideoCommon\UberShaderPixel.h" />
|
||||
<ClInclude Include="VideoCommon\UberShaderVertex.h" />
|
||||
|
@ -1191,6 +1192,7 @@
|
|||
<ClCompile Include="VideoCommon\TextureConversionShader.cpp" />
|
||||
<ClCompile Include="VideoCommon\TextureConverterShaderGen.cpp" />
|
||||
<ClCompile Include="VideoCommon\TextureDecoder_Common.cpp" />
|
||||
<ClCompile Include="VideoCommon\TextureInfo.cpp" />
|
||||
<ClCompile Include="VideoCommon\UberShaderCommon.cpp" />
|
||||
<ClCompile Include="VideoCommon\UberShaderPixel.cpp" />
|
||||
<ClCompile Include="VideoCommon\UberShaderVertex.cpp" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<int>(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> 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> 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<std::mutex> lk(s_textureCacheMutex);
|
||||
|
||||
|
@ -320,7 +265,8 @@ std::shared_ptr<HiresTexture> HiresTexture::Search(const u8* texture, size_t tex
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -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<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);
|
||||
static std::shared_ptr<HiresTexture> 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);
|
||||
|
||||
|
|
|
@ -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<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);
|
||||
}
|
||||
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<HiresTexture> 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<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.
|
||||
entry->texture->FinishedRendering();
|
||||
|
|
|
@ -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<int>* 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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
Loading…
Reference in New Issue