2013-08-17 11:23:51 +02:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
# include <map>
# include <algorithm>
2013-12-29 15:55:09 -08:00
# include <cstring>
2013-08-17 11:23:51 +02:00
# include "Core/MemMap.h"
# include "Core/Reporting.h"
# include "GPU/ge_constants.h"
# include "GPU/GPUState.h"
2014-09-09 00:53:01 -07:00
# include "GPU/Directx9/PixelShaderGeneratorDX9.h"
2013-09-15 12:46:14 +02:00
# include "GPU/Directx9/TextureCacheDX9.h"
# include "GPU/Directx9/FramebufferDX9.h"
2014-09-17 23:26:20 +02:00
# include "GPU/Directx9/ShaderManagerDX9.h"
# include "GPU/Directx9/DepalettizeShaderDX9.h"
2014-08-23 01:52:46 +02:00
# include "GPU/Directx9/helper/dx_state.h"
2014-09-09 08:12:42 -07:00
# include "GPU/Common/FramebufferCommon.h"
2013-09-15 21:27:13 -07:00
# include "GPU/Common/TextureDecoder.h"
2013-08-17 11:23:51 +02:00
# include "Core/Config.h"
2013-12-29 15:55:09 -08:00
# include "Core/Host.h"
2013-08-17 11:23:51 +02:00
2013-08-19 20:36:43 +02:00
# include "ext/xxhash.h"
2013-09-15 12:46:14 +02:00
# include "math/math_util.h"
2013-08-17 11:23:51 +02:00
2014-08-23 01:52:46 +02:00
2013-09-15 23:22:10 -07:00
extern int g_iNumVideos ;
2013-09-15 08:53:21 -07:00
namespace DX9 {
2013-08-19 20:36:43 +02:00
# define INVALID_TEX (LPDIRECT3DTEXTURE9)(-1)
2013-08-17 11:23:51 +02:00
// If a texture hasn't been seen for this many frames, get rid of it.
# define TEXTURE_KILL_AGE 200
# define TEXTURE_KILL_AGE_LOWMEM 60
// Not used in lowmem mode.
# define TEXTURE_SECOND_KILL_AGE 100
2013-08-19 20:36:43 +02:00
// Try to be prime to other decimation intervals.
# define TEXCACHE_DECIMATION_INTERVAL 13
2014-09-09 00:53:01 -07:00
// Changes more frequent than this will be considered "frequent" and prevent texture scaling.
# define TEXCACHE_FRAME_CHANGE_FREQUENT 6
2015-12-30 16:54:25 -08:00
// Note: only used when hash backoff is disabled.
# define TEXCACHE_FRAME_CHANGE_FREQUENT_REGAIN_TRUST 33
2014-09-09 00:53:01 -07:00
# define TEXCACHE_MAX_TEXELS_SCALED (256*256) // Per frame
2014-12-21 16:54:26 -08:00
# define TEXCACHE_MIN_PRESSURE 16 * 1024 * 1024 // Total in VRAM
# define TEXCACHE_SECOND_MIN_PRESSURE 4 * 1024 * 1024
2015-11-28 12:41:37 -08:00
TextureCacheDX9 : : TextureCacheDX9 ( ) : cacheSizeEstimate_ ( 0 ) , secondCacheSizeEstimate_ ( 0 ) , clearCacheNextFrame_ ( false ) , lowMemoryMode_ ( false ) , clutBuf_ ( NULL ) , texelsScaledThisFrame_ ( 0 ) {
2014-09-09 00:53:01 -07:00
timesInvalidatedAllThisFrame_ = 0 ;
2013-08-19 20:36:43 +02:00
lastBoundTexture = INVALID_TEX ;
decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL ;
2014-09-09 00:53:01 -07:00
2014-09-20 22:39:01 -07:00
D3DCAPS9 pCaps ;
ZeroMemory ( & pCaps , sizeof ( pCaps ) ) ;
HRESULT result = 0 ;
if ( pD3DdeviceEx ) {
result = pD3DdeviceEx - > GetDeviceCaps ( & pCaps ) ;
} else {
result = pD3Ddevice - > GetDeviceCaps ( & pCaps ) ;
}
if ( FAILED ( result ) ) {
WARN_LOG ( G3D , " Failed to get the device caps! " ) ;
maxAnisotropyLevel = 16 ;
} else {
maxAnisotropyLevel = pCaps . MaxAnisotropy ;
}
2014-03-22 21:35:16 -07:00
SetupTextureDecoder ( ) ;
2015-03-15 20:05:35 -07:00
nextTexture_ = nullptr ;
2013-08-17 11:23:51 +02:00
}
2013-09-15 12:46:14 +02:00
TextureCacheDX9 : : ~ TextureCacheDX9 ( ) {
2015-01-24 20:36:01 -08:00
Clear ( true ) ;
2013-08-17 11:23:51 +02:00
}
2014-12-21 16:54:26 -08:00
static u32 EstimateTexMemoryUsage ( const TextureCacheDX9 : : TexCacheEntry * entry ) {
const u16 dim = entry - > dim ;
const u8 dimW = ( ( dim > > 0 ) & 0xf ) ;
const u8 dimH = ( ( dim > > 8 ) & 0xf ) ;
u32 pixelSize = 2 ;
switch ( entry - > format ) {
case GE_TFMT_CLUT4 :
case GE_TFMT_CLUT8 :
case GE_TFMT_CLUT16 :
case GE_TFMT_CLUT32 :
// We assume cluts always point to 8888 for simplicity.
pixelSize = 4 ;
break ;
case GE_TFMT_4444 :
case GE_TFMT_5551 :
case GE_TFMT_5650 :
break ;
case GE_TFMT_8888 :
case GE_TFMT_DXT1 :
case GE_TFMT_DXT3 :
case GE_TFMT_DXT5 :
default :
pixelSize = 4 ;
break ;
}
// This in other words multiplies by w and h.
return pixelSize < < ( dimW + dimH ) ;
}
2013-09-15 12:46:14 +02:00
void TextureCacheDX9 : : Clear ( bool delete_them ) {
2013-08-17 11:23:51 +02:00
pD3Ddevice - > SetTexture ( 0 , NULL ) ;
2013-08-19 20:36:43 +02:00
lastBoundTexture = INVALID_TEX ;
2013-08-17 11:23:51 +02:00
if ( delete_them ) {
for ( TexCache : : iterator iter = cache . begin ( ) ; iter ! = cache . end ( ) ; + + iter ) {
2015-03-15 19:46:36 -07:00
DEBUG_LOG ( G3D , " Deleting texture %p " , iter - > second . texturePtr ) ;
ReleaseTexture ( & iter - > second ) ;
2013-08-17 11:23:51 +02:00
}
for ( TexCache : : iterator iter = secondCache . begin ( ) ; iter ! = secondCache . end ( ) ; + + iter ) {
2015-03-15 19:46:36 -07:00
DEBUG_LOG ( G3D , " Deleting texture %p " , iter - > second . texturePtr ) ;
ReleaseTexture ( & iter - > second ) ;
2013-08-17 11:23:51 +02:00
}
}
if ( cache . size ( ) + secondCache . size ( ) ) {
INFO_LOG ( G3D , " Texture cached cleared from %i textures " , ( int ) ( cache . size ( ) + secondCache . size ( ) ) ) ;
cache . clear ( ) ;
secondCache . clear ( ) ;
2014-12-21 16:54:26 -08:00
cacheSizeEstimate_ = 0 ;
secondCacheSizeEstimate_ = 0 ;
2013-08-17 11:23:51 +02:00
}
2014-09-09 00:53:01 -07:00
fbTexInfo_ . clear ( ) ;
}
void TextureCacheDX9 : : DeleteTexture ( TexCache : : iterator it ) {
2015-03-15 19:46:36 -07:00
ReleaseTexture ( & it - > second ) ;
2014-09-09 00:53:01 -07:00
auto fbInfo = fbTexInfo_ . find ( it - > second . addr ) ;
if ( fbInfo ! = fbTexInfo_ . end ( ) ) {
fbTexInfo_ . erase ( fbInfo ) ;
}
2014-12-21 16:54:26 -08:00
cacheSizeEstimate_ - = EstimateTexMemoryUsage ( & it - > second ) ;
2014-09-09 00:53:01 -07:00
cache . erase ( it ) ;
}
void TextureCacheDX9 : : ForgetLastTexture ( ) {
lastBoundTexture = INVALID_TEX ;
gstate_c . textureChanged | = TEXCHANGE_PARAMSONLY ;
2013-08-17 11:23:51 +02:00
}
// Removes old textures.
2013-09-15 12:46:14 +02:00
void TextureCacheDX9 : : Decimate ( ) {
2013-08-19 20:36:43 +02:00
if ( - - decimationCounter_ < = 0 ) {
decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL ;
} else {
return ;
}
2014-12-21 16:54:26 -08:00
if ( cacheSizeEstimate_ > = TEXCACHE_MIN_PRESSURE ) {
const u32 had = cacheSizeEstimate_ ;
pD3Ddevice - > SetTexture ( 0 , NULL ) ;
lastBoundTexture = INVALID_TEX ;
int killAge = lowMemoryMode_ ? TEXTURE_KILL_AGE_LOWMEM : TEXTURE_KILL_AGE ;
for ( TexCache : : iterator iter = cache . begin ( ) ; iter ! = cache . end ( ) ; ) {
if ( iter - > second . lastFrame + killAge < gpuStats . numFlips ) {
DeleteTexture ( iter + + ) ;
} else {
+ + iter ;
}
2013-12-29 15:10:07 -08:00
}
2014-12-21 16:54:26 -08:00
VERBOSE_LOG ( G3D , " Decimated texture cache, saved %d estimated bytes - now %d bytes " , had - cacheSizeEstimate_ , cacheSizeEstimate_ ) ;
2013-08-17 11:23:51 +02:00
}
2013-12-29 15:10:07 -08:00
2014-12-21 16:54:26 -08:00
if ( g_Config . bTextureSecondaryCache & & secondCacheSizeEstimate_ > = TEXCACHE_SECOND_MIN_PRESSURE ) {
const u32 had = secondCacheSizeEstimate_ ;
2013-12-29 15:10:07 -08:00
for ( TexCache : : iterator iter = secondCache . begin ( ) ; iter ! = secondCache . end ( ) ; ) {
// In low memory mode, we kill them all.
if ( lowMemoryMode_ | | iter - > second . lastFrame + TEXTURE_KILL_AGE < gpuStats . numFlips ) {
2015-03-15 19:46:36 -07:00
ReleaseTexture ( & iter - > second ) ;
2013-12-29 15:10:07 -08:00
secondCache . erase ( iter + + ) ;
} else {
+ + iter ;
}
2013-08-17 11:23:51 +02:00
}
2014-12-21 16:54:26 -08:00
VERBOSE_LOG ( G3D , " Decimated second texture cache, saved %d estimated bytes - now %d bytes " , had - secondCacheSizeEstimate_ , secondCacheSizeEstimate_ ) ;
2013-08-17 11:23:51 +02:00
}
}
2013-09-15 12:46:14 +02:00
void TextureCacheDX9 : : Invalidate ( u32 addr , int size , GPUInvalidationType type ) {
2013-12-29 15:10:07 -08:00
// If we're hashing every use, without backoff, then this isn't needed.
if ( ! g_Config . bTextureBackoffCache ) {
return ;
}
2013-08-24 13:24:19 +02:00
addr & = 0x0FFFFFFF ;
2013-08-17 11:23:51 +02:00
u32 addr_end = addr + size ;
// They could invalidate inside the texture, let's just give a bit of leeway.
const int LARGEST_TEXTURE_SIZE = 512 * 512 * 4 ;
2014-09-09 00:53:01 -07:00
const u64 startKey = ( u64 ) ( addr - LARGEST_TEXTURE_SIZE ) < < 32 ;
u64 endKey = ( u64 ) ( addr + size + LARGEST_TEXTURE_SIZE ) < < 32 ;
if ( endKey < startKey ) {
endKey = ( u64 ) - 1 ;
}
2013-08-17 11:23:51 +02:00
for ( TexCache : : iterator iter = cache . lower_bound ( startKey ) , end = cache . upper_bound ( endKey ) ; iter ! = end ; + + iter ) {
u32 texAddr = iter - > second . addr ;
u32 texEnd = iter - > second . addr + iter - > second . sizeInRAM ;
if ( texAddr < addr_end & & addr < texEnd ) {
2014-09-09 00:53:01 -07:00
if ( iter - > second . GetHashStatus ( ) = = TexCacheEntry : : STATUS_RELIABLE ) {
iter - > second . SetHashStatus ( TexCacheEntry : : STATUS_HASHING ) ;
2013-08-17 11:23:51 +02:00
}
if ( type ! = GPU_INVALIDATE_ALL ) {
gpuStats . numTextureInvalidations + + ;
// Start it over from 0 (unless it's safe.)
iter - > second . numFrames = type = = GPU_INVALIDATE_SAFE ? 256 : 0 ;
2015-12-30 16:54:25 -08:00
if ( type = = GPU_INVALIDATE_SAFE ) {
u32 diff = gpuStats . numFlips - iter - > second . lastFrame ;
// We still need to mark if the texture is frequently changing, even if it's safely changing.
if ( diff < TEXCACHE_FRAME_CHANGE_FREQUENT ) {
iter - > second . status | = TexCacheEntry : : STATUS_CHANGE_FREQUENT ;
}
}
2013-08-17 11:23:51 +02:00
iter - > second . framesUntilNextFullHash = 0 ;
2013-12-29 15:10:07 -08:00
} else if ( ! iter - > second . framebuffer ) {
2013-08-17 11:23:51 +02:00
iter - > second . invalidHint + + ;
}
}
}
}
2013-09-15 12:46:14 +02:00
void TextureCacheDX9 : : InvalidateAll ( GPUInvalidationType /*unused*/ ) {
2013-12-29 15:10:07 -08:00
// If we're hashing every use, without backoff, then this isn't needed.
if ( ! g_Config . bTextureBackoffCache ) {
return ;
}
2014-09-09 00:53:01 -07:00
if ( timesInvalidatedAllThisFrame_ > 5 ) {
return ;
}
timesInvalidatedAllThisFrame_ + + ;
2013-08-17 11:23:51 +02:00
for ( TexCache : : iterator iter = cache . begin ( ) , end = cache . end ( ) ; iter ! = end ; + + iter ) {
2014-09-09 00:53:01 -07:00
if ( iter - > second . GetHashStatus ( ) = = TexCacheEntry : : STATUS_RELIABLE ) {
iter - > second . SetHashStatus ( TexCacheEntry : : STATUS_HASHING ) ;
2013-08-17 11:23:51 +02:00
}
2013-12-29 15:10:07 -08:00
if ( ! iter - > second . framebuffer ) {
iter - > second . invalidHint + + ;
}
2013-08-17 11:23:51 +02:00
}
}
2013-09-15 12:46:14 +02:00
void TextureCacheDX9 : : ClearNextFrame ( ) {
2013-08-17 11:23:51 +02:00
clearCacheNextFrame_ = true ;
}
2014-09-09 08:12:42 -07:00
void TextureCacheDX9 : : AttachFramebufferValid ( TexCacheEntry * entry , VirtualFramebuffer * framebuffer , const AttachedFramebufferInfo & fbInfo ) {
2014-12-21 16:54:26 -08:00
const bool hasInvalidFramebuffer = entry - > framebuffer = = nullptr | | entry - > invalidHint = = - 1 ;
const bool hasOlderFramebuffer = entry - > framebuffer ! = nullptr & & entry - > framebuffer - > last_frame_render < framebuffer - > last_frame_render ;
2014-09-09 00:53:01 -07:00
bool hasFartherFramebuffer = false ;
if ( ! hasInvalidFramebuffer & & ! hasOlderFramebuffer ) {
// If it's valid, but the offset is greater, then we still win.
if ( fbTexInfo_ [ entry - > addr ] . yOffset = = fbInfo . yOffset )
hasFartherFramebuffer = fbTexInfo_ [ entry - > addr ] . xOffset > fbInfo . xOffset ;
else
hasFartherFramebuffer = fbTexInfo_ [ entry - > addr ] . yOffset > fbInfo . yOffset ;
}
if ( hasInvalidFramebuffer | | hasOlderFramebuffer | | hasFartherFramebuffer ) {
2014-12-21 16:54:26 -08:00
if ( entry - > framebuffer = = nullptr ) {
cacheSizeEstimate_ - = EstimateTexMemoryUsage ( entry ) ;
}
2013-09-22 00:18:46 -07:00
entry - > framebuffer = framebuffer ;
entry - > invalidHint = 0 ;
2014-09-09 00:53:01 -07:00
entry - > status & = ~ TextureCacheDX9 : : TexCacheEntry : : STATUS_DEPALETTIZE ;
fbTexInfo_ [ entry - > addr ] = fbInfo ;
framebuffer - > last_frame_attached = gpuStats . numFlips ;
2013-09-22 00:18:46 -07:00
host - > GPUNotifyTextureAttachment ( entry - > addr ) ;
2014-09-09 00:53:01 -07:00
} else if ( entry - > framebuffer = = framebuffer ) {
framebuffer - > last_frame_attached = gpuStats . numFlips ;
2013-09-22 00:18:46 -07:00
}
2013-09-10 22:35:38 +02:00
}
2013-08-17 11:23:51 +02:00
2014-09-09 08:12:42 -07:00
void TextureCacheDX9 : : AttachFramebufferInvalid ( TexCacheEntry * entry , VirtualFramebuffer * framebuffer , const AttachedFramebufferInfo & fbInfo ) {
2013-08-19 20:36:43 +02:00
if ( entry - > framebuffer = = 0 | | entry - > framebuffer = = framebuffer ) {
2014-12-21 16:54:26 -08:00
if ( entry - > framebuffer = = nullptr ) {
cacheSizeEstimate_ - = EstimateTexMemoryUsage ( entry ) ;
}
2013-08-19 20:36:43 +02:00
entry - > framebuffer = framebuffer ;
entry - > invalidHint = - 1 ;
2014-09-09 00:53:01 -07:00
entry - > status & = ~ TextureCacheDX9 : : TexCacheEntry : : STATUS_DEPALETTIZE ;
fbTexInfo_ [ entry - > addr ] = fbInfo ;
2013-09-22 00:18:46 -07:00
host - > GPUNotifyTextureAttachment ( entry - > addr ) ;
2013-08-19 20:36:43 +02:00
}
}
2013-08-17 11:23:51 +02:00
2014-09-09 08:12:42 -07:00
bool TextureCacheDX9 : : AttachFramebuffer ( TexCacheEntry * entry , u32 address , VirtualFramebuffer * framebuffer , u32 texaddrOffset ) {
2014-09-09 00:53:01 -07:00
static const u32 MAX_SUBAREA_Y_OFFSET_SAFE = 32 ;
AttachedFramebufferInfo fbInfo = { 0 } ;
const u64 mirrorMask = 0x00600000 ;
// Must be in VRAM so | 0x04000000 it is. Also, ignore memory mirrors.
const u32 addr = ( address | 0x04000000 ) & 0x3FFFFFFF & ~ mirrorMask ;
const u32 texaddr = ( ( entry - > addr + texaddrOffset ) & ~ mirrorMask ) ;
const bool noOffset = texaddr = = addr ;
const bool exactMatch = noOffset & & entry - > format < 4 ;
const u32 h = 1 < < ( ( entry - > dim > > 8 ) & 0xf ) ;
// 512 on a 272 framebuffer is sane, so let's be lenient.
const u32 minSubareaHeight = h / 4 ;
2013-11-15 15:15:12 +01:00
// If they match exactly, it's non-CLUT and from the top left.
2013-08-19 20:36:43 +02:00
if ( exactMatch ) {
2013-11-15 15:15:12 +01:00
// Apply to non-buffered and buffered mode only.
if ( ! ( g_Config . iRenderingMode = = FB_NON_BUFFERED_MODE | | g_Config . iRenderingMode = = FB_BUFFERED_MODE ) )
2014-09-09 00:53:01 -07:00
return false ;
2013-11-15 15:15:12 +01:00
2013-09-10 22:35:38 +02:00
DEBUG_LOG ( G3D , " Render to texture detected at %08x! " , address ) ;
2014-09-09 00:53:01 -07:00
if ( framebuffer - > fb_stride ! = entry - > bufw ) {
WARN_LOG_REPORT_ONCE ( diffStrides1 , G3D , " Render to texture with different strides %d != %d " , entry - > bufw , framebuffer - > fb_stride ) ;
}
if ( entry - > format ! = framebuffer - > format ) {
WARN_LOG_REPORT_ONCE ( diffFormat1 , G3D , " Render to texture with different formats %d != %d " , entry - > format , framebuffer - > format ) ;
// Let's avoid using it when we know the format is wrong. May be a video/etc. updating memory.
// However, some games use a different format to clear the buffer.
if ( framebuffer - > last_frame_attached + 1 < gpuStats . numFlips ) {
DetachFramebuffer ( entry , address , framebuffer ) ;
2013-08-24 13:24:19 +02:00
}
2014-09-09 00:53:01 -07:00
} else {
AttachFramebufferValid ( entry , framebuffer , fbInfo ) ;
return true ;
2013-11-15 15:15:12 +01:00
}
} else {
// Apply to buffered mode only.
if ( ! ( g_Config . iRenderingMode = = FB_BUFFERED_MODE ) )
2014-09-09 00:53:01 -07:00
return false ;
const bool clutFormat =
( framebuffer - > format = = GE_FORMAT_8888 & & entry - > format = = GE_TFMT_CLUT32 ) | |
( framebuffer - > format ! = GE_FORMAT_8888 & & entry - > format = = GE_TFMT_CLUT16 ) ;
2013-11-15 15:15:12 +01:00
2014-09-09 00:53:01 -07:00
const u32 bitOffset = ( texaddr - addr ) * 8 ;
const u32 pixelOffset = bitOffset / std : : max ( 1U , ( u32 ) textureBitsPerPixel [ entry - > format ] ) ;
2015-04-08 11:44:45 -07:00
fbInfo . yOffset = entry - > bufw = = 0 ? 0 : pixelOffset / entry - > bufw ;
2015-01-19 08:45:19 -08:00
fbInfo . xOffset = entry - > bufw = = 0 ? 0 : pixelOffset % entry - > bufw ;
2013-11-15 15:15:12 +01:00
2014-09-09 00:53:01 -07:00
if ( framebuffer - > fb_stride ! = entry - > bufw ) {
if ( noOffset ) {
WARN_LOG_REPORT_ONCE ( diffStrides2 , G3D , " Render to texture using CLUT with different strides %d != %d " , entry - > bufw , framebuffer - > fb_stride ) ;
} else {
// Assume any render-to-tex with different bufw + offset is a render from ram.
DetachFramebuffer ( entry , address , framebuffer ) ;
return false ;
}
}
if ( fbInfo . yOffset + minSubareaHeight > = framebuffer - > height ) {
// Can't be inside the framebuffer then, ram. Detach to be safe.
DetachFramebuffer ( entry , address , framebuffer ) ;
return false ;
}
// Trying to play it safe. Below 0x04110000 is almost always framebuffers.
// TODO: Maybe we can reduce this check and find a better way above 0x04110000?
if ( fbInfo . yOffset > MAX_SUBAREA_Y_OFFSET_SAFE & & addr > 0x04110000 ) {
WARN_LOG_REPORT_ONCE ( subareaIgnored , G3D , " Ignoring possible render to texture at %08x +%dx%d / %dx%d " , address , fbInfo . xOffset , fbInfo . yOffset , framebuffer - > width , framebuffer - > height ) ;
DetachFramebuffer ( entry , address , framebuffer ) ;
return false ;
}
// Check for CLUT. The framebuffer is always RGB, but it can be interpreted as a CLUT texture.
// 3rd Birthday (and a bunch of other games) render to a 16 bit clut texture.
if ( clutFormat ) {
if ( ! noOffset ) {
WARN_LOG_REPORT_ONCE ( subareaClut , G3D , " Render to texture using CLUT with offset at %08x +%dx%d " , address , fbInfo . xOffset , fbInfo . yOffset ) ;
}
AttachFramebufferValid ( entry , framebuffer , fbInfo ) ;
entry - > status | = TexCacheEntry : : STATUS_DEPALETTIZE ;
// We'll validate it compiles later.
return true ;
} else if ( entry - > format = = GE_TFMT_CLUT8 | | entry - > format = = GE_TFMT_CLUT4 ) {
ERROR_LOG_REPORT_ONCE ( fourEightBit , G3D , " 4 and 8-bit CLUT format not supported for framebuffers " ) ;
}
// This is either normal or we failed to generate a shader to depalettize
if ( framebuffer - > format = = entry - > format | | clutFormat ) {
2013-11-15 15:15:12 +01:00
if ( framebuffer - > format ! = entry - > format ) {
2013-09-10 22:35:38 +02:00
WARN_LOG_REPORT_ONCE ( diffFormat2 , G3D , " Render to texture with different formats %d != %d at %08x " , entry - > format , framebuffer - > format , address ) ;
2014-09-09 00:53:01 -07:00
AttachFramebufferValid ( entry , framebuffer , fbInfo ) ;
return true ;
} else {
WARN_LOG_REPORT_ONCE ( subarea , G3D , " Render to area containing texture at %08x +%dx%d " , address , fbInfo . xOffset , fbInfo . yOffset ) ;
2013-09-10 22:35:38 +02:00
// If "AttachFramebufferValid" , God of War Ghost of Sparta/Chains of Olympus will be missing special effect.
2014-09-09 00:53:01 -07:00
AttachFramebufferInvalid ( entry , framebuffer , fbInfo ) ;
return true ;
2013-08-17 11:23:51 +02:00
}
2014-09-09 00:53:01 -07:00
} else {
WARN_LOG_REPORT_ONCE ( diffFormat2 , G3D , " Render to texture with incompatible formats %d != %d at %08x " , entry - > format , framebuffer - > format , address ) ;
2013-08-17 11:23:51 +02:00
}
}
2014-09-09 00:53:01 -07:00
return false ;
2013-08-17 11:23:51 +02:00
}
2014-09-09 08:12:42 -07:00
inline void TextureCacheDX9 : : DetachFramebuffer ( TexCacheEntry * entry , u32 address , VirtualFramebuffer * framebuffer ) {
2013-08-19 20:36:43 +02:00
if ( entry - > framebuffer = = framebuffer ) {
2014-12-21 16:54:26 -08:00
cacheSizeEstimate_ + = EstimateTexMemoryUsage ( entry ) ;
2013-08-19 20:36:43 +02:00
entry - > framebuffer = 0 ;
2013-09-22 00:18:46 -07:00
host - > GPUNotifyTextureAttachment ( entry - > addr ) ;
2013-08-19 20:36:43 +02:00
}
}
2014-09-08 20:55:56 -07:00
void * TextureCacheDX9 : : ReadIndexedTex ( int level , const u8 * texptr , int bytesPerIndex , u32 dstFmt , int bufw ) {
2013-08-19 20:36:43 +02:00
int w = gstate . getTextureWidth ( level ) ;
int h = gstate . getTextureHeight ( level ) ;
2013-08-17 11:23:51 +02:00
int length = bufw * h ;
void * buf = NULL ;
switch ( gstate . getClutPaletteFormat ( ) ) {
case GE_CMODE_16BIT_BGR5650 :
case GE_CMODE_16BIT_ABGR5551 :
case GE_CMODE_16BIT_ABGR4444 :
{
tmpTexBuf16 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
const u16 * clut = GetCurrentClut < u16 > ( ) ;
2013-09-04 11:19:36 +02:00
if ( ! gstate . isTextureSwizzled ( ) ) {
2013-08-17 11:23:51 +02:00
switch ( bytesPerIndex ) {
case 1 :
2014-09-08 20:55:56 -07:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( const u8 * ) texptr , length , clut ) ;
2013-08-17 11:23:51 +02:00
break ;
case 2 :
2014-09-08 20:55:56 -07:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( const u16_le * ) texptr , length , clut ) ;
2013-08-17 11:23:51 +02:00
break ;
case 4 :
2014-09-08 20:55:56 -07:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( const u32_le * ) texptr , length , clut ) ;
2013-08-17 11:23:51 +02:00
break ;
}
} else {
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2015-07-29 10:59:02 +02:00
UnswizzleFromMem ( texptr , bufw , h , bytesPerIndex ) ;
2013-08-17 11:23:51 +02:00
switch ( bytesPerIndex ) {
case 1 :
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( u8 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
break ;
case 2 :
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( u16 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
break ;
case 4 :
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( u32 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
break ;
}
}
buf = tmpTexBuf16 . data ( ) ;
}
break ;
case GE_CMODE_32BIT_ABGR8888 :
{
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
const u32 * clut = GetCurrentClut < u32 > ( ) ;
2013-09-04 11:19:36 +02:00
if ( ! gstate . isTextureSwizzled ( ) ) {
2013-08-17 11:23:51 +02:00
switch ( bytesPerIndex ) {
case 1 :
2014-09-08 20:55:56 -07:00
DeIndexTexture ( tmpTexBuf32 . data ( ) , ( const u8 * ) texptr , length , clut ) ;
2013-08-17 11:23:51 +02:00
break ;
case 2 :
2014-09-08 20:55:56 -07:00
DeIndexTexture ( tmpTexBuf32 . data ( ) , ( const u16_le * ) texptr , length , clut ) ;
2013-08-17 11:23:51 +02:00
break ;
case 4 :
2014-09-08 20:55:56 -07:00
DeIndexTexture ( tmpTexBuf32 . data ( ) , ( const u32_le * ) texptr , length , clut ) ;
2013-08-17 11:23:51 +02:00
break ;
}
buf = tmpTexBuf32 . data ( ) ;
} else {
2015-07-29 10:59:02 +02:00
UnswizzleFromMem ( texptr , bufw , h , bytesPerIndex ) ;
2013-08-17 11:23:51 +02:00
// Since we had to unswizzle to tmpTexBuf32, let's output to tmpTexBuf16.
tmpTexBuf16 . resize ( std : : max ( bufw , w ) * h * 2 ) ;
u32 * dest32 = ( u32 * ) tmpTexBuf16 . data ( ) ;
switch ( bytesPerIndex ) {
case 1 :
DeIndexTexture ( dest32 , ( u8 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
buf = dest32 ;
break ;
case 2 :
DeIndexTexture ( dest32 , ( u16 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
buf = dest32 ;
break ;
case 4 :
// TODO: If a game actually uses this mode, check if using dest32 or tmpTexBuf32 is faster.
DeIndexTexture ( tmpTexBuf32 . data ( ) , tmpTexBuf32 . data ( ) , length , clut ) ;
buf = tmpTexBuf32 . data ( ) ;
break ;
}
}
}
break ;
default :
2013-12-29 15:10:07 -08:00
ERROR_LOG_REPORT ( G3D , " Unhandled clut texture mode %d!!! " , ( gstate . clutformat & 3 ) ) ;
2013-08-17 11:23:51 +02:00
break ;
}
return buf ;
}
D3DFORMAT getClutDestFormat ( GEPaletteFormat format ) {
switch ( format ) {
case GE_CMODE_16BIT_ABGR4444 :
return D3DFMT_A4R4G4B4 ;
case GE_CMODE_16BIT_ABGR5551 :
return D3DFMT_A1R5G5B5 ;
case GE_CMODE_16BIT_BGR5650 :
return D3DFMT_R5G6B5 ;
case GE_CMODE_32BIT_ABGR8888 :
return D3DFMT_A8R8G8B8 ;
}
// Should never be here !
return D3DFMT_A8R8G8B8 ;
}
static const u8 texByteAlignMap [ ] = { 2 , 2 , 2 , 4 } ;
2014-08-23 01:52:46 +02:00
static const u8 MinFilt [ 8 ] = {
2013-08-17 11:23:51 +02:00
D3DTEXF_POINT ,
D3DTEXF_LINEAR ,
D3DTEXF_POINT ,
D3DTEXF_LINEAR ,
D3DTEXF_POINT , // GL_NEAREST_MIPMAP_NEAREST,
D3DTEXF_LINEAR , // GL_LINEAR_MIPMAP_NEAREST,
D3DTEXF_POINT , // GL_NEAREST_MIPMAP_LINEAR,
D3DTEXF_LINEAR , // GL_LINEAR_MIPMAP_LINEAR,
} ;
2014-08-23 01:52:46 +02:00
static const u8 MipFilt [ 8 ] = {
2013-08-17 11:23:51 +02:00
D3DTEXF_POINT ,
D3DTEXF_LINEAR ,
D3DTEXF_POINT ,
D3DTEXF_LINEAR ,
D3DTEXF_POINT , // GL_NEAREST_MIPMAP_NEAREST,
D3DTEXF_POINT , // GL_LINEAR_MIPMAP_NEAREST,
D3DTEXF_LINEAR , // GL_NEAREST_MIPMAP_LINEAR,
D3DTEXF_LINEAR , // GL_LINEAR_MIPMAP_LINEAR,
} ;
2014-08-23 01:52:46 +02:00
static const u8 MagFilt [ 2 ] = {
2013-08-17 11:23:51 +02:00
D3DTEXF_POINT ,
D3DTEXF_LINEAR
} ;
2014-09-09 00:53:01 -07:00
void TextureCacheDX9 : : UpdateSamplingParams ( TexCacheEntry & entry , bool force ) {
int minFilt ;
int magFilt ;
bool sClamp ;
bool tClamp ;
float lodBias ;
GetSamplingParams ( minFilt , magFilt , sClamp , tClamp , lodBias , entry . maxLevel ) ;
if ( entry . maxLevel ! = 0 ) {
2014-09-14 09:13:06 -07:00
GETexLevelMode mode = gstate . getTexLevelMode ( ) ;
switch ( mode ) {
case GE_TEXLEVEL_MODE_AUTO :
// TODO
break ;
case GE_TEXLEVEL_MODE_CONST :
dxstate . texMipLodBias . set ( lodBias ) ;
// TODO
break ;
case GE_TEXLEVEL_MODE_SLOPE :
// TODO
break ;
2014-09-09 00:53:01 -07:00
}
2014-09-14 09:13:06 -07:00
entry . lodBias = lodBias ;
2014-09-09 00:53:01 -07:00
}
2013-08-17 11:23:51 +02:00
2014-09-20 10:32:43 +02:00
D3DTEXTUREFILTERTYPE minf = ( D3DTEXTUREFILTERTYPE ) MinFilt [ minFilt ] ;
D3DTEXTUREFILTERTYPE mipf = ( D3DTEXTUREFILTERTYPE ) MipFilt [ minFilt ] ;
2014-09-20 14:06:02 +02:00
D3DTEXTUREFILTERTYPE magf = ( D3DTEXTUREFILTERTYPE ) MagFilt [ magFilt ] ;
2014-09-20 10:32:43 +02:00
2014-12-30 15:28:55 -08:00
if ( g_Config . iAnisotropyLevel > 0 & & minf = = D3DTEXF_LINEAR ) {
2014-09-20 13:03:22 +02:00
minf = D3DTEXF_ANISOTROPIC ;
2014-09-20 10:32:43 +02:00
}
dxstate . texMinFilter . set ( minf ) ;
dxstate . texMipFilter . set ( mipf ) ;
dxstate . texMagFilter . set ( magf ) ;
2014-08-23 01:52:46 +02:00
dxstate . texAddressU . set ( sClamp ? D3DTADDRESS_CLAMP : D3DTADDRESS_WRAP ) ;
dxstate . texAddressV . set ( tClamp ? D3DTADDRESS_CLAMP : D3DTADDRESS_WRAP ) ;
2013-08-17 11:23:51 +02:00
}
2014-09-09 00:53:01 -07:00
void TextureCacheDX9 : : SetFramebufferSamplingParams ( u16 bufferWidth , u16 bufferHeight ) {
int minFilt ;
int magFilt ;
bool sClamp ;
bool tClamp ;
float lodBias ;
GetSamplingParams ( minFilt , magFilt , sClamp , tClamp , lodBias , 0 ) ;
dxstate . texMinFilter . set ( MinFilt [ minFilt ] ) ;
dxstate . texMipFilter . set ( MipFilt [ minFilt ] ) ;
dxstate . texMagFilter . set ( MagFilt [ magFilt ] ) ;
// Often the framebuffer will not match the texture size. We'll wrap/clamp in the shader in that case.
// This happens whether we have OES_texture_npot or not.
int w = gstate . getTextureWidth ( 0 ) ;
int h = gstate . getTextureHeight ( 0 ) ;
if ( w ! = bufferWidth | | h ! = bufferHeight ) {
return ;
}
dxstate . texAddressU . set ( sClamp ? D3DTADDRESS_CLAMP : D3DTADDRESS_WRAP ) ;
dxstate . texAddressV . set ( tClamp ? D3DTADDRESS_CLAMP : D3DTADDRESS_WRAP ) ;
}
2013-09-15 12:46:14 +02:00
void TextureCacheDX9 : : StartFrame ( ) {
2013-09-04 11:19:36 +02:00
lastBoundTexture = INVALID_TEX ;
2014-09-09 00:53:01 -07:00
timesInvalidatedAllThisFrame_ = 0 ;
if ( texelsScaledThisFrame_ ) {
// INFO_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_);
}
texelsScaledThisFrame_ = 0 ;
2013-12-29 15:10:07 -08:00
if ( clearCacheNextFrame_ ) {
2013-08-17 11:23:51 +02:00
Clear ( true ) ;
clearCacheNextFrame_ = false ;
} else {
Decimate ( ) ;
}
2014-09-20 10:32:43 +02:00
DWORD anisotropyLevel = ( DWORD ) g_Config . iAnisotropyLevel > maxAnisotropyLevel ? maxAnisotropyLevel : g_Config . iAnisotropyLevel ;
pD3Ddevice - > SetSamplerState ( 0 , D3DSAMP_MAXANISOTROPY , anisotropyLevel ) ;
2013-08-17 11:23:51 +02:00
}
static inline u32 MiniHash ( const u32 * ptr ) {
return ptr [ 0 ] ;
}
2015-03-15 21:38:01 -07:00
static inline u32 QuickTexHash ( u32 addr , int bufw , int w , int h , GETextureFormat format , TextureCacheDX9 : : TexCacheEntry * entry ) {
if ( h = = 512 & & entry - > maxSeenV < 512 & & entry - > maxSeenV ! = 0 ) {
2015-12-30 17:15:50 -08:00
h = ( int ) entry - > maxSeenV ;
2015-03-15 21:38:01 -07:00
}
2013-09-15 21:27:13 -07:00
const u32 sizeInRAM = ( textureBitsPerPixel [ format ] * bufw * h ) / 8 ;
2013-08-17 11:23:51 +02:00
const u32 * checkp = ( const u32 * ) Memory : : GetPointer ( addr ) ;
2013-12-29 15:10:07 -08:00
return DoQuickTexHash ( checkp , sizeInRAM ) ;
2013-08-17 11:23:51 +02:00
}
2015-07-29 11:38:42 +02:00
void TextureCacheDX9 : : UpdateCurrentClut ( GEPaletteFormat clutFormat , u32 clutBase , bool clutIndexIsSimple ) {
2013-08-17 11:23:51 +02:00
const u32 clutBaseBytes = clutBase * ( clutFormat = = GE_CMODE_32BIT_ABGR8888 ? sizeof ( u32 ) : sizeof ( u16 ) ) ;
// Technically, these extra bytes weren't loaded, but hopefully it was loaded earlier.
// If not, we're going to hash random data, which hopefully doesn't cause a performance issue.
2015-04-26 00:43:36 -07:00
//
// TODO: Actually, this seems like a hack. The game can upload part of a CLUT and reference other data.
// clutTotalBytes_ is the last amount uploaded. We should hash clutMaxBytes_, but this will often hash
// unrelated old entries for small palettes.
// Adding clutBaseBytes may just be mitigating this for some usage patterns.
const u32 clutExtendedBytes = std : : min ( clutTotalBytes_ + clutBaseBytes , clutMaxBytes_ ) ;
2013-08-17 11:23:51 +02:00
2014-10-26 17:49:24 -07:00
clutHash_ = DoReliableHash32 ( ( const char * ) clutBufRaw_ , clutExtendedBytes , 0xC0108888 ) ;
2014-08-28 01:20:21 -07:00
clutBuf_ = clutBufRaw_ ;
2013-08-17 11:23:51 +02:00
// Special optimization: fonts typically draw clut4 with just alpha values in a single color.
clutAlphaLinear_ = false ;
clutAlphaLinearColor_ = 0 ;
2015-07-29 11:38:42 +02:00
if ( clutFormat = = GE_CMODE_16BIT_ABGR4444 & & clutIndexIsSimple ) {
2014-09-09 00:53:01 -07:00
const u16_le * clut = GetCurrentClut < u16_le > ( ) ;
2013-08-17 11:23:51 +02:00
clutAlphaLinear_ = true ;
2014-09-13 15:13:34 +02:00
clutAlphaLinearColor_ = clut [ 15 ] & 0x0FFF ;
2013-08-17 11:23:51 +02:00
for ( int i = 0 ; i < 16 ; + + i ) {
2016-01-01 09:02:16 -08:00
u16 step = clutAlphaLinearColor_ | ( i < < 12 ) ;
if ( clut [ i ] ! = step ) {
2013-08-17 11:23:51 +02:00
clutAlphaLinear_ = false ;
break ;
}
}
}
clutLastFormat_ = gstate . clutformat ;
}
template < typename T >
2013-09-15 12:46:14 +02:00
inline const T * TextureCacheDX9 : : GetCurrentClut ( ) {
2013-08-17 11:23:51 +02:00
return ( const T * ) clutBuf_ ;
}
2013-09-15 12:46:14 +02:00
inline u32 TextureCacheDX9 : : GetCurrentClutHash ( ) {
2013-08-17 11:23:51 +02:00
return clutHash_ ;
}
2014-09-09 08:12:42 -07:00
void TextureCacheDX9 : : SetTextureFramebuffer ( TexCacheEntry * entry , VirtualFramebuffer * framebuffer ) {
2014-09-09 00:53:01 -07:00
_dbg_assert_msg_ ( G3D , framebuffer ! = nullptr , " Framebuffer must not be null. " ) ;
framebuffer - > usageFlags | = FB_USAGE_TEXTURE ;
2013-09-10 22:35:38 +02:00
bool useBufferedRendering = g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ;
if ( useBufferedRendering ) {
2015-07-29 14:28:40 +02:00
const GEPaletteFormat clutFormat = gstate . getClutPaletteFormat ( ) ;
2014-09-17 23:26:20 +02:00
LPDIRECT3DPIXELSHADER9 pshader = nullptr ;
if ( ( entry - > status & TexCacheEntry : : STATUS_DEPALETTIZE ) & & ! g_Config . bDisableSlowFramebufEffects ) {
2015-07-29 14:28:40 +02:00
pshader = depalShaderCache_ - > GetDepalettizePixelShader ( clutFormat , framebuffer - > drawnFormat ) ;
2014-09-17 23:26:20 +02:00
}
if ( pshader ) {
const u32 bytesPerColor = clutFormat = = GE_CMODE_32BIT_ABGR8888 ? sizeof ( u32 ) : sizeof ( u16 ) ;
2015-04-26 00:31:00 -07:00
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor ;
2014-09-17 23:26:20 +02:00
2015-07-29 14:28:40 +02:00
TexCacheEntry : : Status alphaStatus = CheckAlpha ( clutBuf_ , getClutDestFormat ( clutFormat ) , clutTotalColors , clutTotalColors , 1 ) ;
2014-09-17 23:26:20 +02:00
gstate_c . textureFullAlpha = alphaStatus = = TexCacheEntry : : STATUS_ALPHA_FULL ;
gstate_c . textureSimpleAlpha = alphaStatus = = TexCacheEntry : : STATUS_ALPHA_SIMPLE ;
2013-09-10 22:35:38 +02:00
} else {
2014-09-17 23:26:20 +02:00
entry - > status & = ~ TexCacheEntry : : STATUS_DEPALETTIZE ;
2014-09-09 00:53:01 -07:00
2014-09-17 23:26:20 +02:00
gstate_c . textureFullAlpha = gstate . getTextureFormat ( ) = = GE_TFMT_5650 ;
gstate_c . textureSimpleAlpha = gstate_c . textureFullAlpha ;
}
2014-09-09 00:53:01 -07:00
// Keep the framebuffer alive.
framebuffer - > last_frame_used = gpuStats . numFlips ;
2013-12-29 15:10:07 -08:00
// We need to force it, since we may have set it on a texture before attaching.
2014-09-09 00:53:01 -07:00
gstate_c . curTextureWidth = framebuffer - > bufferWidth ;
gstate_c . curTextureHeight = framebuffer - > bufferHeight ;
2014-08-28 01:20:21 -07:00
gstate_c . bgraTexture = false ;
2014-09-09 00:53:01 -07:00
gstate_c . curTextureXOffset = fbTexInfo_ [ entry - > addr ] . xOffset ;
gstate_c . curTextureYOffset = fbTexInfo_ [ entry - > addr ] . yOffset ;
gstate_c . needShaderTexClamp = gstate_c . curTextureWidth ! = ( u32 ) gstate . getTextureWidth ( 0 ) | | gstate_c . curTextureHeight ! = ( u32 ) gstate . getTextureHeight ( 0 ) ;
if ( gstate_c . curTextureXOffset ! = 0 | | gstate_c . curTextureYOffset ! = 0 ) {
gstate_c . needShaderTexClamp = true ;
}
2015-03-15 20:05:35 -07:00
nextTexture_ = entry ;
2013-09-10 22:35:38 +02:00
} else {
2015-11-14 13:24:51 +01:00
if ( framebuffer - > fbo_dx9 ) {
fbo_destroy ( framebuffer - > fbo_dx9 ) ;
framebuffer - > fbo_dx9 = 0 ;
2014-09-09 00:53:01 -07:00
}
2013-09-10 22:35:38 +02:00
pD3Ddevice - > SetTexture ( 0 , NULL ) ;
2014-09-09 00:53:01 -07:00
gstate_c . needShaderTexClamp = false ;
2013-09-10 22:35:38 +02:00
}
}
2015-03-15 20:05:35 -07:00
void TextureCacheDX9 : : ApplyTexture ( ) {
if ( nextTexture_ = = nullptr ) {
return ;
}
if ( nextTexture_ - > framebuffer ) {
ApplyTextureFramebuffer ( nextTexture_ , nextTexture_ - > framebuffer ) ;
} else {
2015-03-15 21:38:01 -07:00
// If the texture is >= 512 pixels tall...
if ( nextTexture_ - > dim > = 0x900 ) {
// Texture scale/offset and gen modes don't apply in through.
// So we can optimize how much of the texture we look at.
if ( gstate . isModeThrough ( ) ) {
2015-12-30 17:15:50 -08:00
if ( nextTexture_ - > maxSeenV = = 0 ) {
// Let's not hash less than 272, we might use more later and have to rehash. 272 is very common.
nextTexture_ - > maxSeenV = std : : max ( ( u16 ) 272 , gstate_c . vertBounds . maxV ) ;
} else if ( gstate_c . vertBounds . maxV > nextTexture_ - > maxSeenV ) {
// The max height changed higher, so we're better off hashing the entire thing.
nextTexture_ - > maxSeenV = 512 ;
nextTexture_ - > status | = TexCacheEntry : : STATUS_FREE_CHANGE ;
}
2015-03-15 21:38:01 -07:00
} else {
// Otherwise, we need to reset to ensure we use the whole thing.
// Can't tell how much is used.
// TODO: We could tell for texcoord UV gen, and apply scale to max?
nextTexture_ - > maxSeenV = 512 ;
}
}
2015-03-15 20:05:35 -07:00
LPDIRECT3DTEXTURE9 texture = DxTex ( nextTexture_ ) ;
pD3Ddevice - > SetTexture ( 0 , texture ) ;
lastBoundTexture = texture ;
UpdateSamplingParams ( * nextTexture_ , false ) ;
}
nextTexture_ = nullptr ;
}
void TextureCacheDX9 : : ApplyTextureFramebuffer ( TexCacheEntry * entry , VirtualFramebuffer * framebuffer ) {
LPDIRECT3DPIXELSHADER9 pshader = nullptr ;
const GEPaletteFormat clutFormat = gstate . getClutPaletteFormat ( ) ;
if ( ( entry - > status & TexCacheEntry : : STATUS_DEPALETTIZE ) & & ! g_Config . bDisableSlowFramebufEffects ) {
pshader = depalShaderCache_ - > GetDepalettizePixelShader ( clutFormat , framebuffer - > drawnFormat ) ;
}
if ( pshader ) {
LPDIRECT3DTEXTURE9 clutTexture = depalShaderCache_ - > GetClutTexture ( clutFormat , clutHash_ , clutBuf_ ) ;
2015-11-14 13:24:51 +01:00
FBO_DX9 * depalFBO = framebufferManager_ - > GetTempFBO ( framebuffer - > renderWidth , framebuffer - > renderHeight , FBO_8888 ) ;
2015-03-15 20:05:35 -07:00
fbo_bind_as_render_target ( depalFBO ) ;
float xoff = - 0.5f / framebuffer - > renderWidth ;
float yoff = 0.5f / framebuffer - > renderHeight ;
2015-09-13 11:14:51 -07:00
struct Pos {
Pos ( float x_ , float y_ , float z_ ) : x ( x_ ) , y ( y_ ) , z ( z_ ) {
}
float x ;
float y ;
float z ;
} ;
struct UV {
UV ( float u_ , float v_ ) : u ( u_ ) , v ( v_ ) {
}
float u ;
float v ;
} ;
struct PosUV {
Pos pos ;
UV uv ;
2015-03-15 20:05:35 -07:00
} ;
2015-09-13 11:14:51 -07:00
PosUV verts [ 4 ] = {
{ { - 1 + xoff , 1 + yoff , - 1 } , { 0 , 0 } } ,
{ { 1 + xoff , 1 + yoff , - 1 } , { 1 , 0 } } ,
{ { 1 + xoff , - 1 + yoff , - 1 } , { 1 , 1 } } ,
{ { - 1 + xoff , - 1 + yoff , - 1 } , { 0 , 1 } } ,
} ;
// If min is not < max, then we don't have values (wasn't set during decode.)
2015-09-13 14:52:10 -07:00
if ( gstate_c . vertBounds . minV < gstate_c . vertBounds . maxV ) {
2015-09-13 11:14:51 -07:00
const float invWidth = 1.0f / ( float ) framebuffer - > bufferWidth ;
const float invHeight = 1.0f / ( float ) framebuffer - > bufferHeight ;
// Inverse of half = double.
const float invHalfWidth = invWidth * 2.0f ;
const float invHalfHeight = invHeight * 2.0f ;
2015-09-13 14:52:10 -07:00
const int u1 = gstate_c . vertBounds . minU + gstate_c . curTextureXOffset ;
const int v1 = gstate_c . vertBounds . minV + gstate_c . curTextureYOffset ;
const int u2 = gstate_c . vertBounds . maxU + gstate_c . curTextureXOffset ;
const int v2 = gstate_c . vertBounds . maxV + gstate_c . curTextureYOffset ;
2015-09-12 19:43:02 -07:00
const float left = u1 * invHalfWidth - 1.0f + xoff ;
const float right = u2 * invHalfWidth - 1.0f + xoff ;
const float top = v1 * invHalfHeight - 1.0f + yoff ;
const float bottom = v2 * invHalfHeight - 1.0f + yoff ;
2015-09-13 11:14:51 -07:00
// Points are: BL, BR, TR, TL.
verts [ 0 ] . pos = Pos ( left , bottom , - 1.0f ) ;
verts [ 1 ] . pos = Pos ( right , bottom , - 1.0f ) ;
verts [ 2 ] . pos = Pos ( right , top , - 1.0f ) ;
verts [ 3 ] . pos = Pos ( left , top , - 1.0f ) ;
// And also the UVs, same order.
2015-09-12 19:43:02 -07:00
const float uvleft = u1 * invWidth ;
const float uvright = u2 * invWidth ;
2015-11-01 18:05:17 +01:00
const float uvtop = v1 * invHeight ; // TODO: Seems we should ditch the "1.0f - "
const float uvbottom = v2 * invHeight ;
2015-09-13 11:14:51 -07:00
verts [ 0 ] . uv = UV ( uvleft , uvbottom ) ;
verts [ 1 ] . uv = UV ( uvright , uvbottom ) ;
verts [ 2 ] . uv = UV ( uvright , uvtop ) ;
verts [ 3 ] . uv = UV ( uvleft , uvtop ) ;
}
2015-03-15 20:05:35 -07:00
shaderManager_ - > DirtyLastShader ( ) ;
pD3Ddevice - > SetPixelShader ( pshader ) ;
pD3Ddevice - > SetVertexShader ( depalShaderCache_ - > GetDepalettizeVertexShader ( ) ) ;
pD3Ddevice - > SetVertexDeclaration ( pFramebufferVertexDecl ) ;
pD3Ddevice - > SetTexture ( 1 , clutTexture ) ;
pD3Ddevice - > SetSamplerState ( 1 , D3DSAMP_MINFILTER , D3DTEXF_POINT ) ;
pD3Ddevice - > SetSamplerState ( 1 , D3DSAMP_MAGFILTER , D3DTEXF_POINT ) ;
pD3Ddevice - > SetSamplerState ( 1 , D3DSAMP_MIPFILTER , D3DTEXF_NONE ) ;
2015-09-13 11:14:51 -07:00
framebufferManager_ - > BindFramebufferColor ( 0 , framebuffer , BINDFBCOLOR_SKIP_COPY ) ;
2015-03-15 20:05:35 -07:00
pD3Ddevice - > SetSamplerState ( 0 , D3DSAMP_MINFILTER , D3DTEXF_POINT ) ;
pD3Ddevice - > SetSamplerState ( 0 , D3DSAMP_MAGFILTER , D3DTEXF_POINT ) ;
pD3Ddevice - > SetSamplerState ( 0 , D3DSAMP_MIPFILTER , D3DTEXF_NONE ) ;
pD3Ddevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , FALSE ) ;
pD3Ddevice - > SetRenderState ( D3DRS_SEPARATEALPHABLENDENABLE , FALSE ) ;
pD3Ddevice - > SetRenderState ( D3DRS_COLORWRITEENABLE , D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA ) ;
pD3Ddevice - > SetRenderState ( D3DRS_ZENABLE , FALSE ) ;
pD3Ddevice - > SetRenderState ( D3DRS_STENCILENABLE , FALSE ) ;
pD3Ddevice - > SetRenderState ( D3DRS_SCISSORTESTENABLE , FALSE ) ;
pD3Ddevice - > SetRenderState ( D3DRS_CULLMODE , D3DCULL_NONE ) ;
2015-11-12 14:47:43 +01:00
DXSetViewport ( 0 , 0 , framebuffer - > renderWidth , framebuffer - > renderHeight ) ;
2015-09-13 11:14:51 -07:00
HRESULT hr = pD3Ddevice - > DrawPrimitiveUP ( D3DPT_TRIANGLEFAN , 2 , verts , ( 3 + 2 ) * sizeof ( float ) ) ;
2015-03-15 20:05:35 -07:00
if ( FAILED ( hr ) ) {
ERROR_LOG_REPORT ( G3D , " Depal render failed: %08x " , hr ) ;
}
framebufferManager_ - > RebindFramebuffer ( ) ;
fbo_bind_color_as_texture ( depalFBO , 0 ) ;
dxstate . Restore ( ) ;
dxstate . viewport . restore ( ) ;
framebufferManager_ - > RebindFramebuffer ( ) ;
} else {
2015-09-12 19:43:02 -07:00
framebufferManager_ - > BindFramebufferColor ( 0 , framebuffer , BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET ) ;
2015-03-15 20:05:35 -07:00
}
SetFramebufferSamplingParams ( framebuffer - > bufferWidth , framebuffer - > bufferHeight ) ;
lastBoundTexture = INVALID_TEX ;
}
2014-09-09 00:53:01 -07:00
bool TextureCacheDX9 : : SetOffsetTexture ( u32 offset ) {
if ( g_Config . iRenderingMode ! = FB_BUFFERED_MODE ) {
return false ;
}
u32 texaddr = gstate . getTextureAddress ( 0 ) ;
if ( ! Memory : : IsValidAddress ( texaddr ) | | ! Memory : : IsValidAddress ( texaddr + offset ) ) {
return false ;
}
2015-03-14 11:06:03 -07:00
const u16 dim = gstate . getTextureDimension ( 0 ) ;
u64 cachekey = ( ( u64 ) ( texaddr & 0x3FFFFFFF ) < < 32 ) | dim ;
2014-09-09 00:53:01 -07:00
TexCache : : iterator iter = cache . find ( cachekey ) ;
if ( iter = = cache . end ( ) ) {
return false ;
}
TexCacheEntry * entry = & iter - > second ;
bool success = false ;
for ( size_t i = 0 , n = fbCache_ . size ( ) ; i < n ; + + i ) {
auto framebuffer = fbCache_ [ i ] ;
if ( AttachFramebuffer ( entry , framebuffer - > fb_address , framebuffer , offset ) ) {
success = true ;
}
}
if ( success & & entry - > framebuffer ) {
SetTextureFramebuffer ( entry , entry - > framebuffer ) ;
entry - > lastFrame = gpuStats . numFlips ;
return true ;
}
return false ;
}
2013-11-15 15:15:12 +01:00
void TextureCacheDX9 : : SetTexture ( bool force ) {
2013-08-17 11:23:51 +02:00
# ifdef DEBUG_TEXTURES
if ( SetDebugTexture ( ) ) {
// A different texture was bound, let's rebind next time.
2015-03-15 20:05:35 -07:00
lastBoundTexture = INVALID_TEX ;
2013-08-17 11:23:51 +02:00
return ;
}
# endif
2013-11-15 15:15:12 +01:00
if ( force ) {
lastBoundTexture = INVALID_TEX ;
}
2014-09-08 20:55:56 -07:00
2013-09-15 21:39:28 -07:00
u32 texaddr = gstate . getTextureAddress ( 0 ) ;
2013-08-17 11:23:51 +02:00
if ( ! Memory : : IsValidAddress ( texaddr ) ) {
// Bind a null texture and return.
pD3Ddevice - > SetTexture ( 0 , NULL ) ;
2013-08-19 20:36:43 +02:00
lastBoundTexture = INVALID_TEX ;
2013-08-17 11:23:51 +02:00
return ;
}
2015-03-14 11:06:03 -07:00
const u16 dim = gstate . getTextureDimension ( 0 ) ;
2014-09-08 20:55:56 -07:00
int w = gstate . getTextureWidth ( 0 ) ;
int h = gstate . getTextureHeight ( 0 ) ;
2013-08-17 11:23:51 +02:00
GETextureFormat format = gstate . getTextureFormat ( ) ;
if ( format > = 11 ) {
ERROR_LOG_REPORT ( G3D , " Unknown texture format %i " , format ) ;
// TODO: Better assumption?
format = GE_TFMT_5650 ;
}
bool hasClut = gstate . isTextureFormatIndexed ( ) ;
2014-09-08 20:55:56 -07:00
// Ignore uncached/kernel when caching.
2015-03-14 11:06:03 -07:00
u64 cachekey = ( ( u64 ) ( texaddr & 0x3FFFFFFF ) < < 32 ) | dim ;
2013-08-17 11:23:51 +02:00
u32 cluthash ;
if ( hasClut ) {
if ( clutLastFormat_ ! = gstate . clutformat ) {
// We update here because the clut format can be specified after the load.
2015-07-29 11:38:42 +02:00
UpdateCurrentClut ( gstate . getClutPaletteFormat ( ) , gstate . getClutIndexStartPos ( ) , gstate . isClutIndexSimple ( ) ) ;
2013-08-17 11:23:51 +02:00
}
cluthash = GetCurrentClutHash ( ) ^ gstate . clutformat ;
2015-03-14 11:06:03 -07:00
cachekey ^ = cluthash ;
2013-08-17 11:23:51 +02:00
} else {
cluthash = 0 ;
}
2013-12-29 15:10:07 -08:00
int bufw = GetTextureBufw ( 0 , texaddr , format ) ;
2015-03-15 19:51:53 -07:00
u8 maxLevel = gstate . getTextureMaxLevel ( ) ;
2013-08-17 11:23:51 +02:00
u32 texhash = MiniHash ( ( const u32 * ) Memory : : GetPointer ( texaddr ) ) ;
u32 fullhash = 0 ;
TexCache : : iterator iter = cache . find ( cachekey ) ;
TexCacheEntry * entry = NULL ;
2014-09-09 00:53:01 -07:00
gstate_c . needShaderTexClamp = false ;
2014-08-28 01:20:21 -07:00
gstate_c . bgraTexture = true ;
2013-08-17 11:23:51 +02:00
gstate_c . skipDrawReason & = ~ SKIPDRAW_BAD_FB_TEXTURE ;
2013-09-04 11:19:36 +02:00
bool useBufferedRendering = g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ;
2013-08-17 11:23:51 +02:00
bool replaceImages = false ;
if ( iter ! = cache . end ( ) ) {
entry = & iter - > second ;
2013-09-10 22:35:38 +02:00
// Validate the texture still matches the cache entry.
bool match = entry - > Matches ( dim , format , maxLevel ) ;
2015-03-14 17:05:44 -07:00
const char * reason = " different params " ;
2013-09-10 22:35:38 +02:00
2013-08-17 11:23:51 +02:00
// Check for FBO - slow!
2013-12-29 15:10:07 -08:00
if ( entry - > framebuffer ) {
if ( match ) {
2015-11-28 12:41:37 -08:00
if ( hasClut & & clutRenderAddress_ ! = 0xFFFFFFFF ) {
2015-11-26 14:31:46 -08:00
WARN_LOG_REPORT_ONCE ( clutAndTexRender , G3D , " Using rendered texture with rendered CLUT: texfmt=%d, clutfmt=%d " , gstate . getTextureFormat ( ) , gstate . getClutPaletteFormat ( ) ) ;
}
2014-09-09 00:53:01 -07:00
SetTextureFramebuffer ( entry , entry - > framebuffer ) ;
2013-12-29 15:10:07 -08:00
entry - > lastFrame = gpuStats . numFlips ;
return ;
} else {
// Make sure we re-evaluate framebuffers.
DetachFramebuffer ( entry , texaddr , entry - > framebuffer ) ;
2015-03-14 17:05:44 -07:00
reason = " detached framebuf " ;
2013-12-29 15:10:07 -08:00
match = false ;
}
2013-08-17 11:23:51 +02:00
}
2013-08-19 20:36:43 +02:00
2014-09-09 00:53:01 -07:00
bool rehash = entry - > GetHashStatus ( ) = = TexCacheEntry : : STATUS_UNRELIABLE ;
2013-08-17 11:23:51 +02:00
bool doDelete = true ;
2014-09-09 00:53:01 -07:00
// First let's see if another texture with the same address had a hashfail.
if ( entry - > status & TexCacheEntry : : STATUS_CLUT_RECHECK ) {
// Always rehash in this case, if one changed the rest all probably did.
rehash = true ;
entry - > status & = ~ TexCacheEntry : : STATUS_CLUT_RECHECK ;
} else if ( ( gstate_c . textureChanged & TEXCHANGE_UPDATED ) = = 0 ) {
// Okay, just some parameter change - the data didn't change, no need to rehash.
rehash = false ;
}
2013-08-17 11:23:51 +02:00
if ( match ) {
2013-08-17 15:11:27 +02:00
if ( entry - > lastFrame ! = gpuStats . numFlips ) {
2013-12-29 15:10:07 -08:00
u32 diff = gpuStats . numFlips - entry - > lastFrame ;
2013-08-17 11:23:51 +02:00
entry - > numFrames + + ;
2013-12-29 15:10:07 -08:00
if ( entry - > framesUntilNextFullHash < diff ) {
// Exponential backoff up to 512 frames. Textures are often reused.
if ( entry - > numFrames > 32 ) {
// Also, try to add some "randomness" to avoid rehashing several textures the same frame.
2015-03-15 19:46:36 -07:00
entry - > framesUntilNextFullHash = std : : min ( 512 , entry - > numFrames ) + ( ( intptr_t ) entry - > texturePtr & 15 ) ;
2013-12-29 15:10:07 -08:00
} else {
entry - > framesUntilNextFullHash = entry - > numFrames ;
}
rehash = true ;
} else {
entry - > framesUntilNextFullHash - = diff ;
}
2013-08-17 11:23:51 +02:00
}
// If it's not huge or has been invalidated many times, recheck the whole texture.
2014-09-09 00:53:01 -07:00
if ( entry - > invalidHint > 180 | | ( entry - > invalidHint > 15 & & ( dim > > 8 ) < 9 & & ( dim & 0xF ) < 9 ) ) {
2013-08-17 11:23:51 +02:00
entry - > invalidHint = 0 ;
rehash = true ;
}
bool hashFail = false ;
if ( texhash ! = entry - > hash ) {
2015-03-15 21:38:01 -07:00
fullhash = QuickTexHash ( texaddr , bufw , w , h , format , entry ) ;
2013-08-17 11:23:51 +02:00
hashFail = true ;
rehash = false ;
}
2014-09-09 00:53:01 -07:00
if ( rehash & & entry - > GetHashStatus ( ) ! = TexCacheEntry : : STATUS_RELIABLE ) {
2015-03-15 21:38:01 -07:00
fullhash = QuickTexHash ( texaddr , bufw , w , h , format , entry ) ;
2013-08-17 11:23:51 +02:00
if ( fullhash ! = entry - > fullhash ) {
hashFail = true ;
2015-12-30 16:54:25 -08:00
} else {
2013-12-29 15:10:07 -08:00
if ( g_Config . bTextureBackoffCache ) {
2015-12-30 16:54:25 -08:00
if ( entry - > GetHashStatus ( ) ! = TexCacheEntry : : STATUS_HASHING & & entry - > numFrames > TexCacheEntry : : FRAMES_REGAIN_TRUST ) {
// Reset to STATUS_HASHING.
entry - > SetHashStatus ( TexCacheEntry : : STATUS_HASHING ) ;
entry - > status & = ~ TexCacheEntry : : STATUS_CHANGE_FREQUENT ;
}
} else if ( entry - > numFrames > TEXCACHE_FRAME_CHANGE_FREQUENT_REGAIN_TRUST ) {
entry - > status & = ~ TexCacheEntry : : STATUS_CHANGE_FREQUENT ;
2013-12-29 15:10:07 -08:00
}
2013-08-17 11:23:51 +02:00
}
}
if ( hashFail ) {
match = false ;
2015-03-14 17:05:44 -07:00
reason = " hash fail " ;
2013-08-17 11:23:51 +02:00
entry - > status | = TexCacheEntry : : STATUS_UNRELIABLE ;
2014-09-09 00:53:01 -07:00
if ( entry - > numFrames < TEXCACHE_FRAME_CHANGE_FREQUENT ) {
2015-12-30 17:15:50 -08:00
if ( entry - > status & TexCacheEntry : : STATUS_FREE_CHANGE ) {
entry - > status & = ~ TexCacheEntry : : STATUS_FREE_CHANGE ;
} else {
entry - > status | = TexCacheEntry : : STATUS_CHANGE_FREQUENT ;
}
2014-09-09 00:53:01 -07:00
}
2013-08-17 11:23:51 +02:00
entry - > numFrames = 0 ;
// Don't give up just yet. Let's try the secondary cache if it's been invalidated before.
// If it's failed a bunch of times, then the second cache is just wasting time and VRAM.
2013-12-29 15:10:07 -08:00
if ( g_Config . bTextureSecondaryCache ) {
if ( entry - > numInvalidated > 2 & & entry - > numInvalidated < 128 & & ! lowMemoryMode_ ) {
u64 secondKey = fullhash | ( u64 ) cluthash < < 32 ;
TexCache : : iterator secondIter = secondCache . find ( secondKey ) ;
if ( secondIter ! = secondCache . end ( ) ) {
TexCacheEntry * secondEntry = & secondIter - > second ;
if ( secondEntry - > Matches ( dim , format , maxLevel ) ) {
// Reset the numInvalidated value lower, we got a match.
if ( entry - > numInvalidated > 8 ) {
- - entry - > numInvalidated ;
}
entry = secondEntry ;
match = true ;
2013-08-17 11:23:51 +02:00
}
2013-12-29 15:10:07 -08:00
} else {
2014-09-09 00:53:01 -07:00
secondKey = entry - > fullhash | ( ( u64 ) entry - > cluthash < < 32 ) ;
2014-12-21 16:54:26 -08:00
secondCacheSizeEstimate_ + = EstimateTexMemoryUsage ( entry ) ;
2013-12-29 15:10:07 -08:00
secondCache [ secondKey ] = * entry ;
doDelete = false ;
2013-08-17 11:23:51 +02:00
}
}
}
}
}
2014-09-09 00:53:01 -07:00
if ( match & & ( entry - > status & TexCacheEntry : : STATUS_TO_SCALE ) & & g_Config . iTexScalingLevel ! = 1 & & texelsScaledThisFrame_ < TEXCACHE_MAX_TEXELS_SCALED ) {
2015-03-15 09:10:09 -07:00
if ( ( entry - > status & TexCacheEntry : : STATUS_CHANGE_FREQUENT ) = = 0 ) {
// INFO_LOG(G3D, "Reloading texture to do the scaling we skipped..");
match = false ;
2015-03-14 17:05:44 -07:00
reason = " scaling " ;
2015-03-15 09:10:09 -07:00
}
2014-09-09 00:53:01 -07:00
}
2013-08-17 11:23:51 +02:00
if ( match ) {
// TODO: Mark the entry reliable if it's been safe for long enough?
//got one!
2013-08-17 15:11:27 +02:00
entry - > lastFrame = gpuStats . numFlips ;
2015-03-15 19:46:36 -07:00
LPDIRECT3DTEXTURE9 texture = DxTex ( entry ) ;
if ( texture ! = lastBoundTexture ) {
2015-03-15 20:05:35 -07:00
nextTexture_ = entry ;
2014-09-09 00:53:01 -07:00
gstate_c . textureFullAlpha = entry - > GetAlphaStatus ( ) = = TexCacheEntry : : STATUS_ALPHA_FULL ;
gstate_c . textureSimpleAlpha = entry - > GetAlphaStatus ( ) ! = TexCacheEntry : : STATUS_ALPHA_UNKNOWN ;
2013-08-17 11:23:51 +02:00
}
UpdateSamplingParams ( * entry , false ) ;
2013-09-10 22:35:38 +02:00
VERBOSE_LOG ( G3D , " Texture at %08x Found in Cache, applying " , texaddr ) ;
2013-08-17 11:23:51 +02:00
return ; //Done!
} else {
2014-12-21 16:54:26 -08:00
cacheSizeEstimate_ - = EstimateTexMemoryUsage ( entry ) ;
2013-08-17 11:23:51 +02:00
entry - > numInvalidated + + ;
gpuStats . numTextureInvalidations + + ;
2015-03-14 17:05:44 -07:00
DEBUG_LOG ( G3D , " Texture different or overwritten, reloading at %08x: %s " , texaddr , reason ) ;
2013-08-17 11:23:51 +02:00
if ( doDelete ) {
2014-09-15 07:09:25 -07:00
if ( entry - > maxLevel = = maxLevel & & entry - > dim = = gstate . getTextureDimension ( 0 ) & & entry - > format = = format & & g_Config . iTexScalingLevel = = 1 ) {
2013-08-17 11:23:51 +02:00
// Actually, if size and number of levels match, let's try to avoid deleting and recreating.
// Instead, let's use glTexSubImage to replace the images.
replaceImages = true ;
} else {
2015-03-15 19:46:36 -07:00
if ( entry - > texturePtr = = lastBoundTexture ) {
2013-08-19 20:36:43 +02:00
lastBoundTexture = INVALID_TEX ;
2013-08-17 11:23:51 +02:00
}
2015-03-15 19:46:36 -07:00
ReleaseTexture ( entry ) ;
2013-08-17 11:23:51 +02:00
}
}
2014-09-09 00:53:01 -07:00
// Clear the reliable bit if set.
if ( entry - > GetHashStatus ( ) = = TexCacheEntry : : STATUS_RELIABLE ) {
entry - > SetHashStatus ( TexCacheEntry : : STATUS_HASHING ) ;
}
// Also, mark any textures with the same address but different clut. They need rechecking.
if ( cluthash ! = 0 ) {
const u64 cachekeyMin = ( u64 ) ( texaddr & 0x3FFFFFFF ) < < 32 ;
const u64 cachekeyMax = cachekeyMin + ( 1ULL < < 32 ) ;
for ( auto it = cache . lower_bound ( cachekeyMin ) , end = cache . upper_bound ( cachekeyMax ) ; it ! = end ; + + it ) {
if ( it - > second . cluthash ! = cluthash ) {
it - > second . status | = TexCacheEntry : : STATUS_CLUT_RECHECK ;
}
}
2013-08-17 11:23:51 +02:00
}
}
} else {
2013-09-10 22:35:38 +02:00
VERBOSE_LOG ( G3D , " No texture in cache, decoding... " ) ;
2013-08-17 11:23:51 +02:00
TexCacheEntry entryNew = { 0 } ;
cache [ cachekey ] = entryNew ;
2015-11-28 12:41:37 -08:00
if ( hasClut & & clutRenderAddress_ ! = 0xFFFFFFFF ) {
2015-11-26 14:31:46 -08:00
WARN_LOG_REPORT_ONCE ( clutUseRender , G3D , " Using texture with rendered CLUT: texfmt=%d, clutfmt=%d " , gstate . getTextureFormat ( ) , gstate . getClutPaletteFormat ( ) ) ;
}
2013-08-17 11:23:51 +02:00
entry = & cache [ cachekey ] ;
2013-12-29 15:10:07 -08:00
if ( g_Config . bTextureBackoffCache ) {
entry - > status = TexCacheEntry : : STATUS_HASHING ;
} else {
entry - > status = TexCacheEntry : : STATUS_UNRELIABLE ;
}
2013-08-17 11:23:51 +02:00
}
2014-03-01 16:11:56 -08:00
if ( ( bufw = = 0 | | ( gstate . texbufwidth [ 0 ] & 0xf800 ) ! = 0 ) & & texaddr > = PSP_GetKernelMemoryEnd ( ) ) {
2013-09-10 22:35:38 +02:00
ERROR_LOG_REPORT ( G3D , " Texture with unexpected bufw (full=%d) " , gstate . texbufwidth [ 0 ] & 0xffff ) ;
2013-08-17 11:23:51 +02:00
}
// We have to decode it, let's setup the cache entry first.
entry - > addr = texaddr ;
entry - > hash = texhash ;
entry - > format = format ;
2013-08-17 15:11:27 +02:00
entry - > lastFrame = gpuStats . numFlips ;
2013-08-17 11:23:51 +02:00
entry - > framebuffer = 0 ;
entry - > maxLevel = maxLevel ;
2014-09-09 00:53:01 -07:00
entry - > lodBias = 0.0f ;
2013-08-17 11:23:51 +02:00
2015-07-29 11:38:42 +02:00
entry - > dim = dim ;
2013-08-17 11:23:51 +02:00
entry - > bufw = bufw ;
// This would overestimate the size in many case so we underestimate instead
// to avoid excessive clearing caused by cache invalidations.
2013-09-15 21:27:13 -07:00
entry - > sizeInRAM = ( textureBitsPerPixel [ format ] * bufw * h / 2 ) / 8 ;
2013-08-17 11:23:51 +02:00
2015-03-15 21:38:01 -07:00
entry - > fullhash = fullhash = = 0 ? QuickTexHash ( texaddr , bufw , w , h , format , entry ) : fullhash ;
2013-08-17 11:23:51 +02:00
entry - > cluthash = cluthash ;
entry - > status & = ~ TexCacheEntry : : STATUS_ALPHA_MASK ;
gstate_c . curTextureWidth = w ;
gstate_c . curTextureHeight = h ;
2014-12-21 16:54:26 -08:00
// For the estimate, we assume cluts always point to 8888 for simplicity.
cacheSizeEstimate_ + = EstimateTexMemoryUsage ( entry ) ;
2013-12-29 15:10:07 -08:00
// TODO: If a framebuffer is attached here, might end up with a bad entry.texture.
// Should just always create one here or something (like GLES.)
2013-09-10 22:35:38 +02:00
// Before we go reading the texture from memory, let's check for render-to-texture.
for ( size_t i = 0 , n = fbCache_ . size ( ) ; i < n ; + + i ) {
auto framebuffer = fbCache_ [ i ] ;
2014-09-09 00:53:01 -07:00
AttachFramebuffer ( entry , framebuffer - > fb_address , framebuffer ) ;
2013-09-10 22:35:38 +02:00
}
// If we ended up with a framebuffer, attach it - no texture decoding needed.
if ( entry - > framebuffer ) {
2014-09-09 00:53:01 -07:00
SetTextureFramebuffer ( entry , entry - > framebuffer ) ;
2013-09-10 22:35:38 +02:00
entry - > lastFrame = gpuStats . numFlips ;
return ;
2013-08-17 11:23:51 +02:00
}
// Adjust maxLevel to actually present levels..
2014-09-09 00:53:01 -07:00
bool badMipSizes = false ;
2015-03-15 19:51:53 -07:00
for ( u32 i = 0 ; i < = maxLevel ; i + + ) {
2013-08-17 11:23:51 +02:00
// If encountering levels pointing to nothing, adjust max level.
2013-09-15 21:39:28 -07:00
u32 levelTexaddr = gstate . getTextureAddress ( i ) ;
2013-08-17 11:23:51 +02:00
if ( ! Memory : : IsValidAddress ( levelTexaddr ) ) {
maxLevel = i - 1 ;
break ;
}
2014-09-09 00:53:01 -07:00
if ( i > 0 ) {
int tw = gstate . getTextureWidth ( i ) ;
int th = gstate . getTextureHeight ( i ) ;
if ( tw ! = 1 & & tw ! = ( gstate . getTextureWidth ( i - 1 ) > > 1 ) )
badMipSizes = true ;
else if ( th ! = 1 & & th ! = ( gstate . getTextureHeight ( i - 1 ) > > 1 ) )
badMipSizes = true ;
}
}
// In addition, simply don't load more than level 0 if g_Config.bMipMap is false.
if ( ! g_Config . bMipMap ) {
maxLevel = 0 ;
}
// If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient.
D3DFORMAT dstFmt = GetDestFormat ( format , gstate . getClutPaletteFormat ( ) ) ;
int scaleFactor ;
// Auto-texture scale upto 5x rendering resolution
if ( g_Config . iTexScalingLevel = = 0 ) {
scaleFactor = g_Config . iInternalResolution ;
if ( scaleFactor = = 0 ) {
scaleFactor = ( PSP_CoreParameter ( ) . renderWidth + 479 ) / 480 ;
}
scaleFactor = std : : min ( 4 , scaleFactor ) ;
if ( scaleFactor = = 3 ) {
scaleFactor = 2 ;
}
} else {
scaleFactor = g_Config . iTexScalingLevel ;
2013-08-17 11:23:51 +02:00
}
2015-12-30 16:54:25 -08:00
// Rachet down scale factor in low-memory mode.
if ( lowMemoryMode_ ) {
// Keep it even, though, just in case of npot troubles.
scaleFactor = scaleFactor > 4 ? 4 : ( scaleFactor > 2 ? 2 : 1 ) ;
}
2014-09-09 00:53:01 -07:00
// Don't scale the PPGe texture.
if ( entry - > addr > 0x05000000 & & entry - > addr < 0x08800000 )
scaleFactor = 1 ;
2015-12-30 16:54:25 -08:00
if ( ( entry - > status & TexCacheEntry : : STATUS_CHANGE_FREQUENT ) ! = 0 ) {
// Remember for later that we /wanted/ to scale this texture.
entry - > status | = TexCacheEntry : : STATUS_TO_SCALE ;
scaleFactor = 1 ;
}
2013-08-17 11:23:51 +02:00
2015-12-30 16:54:25 -08:00
if ( scaleFactor ! = 1 ) {
2014-09-09 00:53:01 -07:00
if ( texelsScaledThisFrame_ > = TEXCACHE_MAX_TEXELS_SCALED ) {
entry - > status | = TexCacheEntry : : STATUS_TO_SCALE ;
scaleFactor = 1 ;
} else {
entry - > status & = ~ TexCacheEntry : : STATUS_TO_SCALE ;
texelsScaledThisFrame_ + = w * h ;
}
}
2014-09-14 09:13:06 -07:00
if ( replaceImages ) {
// Make sure it's not currently set.
pD3Ddevice - > SetTexture ( 0 , NULL ) ;
}
// Seems to cause problems in Tactics Ogre.
if ( badMipSizes ) {
maxLevel = 0 ;
}
LoadTextureLevel ( * entry , 0 , maxLevel , replaceImages , scaleFactor , dstFmt ) ;
2015-03-15 19:46:36 -07:00
LPDIRECT3DTEXTURE9 & texture = DxTex ( entry ) ;
if ( ! texture ) {
2014-09-14 09:13:06 -07:00
return ;
}
// Mipmapping is only enabled when texture scaling is disabled.
2015-12-30 16:54:25 -08:00
if ( maxLevel > 0 & & scaleFactor = = 1 ) {
2015-03-15 19:51:53 -07:00
for ( u32 i = 1 ; i < = maxLevel ; i + + ) {
2014-09-14 09:13:06 -07:00
LoadTextureLevel ( * entry , i , maxLevel , replaceImages , scaleFactor , dstFmt ) ;
}
}
2014-09-09 00:53:01 -07:00
gstate_c . textureFullAlpha = entry - > GetAlphaStatus ( ) = = TexCacheEntry : : STATUS_ALPHA_FULL ;
gstate_c . textureSimpleAlpha = entry - > GetAlphaStatus ( ) ! = TexCacheEntry : : STATUS_ALPHA_UNKNOWN ;
2015-03-15 20:05:35 -07:00
nextTexture_ = entry ;
UpdateSamplingParams ( * nextTexture_ , true ) ;
2014-09-09 00:53:01 -07:00
}
2013-08-17 11:23:51 +02:00
2014-09-09 00:53:01 -07:00
D3DFORMAT TextureCacheDX9 : : GetDestFormat ( GETextureFormat format , GEPaletteFormat clutFormat ) const {
switch ( format ) {
case GE_TFMT_CLUT4 :
case GE_TFMT_CLUT8 :
case GE_TFMT_CLUT16 :
case GE_TFMT_CLUT32 :
return getClutDestFormat ( clutFormat ) ;
case GE_TFMT_4444 :
return D3DFMT_A4R4G4B4 ;
case GE_TFMT_5551 :
return D3DFMT_A1R5G5B5 ;
case GE_TFMT_5650 :
return D3DFMT_R5G6B5 ;
case GE_TFMT_8888 :
case GE_TFMT_DXT1 :
case GE_TFMT_DXT3 :
case GE_TFMT_DXT5 :
default :
return D3DFMT_A8R8G8B8 ;
}
2013-08-17 11:23:51 +02:00
}
2014-09-08 20:55:56 -07:00
void * TextureCacheDX9 : : DecodeTextureLevel ( GETextureFormat format , GEPaletteFormat clutformat , int level , u32 & texByteAlign , u32 & dstFmt , int * bufwout ) {
2013-08-17 11:23:51 +02:00
void * finalBuf = NULL ;
2013-09-15 21:39:28 -07:00
u32 texaddr = gstate . getTextureAddress ( level ) ;
2015-04-18 12:39:04 -07:00
bool swizzled = gstate . isTextureSwizzled ( ) ;
if ( ( texaddr & 0x00600000 ) ! = 0 & & Memory : : IsVRAMAddress ( texaddr ) ) {
2014-09-09 00:53:01 -07:00
// This means it's in a mirror, possibly a swizzled mirror. Let's report.
2015-04-18 12:39:04 -07:00
WARN_LOG_REPORT_ONCE ( texmirror , G3D , " Decoding texture from VRAM mirror at %08x swizzle=%d " , texaddr , swizzled ? 1 : 0 ) ;
if ( ( texaddr & 0x00200000 ) = = 0x00200000 ) {
// Technically 2 and 6 are slightly different, but this is better than nothing probably.
swizzled = ! swizzled ;
}
// Note that (texaddr & 0x00600000) == 0x00600000 is very likely to be depth texturing.
2014-09-09 00:53:01 -07:00
}
2013-08-17 11:23:51 +02:00
2013-09-15 21:27:13 -07:00
int bufw = GetTextureBufw ( level , texaddr , format ) ;
2014-09-08 20:55:56 -07:00
if ( bufwout )
* bufwout = bufw ;
2013-08-19 20:36:43 +02:00
int w = gstate . getTextureWidth ( level ) ;
int h = gstate . getTextureHeight ( level ) ;
2013-08-17 11:23:51 +02:00
const u8 * texptr = Memory : : GetPointer ( texaddr ) ;
2013-09-04 11:19:36 +02:00
switch ( format ) {
2013-08-17 11:23:51 +02:00
case GE_TFMT_CLUT4 :
{
2013-12-08 23:11:56 -08:00
const bool mipmapShareClut = gstate . isClutSharedForMipmaps ( ) ;
2013-08-17 11:23:51 +02:00
const int clutSharingOffset = mipmapShareClut ? 0 : level * 16 ;
switch ( clutformat ) {
case GE_CMODE_16BIT_BGR5650 :
case GE_CMODE_16BIT_ABGR5551 :
case GE_CMODE_16BIT_ABGR4444 :
{
tmpTexBuf16 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
const u16 * clut = GetCurrentClut < u16 > ( ) + clutSharingOffset ;
texByteAlign = 2 ;
2015-04-18 12:39:04 -07:00
if ( ! swizzled ) {
2013-08-17 11:23:51 +02:00
if ( clutAlphaLinear_ & & mipmapShareClut ) {
2014-09-08 20:55:56 -07:00
DeIndexTexture4Optimal ( tmpTexBuf16 . data ( ) , texptr , bufw * h , clutAlphaLinearColor_ ) ;
2013-08-17 11:23:51 +02:00
} else {
2014-09-08 20:55:56 -07:00
DeIndexTexture4 ( tmpTexBuf16 . data ( ) , texptr , bufw * h , clut ) ;
2013-08-17 11:23:51 +02:00
}
} else {
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2015-07-29 10:59:02 +02:00
UnswizzleFromMem ( texptr , bufw , h , 0 ) ;
2013-08-17 11:23:51 +02:00
if ( clutAlphaLinear_ & & mipmapShareClut ) {
2014-09-08 20:55:56 -07:00
DeIndexTexture4Optimal ( tmpTexBuf16 . data ( ) , ( const u8 * ) tmpTexBuf32 . data ( ) , bufw * h , clutAlphaLinearColor_ ) ;
2013-08-17 11:23:51 +02:00
} else {
2014-09-08 20:55:56 -07:00
DeIndexTexture4 ( tmpTexBuf16 . data ( ) , ( const u8 * ) tmpTexBuf32 . data ( ) , bufw * h , clut ) ;
2013-08-17 11:23:51 +02:00
}
}
finalBuf = tmpTexBuf16 . data ( ) ;
}
break ;
case GE_CMODE_32BIT_ABGR8888 :
{
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
const u32 * clut = GetCurrentClut < u32 > ( ) + clutSharingOffset ;
2015-04-18 12:39:04 -07:00
if ( ! swizzled ) {
2014-09-08 20:55:56 -07:00
DeIndexTexture4 ( tmpTexBuf32 . data ( ) , texptr , bufw * h , clut ) ;
2013-08-17 11:23:51 +02:00
finalBuf = tmpTexBuf32 . data ( ) ;
} else {
2015-07-29 10:59:02 +02:00
UnswizzleFromMem ( texptr , bufw , h , 0 ) ;
2013-08-17 11:23:51 +02:00
// Let's reuse tmpTexBuf16, just need double the space.
tmpTexBuf16 . resize ( std : : max ( bufw , w ) * h * 2 ) ;
DeIndexTexture4 ( ( u32 * ) tmpTexBuf16 . data ( ) , ( u8 * ) tmpTexBuf32 . data ( ) , bufw * h , clut ) ;
finalBuf = tmpTexBuf16 . data ( ) ;
}
}
break ;
default :
2013-09-10 22:35:38 +02:00
ERROR_LOG_REPORT ( G3D , " Unknown CLUT4 texture mode %d " , gstate . getClutPaletteFormat ( ) ) ;
2013-08-17 11:23:51 +02:00
return NULL ;
}
}
break ;
case GE_TFMT_CLUT8 :
texByteAlign = texByteAlignMap [ gstate . getClutPaletteFormat ( ) ] ;
2014-09-08 20:55:56 -07:00
finalBuf = ReadIndexedTex ( level , texptr , 1 , dstFmt , bufw ) ;
2013-08-17 11:23:51 +02:00
break ;
case GE_TFMT_CLUT16 :
texByteAlign = texByteAlignMap [ gstate . getClutPaletteFormat ( ) ] ;
2014-09-08 20:55:56 -07:00
finalBuf = ReadIndexedTex ( level , texptr , 2 , dstFmt , bufw ) ;
2013-08-17 11:23:51 +02:00
break ;
case GE_TFMT_CLUT32 :
texByteAlign = texByteAlignMap [ gstate . getClutPaletteFormat ( ) ] ;
2014-09-08 20:55:56 -07:00
finalBuf = ReadIndexedTex ( level , texptr , 4 , dstFmt , bufw ) ;
2013-08-17 11:23:51 +02:00
break ;
case GE_TFMT_4444 :
case GE_TFMT_5551 :
case GE_TFMT_5650 :
texByteAlign = 2 ;
2015-04-18 12:39:04 -07:00
if ( ! swizzled ) {
2013-08-17 11:23:51 +02:00
int len = std : : max ( bufw , w ) * h ;
tmpTexBuf16 . resize ( len ) ;
tmpTexBufRearrange . resize ( len ) ;
2015-04-05 18:03:50 -07:00
Memory : : MemcpyUnchecked ( tmpTexBuf16 . data ( ) , texaddr , len * sizeof ( u16 ) ) ;
2013-08-17 11:23:51 +02:00
finalBuf = tmpTexBuf16 . data ( ) ;
}
else {
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2015-07-29 10:59:02 +02:00
finalBuf = UnswizzleFromMem ( texptr , bufw , h , 2 ) ;
2013-08-17 11:23:51 +02:00
}
break ;
case GE_TFMT_8888 :
2015-04-18 12:39:04 -07:00
if ( ! swizzled ) {
2013-08-17 11:23:51 +02:00
// Special case: if we don't need to deal with packing, we don't need to copy.
//if (w == bufw) {
// finalBuf = Memory::GetPointer(texaddr);
//} else
{
int len = bufw * h ;
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
2015-04-05 18:03:50 -07:00
Memory : : MemcpyUnchecked ( tmpTexBuf32 . data ( ) , texaddr , len * sizeof ( u32 ) ) ;
2013-08-17 11:23:51 +02:00
finalBuf = tmpTexBuf32 . data ( ) ;
}
2014-09-09 00:53:01 -07:00
} else {
2013-08-17 11:23:51 +02:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2015-07-29 10:59:02 +02:00
finalBuf = UnswizzleFromMem ( texptr , bufw , h , 4 ) ;
2013-08-17 11:23:51 +02:00
}
break ;
case GE_TFMT_DXT1 :
{
int minw = std : : min ( bufw , w ) ;
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
u32 * dst = tmpTexBuf32 . data ( ) ;
DXT1Block * src = ( DXT1Block * ) texptr ;
for ( int y = 0 ; y < h ; y + = 4 ) {
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
for ( int x = 0 ; x < minw ; x + = 4 ) {
2013-09-25 23:07:09 -07:00
DecodeDXT1Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
2013-08-17 11:23:51 +02:00
blockIndex + + ;
}
}
finalBuf = tmpTexBuf32 . data ( ) ;
2013-09-04 11:19:36 +02:00
w = ( w + 3 ) & ~ 3 ;
2013-08-17 11:23:51 +02:00
}
break ;
case GE_TFMT_DXT3 :
{
int minw = std : : min ( bufw , w ) ;
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
u32 * dst = tmpTexBuf32 . data ( ) ;
DXT3Block * src = ( DXT3Block * ) texptr ;
for ( int y = 0 ; y < h ; y + = 4 ) {
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
for ( int x = 0 ; x < minw ; x + = 4 ) {
2013-09-25 23:07:09 -07:00
DecodeDXT3Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
2013-08-17 11:23:51 +02:00
blockIndex + + ;
}
}
w = ( w + 3 ) & ~ 3 ;
finalBuf = tmpTexBuf32 . data ( ) ;
}
break ;
2014-09-09 00:53:01 -07:00
case GE_TFMT_DXT5 :
2013-08-17 11:23:51 +02:00
{
int minw = std : : min ( bufw , w ) ;
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
u32 * dst = tmpTexBuf32 . data ( ) ;
DXT5Block * src = ( DXT5Block * ) texptr ;
for ( int y = 0 ; y < h ; y + = 4 ) {
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
for ( int x = 0 ; x < minw ; x + = 4 ) {
2013-09-25 23:07:09 -07:00
DecodeDXT5Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
2013-08-17 11:23:51 +02:00
blockIndex + + ;
}
}
w = ( w + 3 ) & ~ 3 ;
finalBuf = tmpTexBuf32 . data ( ) ;
}
break ;
default :
ERROR_LOG_REPORT ( G3D , " Unknown Texture Format %d!!! " , format ) ;
return NULL ;
}
if ( ! finalBuf ) {
ERROR_LOG_REPORT ( G3D , " NO finalbuf! Will crash! " ) ;
}
2015-11-28 17:51:15 -08:00
if ( ! ( g_Config . iTexScalingLevel = = 1 & & gstate_c . Supports ( GPU_SUPPORTS_UNPACK_SUBIMAGE ) ) & & w ! = bufw ) {
2013-08-17 11:23:51 +02:00
int pixelSize ;
switch ( dstFmt ) {
case D3DFMT_A4R4G4B4 :
case D3DFMT_A1R5G5B5 :
case D3DFMT_R5G6B5 :
pixelSize = 2 ;
break ;
default :
pixelSize = 4 ;
break ;
}
// Need to rearrange the buffer to simulate GL_UNPACK_ROW_LENGTH etc.
2015-11-28 17:51:15 -08:00
finalBuf = RearrangeBuf ( finalBuf , bufw * pixelSize , w * pixelSize , h ) ;
2013-08-17 11:23:51 +02:00
}
return finalBuf ;
}
2014-09-08 20:55:56 -07:00
TextureCacheDX9 : : TexCacheEntry : : Status TextureCacheDX9 : : CheckAlpha ( const u32 * pixelData , u32 dstFmt , int stride , int w , int h ) {
2015-05-24 22:55:43 -07:00
CheckAlphaResult res ;
2013-08-17 11:23:51 +02:00
switch ( dstFmt ) {
case D3DFMT_A4R4G4B4 :
2015-05-24 22:55:43 -07:00
res = CheckAlphaRGBA4444Basic ( pixelData , stride , w , h ) ;
2013-08-17 11:23:51 +02:00
break ;
case D3DFMT_A1R5G5B5 :
2015-05-24 22:55:43 -07:00
res = CheckAlphaRGBA5551Basic ( pixelData , stride , w , h ) ;
2013-08-17 11:23:51 +02:00
break ;
case D3DFMT_R5G6B5 :
2015-05-24 22:55:43 -07:00
// Never has any alpha.
res = CHECKALPHA_FULL ;
2013-08-17 11:23:51 +02:00
break ;
default :
2015-05-24 22:55:43 -07:00
res = CheckAlphaRGBA8888Basic ( pixelData , stride , w , h ) ;
2013-08-17 11:23:51 +02:00
break ;
}
2015-05-24 22:55:43 -07:00
return ( TexCacheEntry : : Status ) res ;
2013-08-17 11:23:51 +02:00
}
static inline void copyTexture ( int xoffset , int yoffset , int w , int h , int pitch , int srcfmt , int fmt , void * pSrc , void * pDst ) {
2014-08-22 22:16:46 +02:00
int y ;
2013-08-17 11:23:51 +02:00
switch ( fmt ) {
case D3DFMT_R5G6B5 :
case D3DFMT_A4R4G4B4 :
case D3DFMT_A1R5G5B5 :
2014-08-22 22:16:46 +02:00
for ( y = 0 ; y < h ; y + + ) {
2013-08-17 11:23:51 +02:00
const u16 * src = ( const u16 * ) ( ( u8 * ) pSrc + ( w * 2 ) * y ) ;
u16 * dst = ( u16 * ) ( ( u8 * ) pDst + pitch * y ) ;
2013-08-19 10:13:23 +02:00
memcpy ( dst , src , w * sizeof ( u16 ) ) ;
2013-08-17 11:23:51 +02:00
}
break ;
// 32 bit texture
case D3DFMT_A8R8G8B8 :
2014-08-22 22:16:46 +02:00
for ( y = 0 ; y < h ; y + + ) {
2013-08-17 11:23:51 +02:00
const u32 * src = ( const u32 * ) ( ( u8 * ) pSrc + ( w * 4 ) * y ) ;
u32 * dst = ( u32 * ) ( ( u8 * ) pDst + pitch * y ) ;
2013-08-19 10:13:23 +02:00
memcpy ( dst , src , w * sizeof ( u32 ) ) ;
2013-08-17 11:23:51 +02:00
}
break ;
}
}
2014-09-14 09:13:06 -07:00
void TextureCacheDX9 : : LoadTextureLevel ( TexCacheEntry & entry , int level , int maxLevel , bool replaceImages , int scaleFactor , u32 dstFmt ) {
2013-08-17 11:23:51 +02:00
// TODO: only do this once
u32 texByteAlign = 1 ;
GEPaletteFormat clutformat = gstate . getClutPaletteFormat ( ) ;
2014-09-08 20:55:56 -07:00
int bufw ;
2014-09-08 21:43:38 -07:00
void * finalBuf = DecodeTextureLevel ( GETextureFormat ( entry . format ) , clutformat , level , texByteAlign , dstFmt , & bufw ) ;
2013-08-17 11:23:51 +02:00
if ( finalBuf = = NULL ) {
return ;
}
2013-08-19 20:36:43 +02:00
int w = gstate . getTextureWidth ( level ) ;
int h = gstate . getTextureHeight ( level ) ;
2013-08-17 11:23:51 +02:00
gpuStats . numTexturesDecoded + + ;
2013-08-19 20:36:43 +02:00
2013-08-17 11:23:51 +02:00
u32 * pixelData = ( u32 * ) finalBuf ;
2015-01-22 19:53:32 +01:00
if ( scaleFactor > 1 & & ( entry . status & TexCacheEntry : : STATUS_CHANGE_FREQUENT ) = = 0 )
scaler . Scale ( pixelData , dstFmt , w , h , scaleFactor ) ;
2014-09-09 00:53:01 -07:00
if ( ( entry . status & TexCacheEntry : : STATUS_CHANGE_FREQUENT ) = = 0 ) {
TexCacheEntry : : Status alphaStatus = CheckAlpha ( pixelData , dstFmt , w , w , h ) ;
entry . SetAlphaStatus ( alphaStatus , level ) ;
2014-09-08 20:55:56 -07:00
} else {
2014-09-09 00:53:01 -07:00
entry . SetAlphaStatus ( TexCacheEntry : : STATUS_ALPHA_UNKNOWN ) ;
2014-09-08 20:55:56 -07:00
}
2013-08-17 11:23:51 +02:00
2015-03-15 19:46:36 -07:00
LPDIRECT3DTEXTURE9 & texture = DxTex ( & entry ) ;
if ( level = = 0 & & ( ! replaceImages | | texture = = nullptr ) ) {
2014-09-14 09:13:06 -07:00
// Create texture
D3DPOOL pool = D3DPOOL_MANAGED ;
int usage = 0 ;
if ( pD3DdeviceEx ) {
pool = D3DPOOL_DEFAULT ;
usage = D3DUSAGE_DYNAMIC ; // TODO: Switch to using a staging texture?
}
2015-12-30 16:54:25 -08:00
int levels = scaleFactor = = 1 ? maxLevel + 1 : 1 ;
2015-03-15 19:46:36 -07:00
HRESULT hr = pD3Ddevice - > CreateTexture ( w , h , levels , usage , ( D3DFORMAT ) D3DFMT ( dstFmt ) , pool , & texture , NULL ) ;
2014-09-14 09:13:06 -07:00
if ( FAILED ( hr ) ) {
INFO_LOG ( G3D , " Failed to create D3D texture " ) ;
2015-03-15 19:46:36 -07:00
ReleaseTexture ( & entry ) ;
2014-09-14 09:13:06 -07:00
return ;
}
}
2013-08-27 16:31:06 +02:00
2014-09-14 09:13:06 -07:00
D3DLOCKED_RECT rect ;
2015-03-15 19:46:36 -07:00
texture - > LockRect ( level , & rect , NULL , 0 ) ;
2013-08-17 11:23:51 +02:00
2014-09-14 09:13:06 -07:00
copyTexture ( 0 , 0 , w , h , rect . Pitch , entry . format , dstFmt , pixelData , rect . pBits ) ;
2013-08-17 11:23:51 +02:00
2015-03-15 19:46:36 -07:00
texture - > UnlockRect ( level ) ;
2013-08-17 11:23:51 +02:00
}
2015-01-17 11:57:07 -08:00
bool TextureCacheDX9 : : DecodeTexture ( u8 * output , const GPUgstate & state )
2013-08-17 11:23:51 +02:00
{
OutputDebugStringA ( " TextureCache::DecodeTexture : FixMe \r \n " ) ;
return true ;
}
2013-09-15 08:53:21 -07:00
} ;