From 946571b7595b6b24e02cf8622f2bd5935b5aa360 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 14 Jul 2019 15:24:12 +1000 Subject: TextureCache: Support reinterpreting formats for VRAM textures --- Source/Core/VideoCommon/TextureCacheBase.cpp | 99 ++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) (limited to 'Source/Core/VideoCommon/TextureCacheBase.cpp') diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index f21e8e0e0e..5fcf667514 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -311,6 +311,44 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, u8* palette, TLUTForma return decoded_entry; } +TextureCacheBase::TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEntry* existing_entry, + TextureFormat new_format) +{ + TextureConfig new_config = existing_entry->texture->GetConfig(); + new_config.levels = 1; + new_config.flags |= AbstractTextureFlag_RenderTarget; + + TCacheEntry* reinterpreted_entry = AllocateCacheEntry(new_config); + if (!reinterpreted_entry) + return nullptr; + + reinterpreted_entry->SetGeneralParameters(existing_entry->addr, existing_entry->size_in_bytes, + new_format, existing_entry->should_force_safe_hashing); + reinterpreted_entry->SetDimensions(existing_entry->native_width, existing_entry->native_height, + 1); + reinterpreted_entry->SetHashes(existing_entry->base_hash, existing_entry->hash); + reinterpreted_entry->frameCount = existing_entry->frameCount; + reinterpreted_entry->SetNotCopy(); + reinterpreted_entry->is_efb_copy = existing_entry->is_efb_copy; + reinterpreted_entry->may_have_overlapping_textures = + existing_entry->may_have_overlapping_textures; + + g_renderer->BeginUtilityDrawing(); + g_renderer->SetAndDiscardFramebuffer(reinterpreted_entry->framebuffer.get()); + g_renderer->SetViewportAndScissor(reinterpreted_entry->texture->GetRect()); + g_renderer->SetPipeline( + g_shader_cache->GetTextureReinterpretPipeline(existing_entry->format.texfmt, new_format)); + g_renderer->SetTexture(0, existing_entry->texture.get()); + g_renderer->SetSamplerState(1, RenderState::GetPointSamplerState()); + g_renderer->Draw(0, 3); + g_renderer->EndUtilityDrawing(); + reinterpreted_entry->texture->FinishedRendering(); + + textures_by_address.emplace(reinterpreted_entry->addr, reinterpreted_entry); + + return reinterpreted_entry; +} + void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* entry, u32 new_width, u32 new_height) { @@ -385,6 +423,18 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* pale { if (entry->hash == entry->CalculateHash()) { + // If the texture formats are not compatible or convertible, skip it. + if (!IsCompatibleTextureFormat(entry_to_update->format.texfmt, entry->format.texfmt)) + { + if (!CanReinterpretTextureOnGPU(entry_to_update->format.texfmt, entry->format.texfmt)) + { + ++iter.first; + continue; + } + + entry = ReinterpretEntry(entry, entry_to_update->format.texfmt); + } + if (isPaletteTexture) { TCacheEntry* decoded_entry = ApplyPaletteToEntry(entry, palette, tlutfmt); @@ -930,6 +980,7 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo TexAddrCache::iterator oldest_entry = iter; int temp_frameCount = 0x7fffffff; TexAddrCache::iterator unconverted_copy = textures_by_address.end(); + TexAddrCache::iterator unreinterpreted_copy = textures_by_address.end(); while (iter != iter_range.second) { @@ -958,10 +1009,38 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo (!isPaletteTexture || g_Config.backend_info.bSupportsPaletteConversion)) || IsPlayingBackFifologWithBrokenEFBCopies) { - // TODO: We should check format/width/height/levels for EFB copies. Checking - // format is complicated because EFB copy formats don't exactly match - // texture formats. I'm not sure what effect checking width/height/levels - // would have. + // The texture format in VRAM must match the format that the copy was created with. Some + // formats are inherently compatible, as the channel and bit layout is identical (e.g. + // I8/C8). Others have the same number of bits per texel, and can be reinterpreted on the + // 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)) + { + // Can we reinterpret this in VRAM? + if (CanReinterpretTextureOnGPU(entry->format.texfmt, texformat)) + { + // Delay the conversion until afterwards, it's possible this texture has already been + // converted. + unreinterpreted_copy = iter++; + continue; + } + else + { + // If the EFB copies are in a different format and are not reinterpretable, use the RAM + // copy. + ++iter; + continue; + } + } + else + { + // Prefer the already-converted copy. + unconverted_copy = textures_by_address.end(); + } + + // 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) return entry; @@ -1010,6 +1089,18 @@ TextureCacheBase::GetTexture(u32 address, u32 width, u32 height, const TextureFo ++iter; } + if (unreinterpreted_copy != textures_by_address.end()) + { + TCacheEntry* decoded_entry = ReinterpretEntry(unreinterpreted_copy->second, texformat); + + // It's possible to combine reinterpreted textures + palettes. + if (unreinterpreted_copy == unconverted_copy && decoded_entry) + decoded_entry = ApplyPaletteToEntry(decoded_entry, &texMem[tlutaddr], tlutfmt); + + if (decoded_entry) + return decoded_entry; + } + if (unconverted_copy != textures_by_address.end()) { TCacheEntry* decoded_entry = -- cgit v1.2.3