2013-04-18 03:09:55 +00:00
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
2010-10-19 22:24:27 +00:00
2014-05-03 02:47:04 +00:00
# include <algorithm>
2014-06-03 05:08:54 +00:00
# include <string>
2014-05-03 02:47:04 +00:00
2014-02-17 10:18:15 +00:00
# include "Common/FileUtil.h"
# include "Common/MemoryUtil.h"
2014-06-03 05:08:54 +00:00
# include "Common/StringUtil.h"
2010-10-19 22:24:27 +00:00
2014-02-17 10:18:15 +00:00
# include "Core/ConfigManager.h"
# include "Core/HW/Memmap.h"
2010-10-19 22:24:27 +00:00
2014-02-17 10:18:15 +00:00
# include "VideoCommon/Debugger.h"
# include "VideoCommon/HiresTextures.h"
# include "VideoCommon/RenderBase.h"
# include "VideoCommon/Statistics.h"
# include "VideoCommon/TextureCacheBase.h"
# include "VideoCommon/VideoConfig.h"
2010-10-19 22:24:27 +00:00
enum
{
TEXTURE_KILL_THRESHOLD = 200 ,
2014-06-11 23:04:42 +00:00
RENDER_TARGET_KILL_THRESHOLD = 3 ,
2010-10-19 22:24:27 +00:00
} ;
TextureCache * g_texture_cache ;
2014-03-09 20:14:26 +00:00
GC_ALIGNED16 ( u8 * TextureCache : : temp ) = nullptr ;
2012-05-13 13:38:56 +00:00
unsigned int TextureCache : : temp_size ;
2011-02-05 10:08:06 +00:00
2010-10-19 22:24:27 +00:00
TextureCache : : TexCache TextureCache : : textures ;
2014-06-11 23:04:42 +00:00
TextureCache : : RenderTargetPool TextureCache : : render_target_pool ;
2012-05-28 09:31:37 +00:00
TextureCache : : BackupConfig TextureCache : : backup_config ;
2014-07-08 13:58:25 +00:00
static bool invalidate_texture_cache_requested ;
2010-10-19 22:24:27 +00:00
TextureCache : : TCacheEntryBase : : ~ TCacheEntryBase ( )
{
}
TextureCache : : TextureCache ( )
{
2012-05-13 13:38:56 +00:00
temp_size = 2048 * 2048 * 4 ;
2011-02-05 10:08:06 +00:00
if ( ! temp )
2012-05-13 13:38:56 +00:00
temp = ( u8 * ) AllocateAlignedMemory ( temp_size , 16 ) ;
2013-04-24 13:21:54 +00:00
2010-10-19 22:24:27 +00:00
TexDecoder_SetTexFmtOverlayOptions ( g_ActiveConfig . bTexFmtOverlayEnable , g_ActiveConfig . bTexFmtOverlayCenter ) ;
2013-04-24 13:21:54 +00:00
2014-03-10 11:30:55 +00:00
if ( g_ActiveConfig . bHiresTextures & & ! g_ActiveConfig . bDumpTextures )
2014-03-12 19:33:41 +00:00
HiresTextures : : Init ( SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strUniqueID ) ;
2013-04-24 13:21:54 +00:00
2011-02-05 10:08:06 +00:00
SetHash64Function ( g_ActiveConfig . bHiresTextures | | g_ActiveConfig . bDumpTextures ) ;
2013-06-08 01:28:54 +00:00
invalidate_texture_cache_requested = false ;
}
void TextureCache : : RequestInvalidateTextureCache ( )
{
invalidate_texture_cache_requested = true ;
2010-10-19 22:24:27 +00:00
}
2012-05-28 09:37:14 +00:00
void TextureCache : : Invalidate ( )
2010-10-19 22:24:27 +00:00
{
2014-03-03 05:25:15 +00:00
for ( auto & tex : textures )
{
delete tex . second ;
}
2010-10-19 22:24:27 +00:00
textures . clear ( ) ;
2014-06-11 23:04:42 +00:00
for ( auto & rt : render_target_pool )
{
delete rt ;
}
render_target_pool . clear ( ) ;
2010-10-19 22:24:27 +00:00
}
TextureCache : : ~ TextureCache ( )
{
2012-06-27 18:19:04 +00:00
Invalidate ( ) ;
2014-02-23 22:03:39 +00:00
FreeAlignedMemory ( temp ) ;
2014-03-09 20:14:26 +00:00
temp = nullptr ;
2010-10-19 22:24:27 +00:00
}
2012-05-28 09:31:37 +00:00
void TextureCache : : OnConfigChanged ( VideoConfig & config )
{
2013-02-16 02:46:03 +00:00
if ( g_texture_cache )
2012-05-28 09:37:14 +00:00
{
2013-02-16 02:46:03 +00:00
// TODO: Invalidating texcache is really stupid in some of these cases
if ( config . iSafeTextureCache_ColorSamples ! = backup_config . s_colorsamples | |
config . bTexFmtOverlayEnable ! = backup_config . s_texfmt_overlay | |
config . bTexFmtOverlayCenter ! = backup_config . s_texfmt_overlay_center | |
2013-06-08 01:28:54 +00:00
config . bHiresTextures ! = backup_config . s_hires_textures | |
invalidate_texture_cache_requested )
2013-02-16 02:46:03 +00:00
{
g_texture_cache - > Invalidate ( ) ;
2012-05-28 09:37:14 +00:00
2014-03-10 11:30:55 +00:00
if ( g_ActiveConfig . bHiresTextures )
2014-03-12 19:33:41 +00:00
HiresTextures : : Init ( SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strUniqueID ) ;
2012-05-28 09:37:14 +00:00
2013-02-16 02:46:03 +00:00
SetHash64Function ( g_ActiveConfig . bHiresTextures | | g_ActiveConfig . bDumpTextures ) ;
TexDecoder_SetTexFmtOverlayOptions ( g_ActiveConfig . bTexFmtOverlayEnable , g_ActiveConfig . bTexFmtOverlayCenter ) ;
2013-06-08 01:28:54 +00:00
invalidate_texture_cache_requested = false ;
2013-02-16 02:46:03 +00:00
}
2012-05-28 09:31:37 +00:00
2013-02-16 02:46:03 +00:00
// TODO: Probably shouldn't clear all render targets here, just mark them dirty or something.
if ( config . bEFBCopyCacheEnable ! = backup_config . s_copy_cache_enable | | // TODO: not sure if this is needed?
config . bCopyEFBToTexture ! = backup_config . s_copy_efb_to_texture | |
config . bCopyEFBScaled ! = backup_config . s_copy_efb_scaled | |
config . bEFBCopyEnable ! = backup_config . s_copy_efb | |
config . iEFBScale ! = backup_config . s_efb_scale )
{
g_texture_cache - > ClearRenderTargets ( ) ;
}
2014-11-03 23:53:14 +00:00
2014-11-08 15:19:15 +00:00
if ( ( config . iStereoMode > 0 ) ! = backup_config . s_stereo_3d | |
2014-12-24 22:06:44 +00:00
config . bStereoEFBMonoDepth ! = backup_config . s_efb_mono_depth )
2014-11-03 23:53:14 +00:00
{
g_texture_cache - > DeleteShaders ( ) ;
g_texture_cache - > CompileShaders ( ) ;
}
2012-05-28 09:31:37 +00:00
}
2013-10-29 05:23:17 +00:00
2012-05-28 09:31:37 +00:00
backup_config . s_colorsamples = config . iSafeTextureCache_ColorSamples ;
backup_config . s_copy_efb_to_texture = config . bCopyEFBToTexture ;
backup_config . s_copy_efb_scaled = config . bCopyEFBScaled ;
backup_config . s_copy_efb = config . bEFBCopyEnable ;
backup_config . s_efb_scale = config . iEFBScale ;
backup_config . s_texfmt_overlay = config . bTexFmtOverlayEnable ;
backup_config . s_texfmt_overlay_center = config . bTexFmtOverlayCenter ;
backup_config . s_hires_textures = config . bHiresTextures ;
backup_config . s_copy_cache_enable = config . bEFBCopyCacheEnable ;
2014-11-03 23:53:14 +00:00
backup_config . s_stereo_3d = config . iStereoMode > 0 ;
2014-12-24 22:06:44 +00:00
backup_config . s_efb_mono_depth = config . bStereoEFBMonoDepth ;
2012-05-28 09:31:37 +00:00
}
2010-10-19 22:24:27 +00:00
void TextureCache : : Cleanup ( )
{
2011-12-26 17:45:22 +00:00
TexCache : : iterator iter = textures . begin ( ) ;
TexCache : : iterator tcend = textures . end ( ) ;
2010-10-19 22:24:27 +00:00
while ( iter ! = tcend )
{
2014-03-10 11:30:55 +00:00
if ( frameCount > TEXTURE_KILL_THRESHOLD + iter - > second - > frameCount & &
// EFB copies living on the host GPU are unrecoverable and thus shouldn't be deleted
! iter - > second - > IsEfbCopy ( ) )
2010-10-19 22:24:27 +00:00
{
2013-11-07 20:16:36 +00:00
delete iter - > second ;
2010-10-19 22:24:27 +00:00
textures . erase ( iter + + ) ;
}
else
2013-04-24 13:21:54 +00:00
{
2010-10-19 22:24:27 +00:00
+ + iter ;
2013-04-24 13:21:54 +00:00
}
2010-10-19 22:24:27 +00:00
}
2014-06-11 23:04:42 +00:00
for ( size_t i = 0 ; i < render_target_pool . size ( ) ; )
{
auto rt = render_target_pool [ i ] ;
if ( frameCount > RENDER_TARGET_KILL_THRESHOLD + rt - > frameCount )
{
delete rt ;
render_target_pool [ i ] = render_target_pool . back ( ) ;
render_target_pool . pop_back ( ) ;
}
else
{
+ + i ;
}
}
2010-10-19 22:24:27 +00:00
}
void TextureCache : : InvalidateRange ( u32 start_address , u32 size )
{
TexCache : : iterator
iter = textures . begin ( ) ,
tcend = textures . end ( ) ;
while ( iter ! = tcend )
{
const int rangePosition = iter - > second - > IntersectsMemoryRange ( start_address , size ) ;
if ( 0 = = rangePosition )
{
2013-11-07 20:16:36 +00:00
delete iter - > second ;
2010-10-19 22:24:27 +00:00
textures . erase ( iter + + ) ;
}
else
2013-04-24 13:21:54 +00:00
{
2010-10-19 22:24:27 +00:00
+ + iter ;
2013-04-24 13:21:54 +00:00
}
2010-10-19 22:24:27 +00:00
}
}
void TextureCache : : MakeRangeDynamic ( u32 start_address , u32 size )
{
TexCache : : iterator
2010-11-06 04:46:44 +00:00
iter = textures . lower_bound ( start_address ) ,
tcend = textures . upper_bound ( start_address + size ) ;
if ( iter ! = textures . begin ( ) )
2014-02-12 15:00:34 +00:00
- - iter ;
2010-11-06 04:46:44 +00:00
2010-10-19 22:24:27 +00:00
for ( ; iter ! = tcend ; + + iter )
{
const int rangePosition = iter - > second - > IntersectsMemoryRange ( start_address , size ) ;
if ( 0 = = rangePosition )
{
2011-12-26 17:05:01 +00:00
iter - > second - > SetHashes ( TEXHASH_INVALID ) ;
2010-10-19 22:24:27 +00:00
}
}
}
2010-11-06 04:46:44 +00:00
bool TextureCache : : Find ( u32 start_address , u64 hash )
{
2010-11-07 04:28:33 +00:00
TexCache : : iterator iter = textures . lower_bound ( start_address ) ;
2010-11-06 04:46:44 +00:00
if ( iter - > second - > hash = = hash )
return true ;
return false ;
}
2010-10-19 22:24:27 +00:00
int TextureCache : : TCacheEntryBase : : IntersectsMemoryRange ( u32 range_address , u32 range_size ) const
{
if ( addr + size_in_bytes < range_address )
return - 1 ;
if ( addr > = range_address + range_size )
return 1 ;
return 0 ;
}
void TextureCache : : ClearRenderTargets ( )
{
TexCache : : iterator
iter = textures . begin ( ) ,
tcend = textures . end ( ) ;
2012-03-20 21:36:21 +00:00
2013-01-09 04:46:30 +00:00
while ( iter ! = tcend )
{
2012-12-17 18:31:52 +00:00
if ( iter - > second - > type = = TCET_EC_VRAM )
{
2013-11-07 20:16:36 +00:00
delete iter - > second ;
2012-12-17 18:31:52 +00:00
textures . erase ( iter + + ) ;
}
2013-01-09 04:46:30 +00:00
else
2013-04-24 13:21:54 +00:00
{
2013-01-09 04:46:30 +00:00
+ + iter ;
2013-04-24 13:21:54 +00:00
}
2013-01-09 04:46:30 +00:00
}
2010-10-19 22:24:27 +00:00
}
2012-05-12 12:31:38 +00:00
bool TextureCache : : CheckForCustomTextureLODs ( u64 tex_hash , int texformat , unsigned int levels )
{
2012-08-10 11:09:16 +00:00
if ( levels = = 1 )
return false ;
2012-05-12 12:31:38 +00:00
// Just checking if the necessary files exist, if they can't be loaded or have incorrect dimensions LODs will be black
2014-06-03 05:08:54 +00:00
std : : string texBasePathTemp = StringFromFormat ( " %s_%08x_%i " , SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strUniqueID . c_str ( ) , ( u32 ) ( tex_hash & 0x00000000FFFFFFFFLL ) , texformat ) ;
2012-05-12 12:31:38 +00:00
for ( unsigned int level = 1 ; level < levels ; + + level )
{
2014-06-03 05:08:54 +00:00
std : : string texPathTemp = StringFromFormat ( " %s_mip%u " , texBasePathTemp . c_str ( ) , level ) ;
2012-05-12 12:31:38 +00:00
if ( ! HiresTextures : : HiresTexExists ( texPathTemp ) )
{
if ( level > 1 )
2014-06-03 05:08:54 +00:00
WARN_LOG ( VIDEO , " Couldn't find custom texture LOD with index %u (filename: %s), disabling custom LODs for this texture " , level , texPathTemp . c_str ( ) ) ;
2012-05-12 12:31:38 +00:00
return false ;
}
}
2014-06-03 05:08:54 +00:00
2012-05-12 12:31:38 +00:00
return true ;
}
2014-10-02 06:20:46 +00:00
PC_TexFormat TextureCache : : LoadCustomTexture ( u64 tex_hash , int texformat , unsigned int level , unsigned int * widthp , unsigned int * heightp )
2012-05-12 11:25:13 +00:00
{
2014-06-03 05:08:54 +00:00
std : : string texPathTemp ;
2012-05-12 11:25:13 +00:00
unsigned int newWidth = 0 ;
unsigned int newHeight = 0 ;
2014-01-03 13:30:12 +00:00
u32 tex_hash_u32 = tex_hash & 0x00000000FFFFFFFFLL ;
2012-05-12 11:25:13 +00:00
2012-05-12 12:31:38 +00:00
if ( level = = 0 )
2014-06-03 05:08:54 +00:00
texPathTemp = StringFromFormat ( " %s_%08x_%i " , SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strUniqueID . c_str ( ) , tex_hash_u32 , texformat ) ;
2012-05-12 12:31:38 +00:00
else
2014-06-03 05:08:54 +00:00
texPathTemp = StringFromFormat ( " %s_%08x_%i_mip%u " , SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strUniqueID . c_str ( ) , tex_hash_u32 , texformat , level ) ;
2012-05-12 12:31:38 +00:00
2012-05-13 13:38:56 +00:00
unsigned int required_size = 0 ;
PC_TexFormat ret = HiresTextures : : GetHiresTex ( texPathTemp , & newWidth , & newHeight , & required_size , texformat , temp_size , temp ) ;
if ( ret = = PC_TEX_FMT_NONE & & temp_size < required_size )
{
// Allocate more memory and try again
// TODO: Should probably check if newWidth and newHeight are texture dimensions which are actually supported by the current video backend
temp_size = required_size ;
FreeAlignedMemory ( temp ) ;
temp = ( u8 * ) AllocateAlignedMemory ( temp_size , 16 ) ;
ret = HiresTextures : : GetHiresTex ( texPathTemp , & newWidth , & newHeight , & required_size , texformat , temp_size , temp ) ;
}
2012-05-12 11:25:13 +00:00
if ( ret ! = PC_TEX_FMT_NONE )
{
2014-10-02 06:20:46 +00:00
unsigned int width = * widthp , height = * heightp ;
2014-01-03 13:30:12 +00:00
if ( level > 0 & & ( newWidth ! = width | | newHeight ! = height ) )
2014-06-03 05:08:54 +00:00
ERROR_LOG ( VIDEO , " Invalid custom texture size %dx%d for texture %s. This mipmap layer _must_ be %dx%d. " , newWidth , newHeight , texPathTemp . c_str ( ) , width , height ) ;
2014-01-03 13:30:12 +00:00
if ( newWidth * height ! = newHeight * width )
2014-06-03 05:08:54 +00:00
ERROR_LOG ( VIDEO , " Invalid custom texture size %dx%d for texture %s. The aspect differs from the native size %dx%d. " , newWidth , newHeight , texPathTemp . c_str ( ) , width , height ) ;
2014-01-03 13:30:12 +00:00
if ( newWidth % width | | newHeight % height )
2014-06-03 05:08:54 +00:00
WARN_LOG ( VIDEO , " Invalid custom texture size %dx%d for texture %s. Please use an integer upscaling factor based on the native size %dx%d. " , newWidth , newHeight , texPathTemp . c_str ( ) , width , height ) ;
2014-01-03 13:30:12 +00:00
2014-10-02 06:20:46 +00:00
* widthp = newWidth ;
* heightp = newHeight ;
2012-05-12 11:25:13 +00:00
}
return ret ;
}
2012-05-12 11:50:03 +00:00
void TextureCache : : DumpTexture ( TCacheEntryBase * entry , unsigned int level )
2012-05-12 11:31:09 +00:00
{
2013-11-16 22:12:07 +00:00
std : : string filename ;
2012-05-12 11:31:09 +00:00
std : : string szDir = File : : GetUserPath ( D_DUMPTEXTURES_IDX ) +
SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strUniqueID ;
// make sure that the directory exists
2014-03-12 19:33:41 +00:00
if ( ! File : : Exists ( szDir ) | | ! File : : IsDirectory ( szDir ) )
File : : CreateDir ( szDir ) ;
2012-05-12 11:31:09 +00:00
2012-05-12 11:50:03 +00:00
// For compatibility with old texture packs, don't print the LOD index for level 0.
// TODO: TLUT format should actually be stored in filename? :/
if ( level = = 0 )
{
2013-11-16 22:12:07 +00:00
filename = StringFromFormat ( " %s/%s_%08x_%i.png " , szDir . c_str ( ) ,
SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strUniqueID . c_str ( ) ,
( u32 ) ( entry - > hash & 0x00000000FFFFFFFFLL ) , entry - > format & 0xFFFF ) ;
2012-05-12 11:50:03 +00:00
}
else
{
2013-11-16 22:12:07 +00:00
filename = StringFromFormat ( " %s/%s_%08x_%i_mip%i.png " , szDir . c_str ( ) ,
2012-05-12 11:50:03 +00:00
SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strUniqueID . c_str ( ) ,
( u32 ) ( entry - > hash & 0x00000000FFFFFFFFLL ) , entry - > format & 0xFFFF , level ) ;
}
2012-05-12 11:31:09 +00:00
2013-11-16 22:12:07 +00:00
if ( ! File : : Exists ( filename ) )
entry - > Save ( filename , level ) ;
2012-05-12 11:31:09 +00:00
}
2013-02-16 02:46:03 +00:00
static u32 CalculateLevelSize ( u32 level_0_size , u32 level )
{
return ( level_0_size + ( ( 1 < < level ) - 1 ) ) > > level ;
}
// Used by TextureCache::Load
static TextureCache : : TCacheEntryBase * ReturnEntry ( unsigned int stage , TextureCache : : TCacheEntryBase * entry )
{
entry - > frameCount = frameCount ;
entry - > Bind ( stage ) ;
GFX_DEBUGGER_PAUSE_AT ( NEXT_TEXTURE_CHANGE , true ) ;
return entry ;
}
TextureCache : : TCacheEntryBase * TextureCache : : Load ( unsigned int const stage ,
u32 const address , unsigned int width , unsigned int height , int const texformat ,
2013-02-18 16:14:56 +00:00
unsigned int const tlutaddr , int const tlutfmt , bool const use_mipmaps , unsigned int maxlevel , bool const from_tmem )
2010-10-19 22:24:27 +00:00
{
if ( 0 = = address )
2014-03-09 20:14:26 +00:00
return nullptr ;
2010-10-19 22:24:27 +00:00
2013-02-16 02:46:03 +00:00
// TexelSizeInNibbles(format) * width * height / 16;
2010-10-19 22:24:27 +00:00
const unsigned int bsw = TexDecoder_GetBlockWidthInTexels ( texformat ) - 1 ;
const unsigned int bsh = TexDecoder_GetBlockHeightInTexels ( texformat ) - 1 ;
unsigned int expandedWidth = ( width + bsw ) & ( ~ bsw ) ;
unsigned int expandedHeight = ( height + bsh ) & ( ~ bsh ) ;
2010-10-20 00:12:41 +00:00
const unsigned int nativeW = width ;
const unsigned int nativeH = height ;
2010-10-19 22:24:27 +00:00
u32 texID = address ;
2013-02-16 02:46:03 +00:00
// Hash assigned to texcache entry (also used to generate filenames used for texture dumping and custom texture lookup)
u64 tex_hash = TEXHASH_INVALID ;
2011-12-26 20:37:18 +00:00
u64 tlut_hash = TEXHASH_INVALID ;
2010-10-19 22:24:27 +00:00
u32 full_format = texformat ;
2010-10-20 00:12:41 +00:00
PC_TexFormat pcfmt = PC_TEX_FMT_NONE ;
2010-10-19 22:24:27 +00:00
2011-02-05 10:08:06 +00:00
const bool isPaletteTexture = ( texformat = = GX_TF_C4 | | texformat = = GX_TF_C8 | | texformat = = GX_TF_C14X2 ) ;
if ( isPaletteTexture )
2010-10-19 22:24:27 +00:00
full_format = texformat | ( tlutfmt < < 16 ) ;
2011-12-26 22:14:12 +00:00
const u32 texture_size = TexDecoder_GetTextureSizeInBytes ( expandedWidth , expandedHeight , texformat ) ;
2013-02-18 16:14:56 +00:00
2013-02-16 02:46:03 +00:00
const u8 * src_data ;
if ( from_tmem )
src_data = & texMem [ bpmem . tex [ stage / 4 ] . texImage1 [ stage % 4 ] . tmem_even * TMEM_LINE_SIZE ] ;
else
src_data = Memory : : GetPointer ( address ) ;
2011-12-26 22:14:12 +00:00
2013-01-10 14:12:21 +00:00
// 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)
2012-01-29 20:49:50 +00:00
tex_hash = GetHash64 ( src_data , texture_size , g_ActiveConfig . iSafeTextureCache_ColorSamples ) ;
2011-12-26 21:04:59 +00:00
if ( isPaletteTexture )
2010-10-19 22:24:27 +00:00
{
2011-12-26 20:11:31 +00:00
const u32 palette_size = TexDecoder_GetPaletteSize ( texformat ) ;
tlut_hash = GetHash64 ( & texMem [ tlutaddr ] , palette_size , g_ActiveConfig . iSafeTextureCache_ColorSamples ) ;
// NOTE: For non-paletted textures, texID is equal to the texture address.
2014-02-17 04:51:41 +00:00
// A paletted texture, however, may have multiple texIDs assigned though depending on the currently used tlut.
// This (changing texID depending on the tlut_hash) is a trick to get around
// an issue with Metroid Prime's fonts (it has multiple sets of fonts on each other
// stored in a single texture and uses the palette to make different characters
// visible or invisible. Thus, unless we want to recreate the textures for every drawn character,
// we must make sure that a paletted texture gets assigned multiple IDs for each tlut used.
2011-12-26 20:11:31 +00:00
//
// TODO: Because texID isn't always the same as the address now, CopyRenderTargetToTexture might be broken now
2011-12-26 21:04:59 +00:00
texID ^ = ( ( u32 ) tlut_hash ) ^ ( u32 ) ( tlut_hash > > 32 ) ;
2012-01-24 14:25:01 +00:00
tex_hash ^ = tlut_hash ;
2011-12-26 20:11:31 +00:00
}
2010-10-24 15:16:31 +00:00
2013-02-18 16:14:56 +00:00
// D3D doesn'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,1x1, so we limit the mipmap count to 6 there
2014-11-13 05:42:34 +00:00
while ( g_ActiveConfig . backend_info . bUseMinimalMipCount & & std : : max ( width , height ) > > maxlevel = = 0 )
2013-02-18 16:14:56 +00:00
- - maxlevel ;
2012-02-04 12:01:52 +00:00
TCacheEntryBase * entry = textures [ texID ] ;
2011-12-26 20:11:31 +00:00
if ( entry )
{
2011-12-26 21:04:59 +00:00
// 1. Calculate reference hash:
// calculated from RAM texture data for normal textures. Hashes for paletted textures are modified by tlut_hash. 0 for virtual EFB copies.
2011-12-26 23:05:26 +00:00
if ( g_ActiveConfig . bCopyEFBToTexture & & entry - > IsEfbCopy ( ) )
2012-01-24 14:25:01 +00:00
tex_hash = TEXHASH_INVALID ;
2010-10-19 22:24:27 +00:00
2011-12-26 18:45:13 +00:00
// 2. a) For EFB copies, only the hash and the texture address need to match
2012-01-24 14:25:01 +00:00
if ( entry - > IsEfbCopy ( ) & & tex_hash = = entry - > hash & & address = = entry - > addr )
2010-10-19 22:24:27 +00:00
{
2012-06-03 11:03:20 +00:00
entry - > type = TCET_EC_VRAM ;
2012-03-20 21:36:21 +00:00
2013-02-16 02:46:03 +00:00
// TODO: Print a warning if the format changes! In this case,
// we could reinterpret the internal texture object data to the new pixel format
2013-04-19 13:21:45 +00:00
// (similar to what is already being done in Renderer::ReinterpretPixelFormat())
2013-02-16 02:46:03 +00:00
return ReturnEntry ( stage , entry ) ;
2010-10-19 22:24:27 +00:00
}
2011-12-26 18:45:13 +00:00
// 2. b) For normal textures, all texture parameters need to match
2012-01-24 14:25:01 +00:00
if ( address = = entry - > addr & & tex_hash = = entry - > hash & & full_format = = entry - > format & &
2012-08-10 10:36:19 +00:00
entry - > num_mipmaps > maxlevel & & entry - > native_width = = nativeW & & entry - > native_height = = nativeH )
2011-12-26 18:45:13 +00:00
{
2013-02-16 02:46:03 +00:00
return ReturnEntry ( stage , entry ) ;
2011-12-26 18:45:13 +00:00
}
// 3. If we reach this line, we'll have to upload the new texture data to VRAM.
// If we're lucky, the texture parameters didn't change and we can reuse the internal texture object instead of destroying and recreating it.
2011-12-26 23:05:26 +00:00
//
2011-12-26 18:45:13 +00:00
// TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies?
// TODO: Actually, it should be enough if the internal texture format matches...
2014-10-28 13:47:13 +00:00
if ( ( ( entry - > type = = TCET_NORMAL & &
2014-03-12 19:33:41 +00:00
width = = entry - > virtual_width & &
height = = entry - > virtual_height & &
2014-03-10 11:30:55 +00:00
full_format = = entry - > format & &
entry - > num_mipmaps > maxlevel ) | |
( entry - > type = = TCET_EC_DYNAMIC & &
entry - > native_width = = width & &
2014-10-28 13:47:13 +00:00
entry - > native_height = = height ) ) & &
2014-10-30 14:13:31 +00:00
entry - > num_layers = = 1 )
2011-12-26 18:45:13 +00:00
{
// reuse the texture
}
2010-10-19 22:24:27 +00:00
else
{
2013-11-07 20:16:36 +00:00
// delete the texture and make a new one
delete entry ;
2014-03-09 20:14:26 +00:00
entry = nullptr ;
2010-10-19 22:24:27 +00:00
}
}
2011-12-26 18:17:04 +00:00
2013-02-16 02:46:03 +00:00
bool using_custom_texture = false ;
2012-05-12 11:25:13 +00:00
2010-10-19 22:24:27 +00:00
if ( g_ActiveConfig . bHiresTextures )
{
2014-10-02 06:20:46 +00:00
pcfmt = LoadCustomTexture ( tex_hash , texformat , 0 , & width , & height ) ;
2010-10-19 22:24:27 +00:00
if ( pcfmt ! = PC_TEX_FMT_NONE )
{
2012-12-19 15:55:44 +00:00
if ( expandedWidth ! = width | | expandedHeight ! = height )
{
expandedWidth = width ;
expandedHeight = height ;
2013-11-04 22:35:19 +00:00
// If we thought we could reuse the texture before, make sure to pool it now!
2014-03-10 11:30:55 +00:00
if ( entry )
2013-11-07 14:54:16 +00:00
{
2013-11-07 20:16:36 +00:00
delete entry ;
2014-03-09 20:14:26 +00:00
entry = nullptr ;
2013-11-07 14:54:16 +00:00
}
2012-12-19 15:55:44 +00:00
}
2012-05-12 12:31:38 +00:00
using_custom_texture = true ;
2010-10-19 22:24:27 +00:00
}
}
2012-12-19 15:55:44 +00:00
if ( ! using_custom_texture )
2013-01-10 14:12:21 +00:00
{
if ( ! ( texformat = = GX_TF_RGBA8 & & from_tmem ) )
{
2014-08-10 19:28:42 +00:00
const u8 * tlut = & texMem [ tlutaddr ] ;
pcfmt = TexDecoder_Decode ( temp , src_data , expandedWidth , expandedHeight , texformat , tlut , ( TlutFormat ) tlutfmt ) ;
2013-01-10 14:12:21 +00:00
}
else
{
u8 * src_data_gb = & texMem [ bpmem . tex [ stage / 4 ] . texImage2 [ stage % 4 ] . tmem_odd * TMEM_LINE_SIZE ] ;
pcfmt = TexDecoder_DecodeRGBA8FromTmem ( temp , src_data , src_data_gb , expandedWidth , expandedHeight ) ;
}
}
2010-10-19 22:24:27 +00:00
2013-02-16 02:46:03 +00:00
u32 texLevels = use_mipmaps ? ( maxlevel + 1 ) : 1 ;
const bool using_custom_lods = using_custom_texture & & CheckForCustomTextureLODs ( tex_hash , texformat , texLevels ) ;
// Only load native mips if their dimensions fit to our virtual texture dimensions
const bool use_native_mips = use_mipmaps & & ! using_custom_lods & & ( width = = nativeW & & height = = nativeH ) ;
2013-02-18 16:14:56 +00:00
texLevels = ( use_native_mips | | using_custom_lods ) ? texLevels : 1 ; // TODO: Should be forced to 1 for non-pow2 textures (e.g. efb copies with automatically adjusted IR)
2010-10-19 22:24:27 +00:00
// create the entry/texture
2014-03-09 20:14:26 +00:00
if ( nullptr = = entry )
2013-02-16 02:46:03 +00:00
{
2010-10-19 22:24:27 +00:00
textures [ texID ] = entry = g_texture_cache - > CreateTexture ( width , height , expandedWidth , texLevels , pcfmt ) ;
2012-01-29 20:49:50 +00:00
// Sometimes, we can get around recreating a texture if only the number of mip levels changes
2010-10-24 15:16:31 +00:00
// e.g. if our texture cache entry got too many mipmap levels we can limit the number of used levels by setting the appropriate render states
// Thus, we don't update this member for every Load, but just whenever the texture gets recreated
2013-10-06 11:37:10 +00:00
2013-02-16 02:46:03 +00:00
// TODO: This is the wrong value. We should be storing the number of levels our actual texture has.
// But that will currently make the above "existing entry" tests fail as "texLevels" is not calculated until after.
// Currently, we might try to reuse a texture which appears to have more levels than actual, maybe..
2012-08-10 11:09:16 +00:00
entry - > num_mipmaps = maxlevel + 1 ;
2014-10-28 13:47:13 +00:00
entry - > num_layers = 1 ;
2012-01-29 20:49:50 +00:00
entry - > type = TCET_NORMAL ;
2010-12-05 14:15:36 +00:00
GFX_DEBUGGER_PAUSE_AT ( NEXT_NEW_TEXTURE , true ) ;
2010-10-24 15:16:31 +00:00
}
2013-02-20 11:37:01 +00:00
else
{
// load texture (CreateTexture also loads level 0)
entry - > Load ( width , height , expandedWidth , 0 ) ;
}
2010-10-19 22:24:27 +00:00
2014-11-06 10:41:39 +00:00
entry - > SetGeneralParameters ( address , texture_size , full_format , entry - > num_mipmaps , entry - > num_layers ) ;
2011-12-26 17:05:01 +00:00
entry - > SetDimensions ( nativeW , nativeH , width , height ) ;
2012-01-24 14:25:01 +00:00
entry - > hash = tex_hash ;
2013-10-29 05:23:17 +00:00
2013-02-20 11:37:01 +00:00
if ( entry - > IsEfbCopy ( ) & & ! g_ActiveConfig . bCopyEFBToTexture )
entry - > type = TCET_EC_DYNAMIC ;
else
entry - > type = TCET_NORMAL ;
2010-10-19 22:24:27 +00:00
2012-05-12 12:31:38 +00:00
if ( g_ActiveConfig . bDumpTextures & & ! using_custom_texture )
2012-05-12 11:50:03 +00:00
DumpTexture ( entry , 0 ) ;
2013-02-16 02:46:03 +00:00
u32 level = 1 ;
2012-01-29 20:49:50 +00:00
// load mips - TODO: Loading mipmaps from tmem is untested!
2013-02-16 02:46:03 +00:00
if ( pcfmt ! = PC_TEX_FMT_NONE )
2010-10-19 22:24:27 +00:00
{
2013-02-16 02:46:03 +00:00
if ( use_native_mips )
2012-01-29 20:49:50 +00:00
{
2013-02-16 02:46:03 +00:00
src_data + = texture_size ;
2013-10-29 05:23:17 +00:00
2014-03-09 20:14:26 +00:00
const u8 * ptr_even = nullptr ;
const u8 * ptr_odd = nullptr ;
2013-02-16 02:46:03 +00:00
if ( from_tmem )
{
ptr_even = & texMem [ bpmem . tex [ stage / 4 ] . texImage1 [ stage % 4 ] . tmem_even * TMEM_LINE_SIZE + texture_size ] ;
ptr_odd = & texMem [ bpmem . tex [ stage / 4 ] . texImage2 [ stage % 4 ] . tmem_odd * TMEM_LINE_SIZE ] ;
}
2012-05-12 11:50:03 +00:00
2013-02-16 02:46:03 +00:00
for ( ; level ! = texLevels ; + + level )
{
const u32 mip_width = CalculateLevelSize ( width , level ) ;
const u32 mip_height = CalculateLevelSize ( height , level ) ;
const u32 expanded_mip_width = ( mip_width + bsw ) & ( ~ bsw ) ;
const u32 expanded_mip_height = ( mip_height + bsh ) & ( ~ bsh ) ;
2013-10-29 05:23:17 +00:00
2013-02-16 02:46:03 +00:00
const u8 * & mip_src_data = from_tmem
? ( ( level % 2 ) ? ptr_odd : ptr_even )
: src_data ;
2014-08-10 19:28:42 +00:00
const u8 * tlut = & texMem [ tlutaddr ] ;
TexDecoder_Decode ( temp , mip_src_data , expanded_mip_width , expanded_mip_height , texformat , tlut , ( TlutFormat ) tlutfmt ) ;
2013-02-16 02:46:03 +00:00
mip_src_data + = TexDecoder_GetTextureSizeInBytes ( expanded_mip_width , expanded_mip_height , texformat ) ;
2013-10-29 05:23:17 +00:00
2013-02-16 02:46:03 +00:00
entry - > Load ( mip_width , mip_height , expanded_mip_width , level ) ;
if ( g_ActiveConfig . bDumpTextures )
DumpTexture ( entry , level ) ;
}
2010-10-19 22:24:27 +00:00
}
2013-02-16 02:46:03 +00:00
else if ( using_custom_lods )
2012-05-12 12:31:38 +00:00
{
2013-02-16 02:46:03 +00:00
for ( ; level ! = texLevels ; + + level )
{
unsigned int mip_width = CalculateLevelSize ( width , level ) ;
unsigned int mip_height = CalculateLevelSize ( height , level ) ;
2012-05-12 12:31:38 +00:00
2014-10-02 06:20:46 +00:00
LoadCustomTexture ( tex_hash , texformat , level , & mip_width , & mip_height ) ;
2013-02-16 02:46:03 +00:00
entry - > Load ( mip_width , mip_height , mip_width , level ) ;
}
2012-05-12 12:31:38 +00:00
}
}
2010-10-19 22:24:27 +00:00
INCSTAT ( stats . numTexturesCreated ) ;
SETSTAT ( stats . numTexturesAlive , textures . size ( ) ) ;
2013-02-16 02:46:03 +00:00
return ReturnEntry ( stage , entry ) ;
2010-10-19 22:24:27 +00:00
}
2014-03-23 20:44:23 +00:00
void TextureCache : : CopyRenderTargetToTexture ( u32 dstAddr , unsigned int dstFormat , PEControl : : PixelFormat srcFormat ,
2011-02-26 23:41:02 +00:00
const EFBRectangle & srcRect , bool isIntensity , bool scaleByHalf )
2010-10-19 22:24:27 +00:00
{
2011-12-26 22:14:12 +00:00
// Emulation methods:
2013-10-29 05:23:17 +00:00
//
2011-12-26 22:14:12 +00:00
// - EFB to RAM:
2014-02-17 04:51:41 +00:00
// Encodes the requested EFB data at its native resolution to the emulated RAM using shaders.
// Load() decodes the data from there again (using TextureDecoder) if the EFB copy is being used as a texture again.
// Advantage: CPU can read data from the EFB copy and we don't lose any important updates to the texture
// Disadvantage: Encoding+decoding steps often are redundant because only some games read or modify EFB copies before using them as textures.
2013-10-29 05:23:17 +00:00
//
2011-12-26 22:14:12 +00:00
// - EFB to texture:
2014-02-17 04:51:41 +00:00
// Copies the requested EFB data to a texture object in VRAM, performing any color conversion using shaders.
// Advantage: Works for many games, since in most cases EFB copies aren't read or modified at all before being used as a texture again.
// Since we don't do any further encoding or decoding here, this method is much faster.
// It also allows enhancing the visual quality by doing scaled EFB copies.
2013-10-29 05:23:17 +00:00
//
2013-04-24 13:21:54 +00:00
// - Hybrid EFB copies:
2014-02-17 04:51:41 +00:00
// 1a) Whenever this function gets called, encode the requested EFB data to RAM (like EFB to RAM)
// 1b) Set type to TCET_EC_DYNAMIC for all texture cache entries in the destination address range.
// If EFB copy caching is enabled, further checks will (try to) prevent redundant EFB copies.
// 2) Check if a texture cache entry for the specified dstAddr already exists (i.e. if an EFB copy was triggered to that address before):
// 2a) Entry doesn't exist:
// - Also copy the requested EFB data to a texture object in VRAM (like EFB to texture)
// - Create a texture cache entry for the target (type = TCET_EC_VRAM)
// - Store a hash of the encoded RAM data in the texcache entry.
// 2b) Entry exists AND type is TCET_EC_VRAM:
// - Like case 2a, but reuse the old texcache entry instead of creating a new one.
// 2c) Entry exists AND type is TCET_EC_DYNAMIC:
// - Only encode the texture to RAM (like EFB to RAM) and store a hash of the encoded data in the existing texcache entry.
// - Do NOT copy the requested EFB data to a VRAM object. Reason: the texture is dynamic, i.e. the CPU is modifying it. Storing a VRAM copy is useless, because we'd always end up deleting it and reloading the data from RAM anyway.
// 3) If the EFB copy gets used as a texture, compare the source RAM hash with the hash you stored when encoding the EFB data to RAM.
// 3a) If the two hashes match AND type is TCET_EC_VRAM, reuse the VRAM copy you created
// 3b) If the two hashes differ AND type is TCET_EC_VRAM, screw your existing VRAM copy. Set type to TCET_EC_DYNAMIC.
// Redecode the source RAM data to a VRAM object. The entry basically behaves like a normal texture now.
// 3c) If type is TCET_EC_DYNAMIC, treat the EFB copy like a normal texture.
// Advantage: Non-dynamic EFB copies can be visually enhanced like with EFB to texture.
// Compatibility is as good as EFB to RAM.
// Disadvantage: Slower than EFB to texture and often even slower than EFB to RAM.
// EFB copy cache depends on accurate texture hashing being enabled. However, with accurate hashing you end up being as slow as without a copy cache anyway.
2011-12-26 22:14:12 +00:00
//
// Disadvantage of all methods: Calling this function requires the GPU to perform a pipeline flush which stalls any further CPU processing.
2012-01-24 15:38:27 +00:00
//
// For historical reasons, Dolphin doesn't actually implement "pure" EFB to RAM emulation, but only EFB to texture and hybrid EFB copies.
2011-12-26 22:14:12 +00:00
2011-01-07 19:23:57 +00:00
float colmat [ 28 ] = { 0 } ;
2010-10-19 22:24:27 +00:00
float * const fConstAdd = colmat + 16 ;
2011-01-07 19:23:57 +00:00
float * const ColorMask = colmat + 20 ;
2011-12-10 15:08:26 +00:00
ColorMask [ 0 ] = ColorMask [ 1 ] = ColorMask [ 2 ] = ColorMask [ 3 ] = 255.0f ;
2011-01-07 19:23:57 +00:00
ColorMask [ 4 ] = ColorMask [ 5 ] = ColorMask [ 6 ] = ColorMask [ 7 ] = 1.0f / 255.0f ;
2010-10-19 22:24:27 +00:00
unsigned int cbufid = - 1 ;
2014-03-23 20:44:23 +00:00
bool efbHasAlpha = bpmem . zcontrol . pixel_format = = PEControl : : RGBA6_Z24 ;
2010-10-19 22:24:27 +00:00
2014-03-23 20:44:23 +00:00
if ( srcFormat = = PEControl : : Z24 )
2010-10-19 22:24:27 +00:00
{
2011-02-26 23:41:02 +00:00
switch ( dstFormat )
2010-10-19 22:24:27 +00:00
{
2011-12-10 15:08:26 +00:00
case 0 : // Z4
2011-01-07 19:23:57 +00:00
colmat [ 3 ] = colmat [ 7 ] = colmat [ 11 ] = colmat [ 15 ] = 1.0f ;
cbufid = 0 ;
break ;
case 1 : // Z8
case 8 : // Z8
colmat [ 0 ] = colmat [ 4 ] = colmat [ 8 ] = colmat [ 12 ] = 1.0f ;
cbufid = 1 ;
break ;
2011-12-10 15:08:26 +00:00
case 3 : // Z16
2011-01-07 19:23:57 +00:00
colmat [ 1 ] = colmat [ 5 ] = colmat [ 9 ] = colmat [ 12 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 2 ;
2011-01-07 19:23:57 +00:00
break ;
case 11 : // Z16 (reverse order)
colmat [ 0 ] = colmat [ 4 ] = colmat [ 8 ] = colmat [ 13 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 3 ;
2011-01-07 19:23:57 +00:00
break ;
case 6 : // Z24X8
colmat [ 0 ] = colmat [ 5 ] = colmat [ 10 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 4 ;
2011-01-07 19:23:57 +00:00
break ;
case 9 : // Z8M
colmat [ 1 ] = colmat [ 5 ] = colmat [ 9 ] = colmat [ 13 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 5 ;
2011-01-07 19:23:57 +00:00
break ;
case 10 : // Z8L
colmat [ 2 ] = colmat [ 6 ] = colmat [ 10 ] = colmat [ 14 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 6 ;
2011-01-07 19:23:57 +00:00
break ;
2011-12-26 17:05:01 +00:00
case 12 : // Z16L - copy lower 16 depth bits
// expected to be used as an IA8 texture (upper 8 bits stored as intensity, lower 8 bits stored as alpha)
// Used e.g. in Zelda: Skyward Sword
2011-12-10 15:08:26 +00:00
colmat [ 1 ] = colmat [ 5 ] = colmat [ 9 ] = colmat [ 14 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 7 ;
2011-01-07 19:23:57 +00:00
break ;
default :
2011-02-26 23:41:02 +00:00
ERROR_LOG ( VIDEO , " Unknown copy zbuf format: 0x%x " , dstFormat ) ;
2011-01-07 19:23:57 +00:00
colmat [ 2 ] = colmat [ 5 ] = colmat [ 8 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 8 ;
2011-01-07 19:23:57 +00:00
break ;
2010-10-19 22:24:27 +00:00
}
}
2013-10-29 05:23:17 +00:00
else if ( isIntensity )
2010-10-19 22:24:27 +00:00
{
fConstAdd [ 0 ] = fConstAdd [ 1 ] = fConstAdd [ 2 ] = 16.0f / 255.0f ;
2013-10-29 05:23:17 +00:00
switch ( dstFormat )
2010-10-19 22:24:27 +00:00
{
case 0 : // I4
case 1 : // I8
case 2 : // IA4
case 3 : // IA8
2010-12-18 18:23:22 +00:00
case 8 : // I8
2010-10-19 22:24:27 +00:00
// TODO - verify these coefficients
colmat [ 0 ] = 0.257f ; colmat [ 1 ] = 0.504f ; colmat [ 2 ] = 0.098f ;
colmat [ 4 ] = 0.257f ; colmat [ 5 ] = 0.504f ; colmat [ 6 ] = 0.098f ;
colmat [ 8 ] = 0.257f ; colmat [ 9 ] = 0.504f ; colmat [ 10 ] = 0.098f ;
2013-10-29 05:23:17 +00:00
if ( dstFormat < 2 | | dstFormat = = 8 )
2010-10-19 22:24:27 +00:00
{
colmat [ 12 ] = 0.257f ; colmat [ 13 ] = 0.504f ; colmat [ 14 ] = 0.098f ;
2011-01-07 19:23:57 +00:00
fConstAdd [ 3 ] = 16.0f / 255.0f ;
2011-02-26 23:41:02 +00:00
if ( dstFormat = = 0 )
2011-01-07 19:23:57 +00:00
{
ColorMask [ 0 ] = ColorMask [ 1 ] = ColorMask [ 2 ] = 15.0f ;
ColorMask [ 4 ] = ColorMask [ 5 ] = ColorMask [ 6 ] = 1.0f / 15.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 9 ;
2011-01-07 19:23:57 +00:00
}
else
{
2013-11-24 03:43:54 +00:00
cbufid = 10 ;
2013-04-24 13:21:54 +00:00
}
2010-10-19 22:24:27 +00:00
}
else // alpha
{
colmat [ 15 ] = 1 ;
2011-02-26 23:41:02 +00:00
if ( dstFormat = = 2 )
2011-01-07 19:23:57 +00:00
{
ColorMask [ 0 ] = ColorMask [ 1 ] = ColorMask [ 2 ] = ColorMask [ 3 ] = 15.0f ;
ColorMask [ 4 ] = ColorMask [ 5 ] = ColorMask [ 6 ] = ColorMask [ 7 ] = 1.0f / 15.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 11 ;
2011-01-07 19:23:57 +00:00
}
else
{
2013-11-24 03:43:54 +00:00
cbufid = 12 ;
2011-01-07 19:23:57 +00:00
}
2013-10-29 05:23:17 +00:00
2010-10-19 22:24:27 +00:00
}
break ;
default :
2011-02-26 23:41:02 +00:00
ERROR_LOG ( VIDEO , " Unknown copy intensity format: 0x%x " , dstFormat ) ;
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 5 ] = colmat [ 10 ] = colmat [ 15 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 13 ;
2010-10-19 22:24:27 +00:00
break ;
}
}
else
{
2013-10-29 05:23:17 +00:00
switch ( dstFormat )
2010-10-19 22:24:27 +00:00
{
case 0 : // R4
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 4 ] = colmat [ 8 ] = colmat [ 12 ] = 1 ;
ColorMask [ 0 ] = 15.0f ;
ColorMask [ 4 ] = 1.0f / 15.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 14 ;
2011-01-07 19:23:57 +00:00
break ;
2010-12-18 18:23:22 +00:00
case 1 : // R8
2010-10-19 22:24:27 +00:00
case 8 : // R8
colmat [ 0 ] = colmat [ 4 ] = colmat [ 8 ] = colmat [ 12 ] = 1 ;
2013-11-24 03:43:54 +00:00
cbufid = 15 ;
2010-10-19 22:24:27 +00:00
break ;
case 2 : // RA4
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 4 ] = colmat [ 8 ] = colmat [ 15 ] = 1.0f ;
ColorMask [ 0 ] = ColorMask [ 3 ] = 15.0f ;
ColorMask [ 4 ] = ColorMask [ 7 ] = 1.0f / 15.0f ;
2014-03-29 10:05:44 +00:00
2013-11-24 03:43:54 +00:00
cbufid = 16 ;
2014-08-30 20:51:27 +00:00
if ( ! efbHasAlpha )
{
2013-11-06 21:19:37 +00:00
ColorMask [ 3 ] = 0.0f ;
fConstAdd [ 3 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 17 ;
2013-11-06 21:19:37 +00:00
}
2011-01-07 19:23:57 +00:00
break ;
2010-10-19 22:24:27 +00:00
case 3 : // RA8
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 4 ] = colmat [ 8 ] = colmat [ 15 ] = 1.0f ;
2014-03-29 10:05:44 +00:00
2013-11-24 03:43:54 +00:00
cbufid = 18 ;
2014-08-30 20:51:27 +00:00
if ( ! efbHasAlpha )
{
2013-11-06 21:19:37 +00:00
ColorMask [ 3 ] = 0.0f ;
fConstAdd [ 3 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 19 ;
2013-11-06 21:19:37 +00:00
}
2010-10-19 22:24:27 +00:00
break ;
case 7 : // A8
2011-01-07 19:23:57 +00:00
colmat [ 3 ] = colmat [ 7 ] = colmat [ 11 ] = colmat [ 15 ] = 1.0f ;
2014-03-29 10:05:44 +00:00
2013-11-24 03:43:54 +00:00
cbufid = 20 ;
2014-08-30 20:51:27 +00:00
if ( ! efbHasAlpha )
{
2013-11-06 21:19:37 +00:00
ColorMask [ 3 ] = 0.0f ;
fConstAdd [ 0 ] = 1.0f ;
fConstAdd [ 1 ] = 1.0f ;
fConstAdd [ 2 ] = 1.0f ;
fConstAdd [ 3 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 21 ;
2013-11-06 21:19:37 +00:00
}
2010-10-19 22:24:27 +00:00
break ;
case 9 : // G8
2011-01-07 19:23:57 +00:00
colmat [ 1 ] = colmat [ 5 ] = colmat [ 9 ] = colmat [ 13 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 22 ;
2010-10-19 22:24:27 +00:00
break ;
case 10 : // B8
2011-01-07 19:23:57 +00:00
colmat [ 2 ] = colmat [ 6 ] = colmat [ 10 ] = colmat [ 14 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 23 ;
2010-10-19 22:24:27 +00:00
break ;
case 11 : // RG8
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 4 ] = colmat [ 8 ] = colmat [ 13 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 24 ;
2010-10-19 22:24:27 +00:00
break ;
case 12 : // GB8
2013-03-20 01:51:12 +00:00
colmat [ 1 ] = colmat [ 5 ] = colmat [ 9 ] = colmat [ 14 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 25 ;
2010-10-19 22:24:27 +00:00
break ;
case 4 : // RGB565
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 5 ] = colmat [ 10 ] = 1.0f ;
ColorMask [ 0 ] = ColorMask [ 2 ] = 31.0f ;
ColorMask [ 4 ] = ColorMask [ 6 ] = 1.0f / 31.0f ;
ColorMask [ 1 ] = 63.0f ;
ColorMask [ 5 ] = 1.0f / 63.0f ;
fConstAdd [ 3 ] = 1.0f ; // set alpha to 1
2013-11-24 03:43:54 +00:00
cbufid = 26 ;
2010-10-19 22:24:27 +00:00
break ;
case 5 : // RGB5A3
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 5 ] = colmat [ 10 ] = colmat [ 15 ] = 1.0f ;
ColorMask [ 0 ] = ColorMask [ 1 ] = ColorMask [ 2 ] = 31.0f ;
ColorMask [ 4 ] = ColorMask [ 5 ] = ColorMask [ 6 ] = 1.0f / 31.0f ;
ColorMask [ 3 ] = 7.0f ;
ColorMask [ 7 ] = 1.0f / 7.0f ;
2014-03-29 10:05:44 +00:00
2013-11-24 03:43:54 +00:00
cbufid = 27 ;
2014-08-30 20:51:27 +00:00
if ( ! efbHasAlpha )
{
2013-11-06 21:19:37 +00:00
ColorMask [ 3 ] = 0.0f ;
fConstAdd [ 3 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 28 ;
2013-11-06 21:19:37 +00:00
}
2011-01-07 19:23:57 +00:00
break ;
2010-10-19 22:24:27 +00:00
case 6 : // RGBA8
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 5 ] = colmat [ 10 ] = colmat [ 15 ] = 1.0f ;
2014-03-29 10:05:44 +00:00
2013-11-24 03:43:54 +00:00
cbufid = 29 ;
2014-08-30 20:51:27 +00:00
if ( ! efbHasAlpha )
{
2013-11-06 21:19:37 +00:00
ColorMask [ 3 ] = 0.0f ;
fConstAdd [ 3 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 30 ;
2013-11-06 21:19:37 +00:00
}
2010-10-19 22:24:27 +00:00
break ;
default :
2011-02-26 23:41:02 +00:00
ERROR_LOG ( VIDEO , " Unknown copy color format: 0x%x " , dstFormat ) ;
2011-01-07 19:23:57 +00:00
colmat [ 0 ] = colmat [ 5 ] = colmat [ 10 ] = colmat [ 15 ] = 1.0f ;
2013-11-24 03:43:54 +00:00
cbufid = 31 ;
2010-10-19 22:24:27 +00:00
break ;
}
}
2011-02-26 23:41:02 +00:00
const unsigned int tex_w = scaleByHalf ? srcRect . GetWidth ( ) / 2 : srcRect . GetWidth ( ) ;
const unsigned int tex_h = scaleByHalf ? srcRect . GetHeight ( ) / 2 : srcRect . GetHeight ( ) ;
2010-10-19 22:24:27 +00:00
2010-12-10 15:54:14 +00:00
unsigned int scaled_tex_w = g_ActiveConfig . bCopyEFBScaled ? Renderer : : EFBToScaledX ( tex_w ) : tex_w ;
unsigned int scaled_tex_h = g_ActiveConfig . bCopyEFBScaled ? Renderer : : EFBToScaledY ( tex_h ) : tex_h ;
2010-10-19 22:24:27 +00:00
2014-11-29 15:53:19 +00:00
const unsigned int efb_layers = FramebufferManagerBase : : GetEFBLayers ( ) ;
2010-10-19 22:24:27 +00:00
2011-02-26 23:41:02 +00:00
TCacheEntryBase * entry = textures [ dstAddr ] ;
2010-10-19 22:24:27 +00:00
if ( entry )
{
2014-11-29 15:53:19 +00:00
if ( entry - > type = = TCET_EC_DYNAMIC & & entry - > native_width = = tex_w & & entry - > native_height = = tex_h & & entry - > num_layers = = efb_layers )
2010-10-19 22:24:27 +00:00
{
2012-06-03 11:02:11 +00:00
scaled_tex_w = tex_w ;
scaled_tex_h = tex_h ;
2010-10-19 22:24:27 +00:00
}
2014-11-29 15:53:19 +00:00
else if ( ! ( entry - > type = = TCET_EC_VRAM & & entry - > virtual_width = = scaled_tex_w & & entry - > virtual_height = = scaled_tex_h & & entry - > num_layers = = efb_layers ) )
2010-10-19 22:24:27 +00:00
{
2014-06-11 23:04:42 +00:00
if ( entry - > type = = TCET_EC_VRAM )
{
// try to re-use this render target later
FreeRenderTarget ( entry ) ;
}
else
{
// remove it and recreate it as a render target
delete entry ;
}
2014-03-09 20:14:26 +00:00
entry = nullptr ;
2010-10-19 22:24:27 +00:00
}
}
2014-03-09 20:14:26 +00:00
if ( nullptr = = entry )
2010-10-19 22:24:27 +00:00
{
2013-11-07 20:16:36 +00:00
// create the texture
2014-06-11 23:04:42 +00:00
textures [ dstAddr ] = entry = AllocateRenderTarget ( scaled_tex_w , scaled_tex_h ) ;
2013-11-07 20:16:36 +00:00
2011-12-26 17:05:01 +00:00
// TODO: Using the wrong dstFormat, dumb...
2014-11-29 15:53:19 +00:00
entry - > SetGeneralParameters ( dstAddr , 0 , dstFormat , 1 , efb_layers ) ;
2011-12-26 17:05:01 +00:00
entry - > SetDimensions ( tex_w , tex_h , scaled_tex_w , scaled_tex_h ) ;
entry - > SetHashes ( TEXHASH_INVALID ) ;
2012-01-29 19:24:23 +00:00
entry - > type = TCET_EC_VRAM ;
2010-10-19 22:24:27 +00:00
}
entry - > frameCount = frameCount ;
2011-02-26 23:41:02 +00:00
entry - > FromRenderTarget ( dstAddr , dstFormat , srcFormat , srcRect , isIntensity , scaleByHalf , cbufid , colmat ) ;
2010-12-27 22:17:19 +00:00
}
2014-06-11 23:04:42 +00:00
TextureCache : : TCacheEntryBase * TextureCache : : AllocateRenderTarget ( unsigned int width , unsigned int height )
{
for ( size_t i = 0 ; i < render_target_pool . size ( ) ; + + i )
{
auto rt = render_target_pool [ i ] ;
if ( rt - > virtual_width ! = width | | rt - > virtual_height ! = height )
continue ;
render_target_pool [ i ] = render_target_pool . back ( ) ;
render_target_pool . pop_back ( ) ;
return rt ;
}
return g_texture_cache - > CreateRenderTargetTexture ( width , height ) ;
}
void TextureCache : : FreeRenderTarget ( TCacheEntryBase * entry )
{
render_target_pool . push_back ( entry ) ;
}