2012-11-01 16:19:01 +01: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
2012-11-04 23:58:25 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01:00
// 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/.
2013-02-10 13:19:01 -08:00
# include <algorithm>
2013-12-29 15:55:09 -08:00
# include <cstring>
2012-11-01 16:19:01 +01:00
2015-09-06 13:45:17 +02:00
# include "ext/xxhash.h"
2015-12-30 13:29:29 -08:00
# include "i18n/i18n.h"
2015-09-06 13:45:17 +02:00
# include "math/math_util.h"
2015-05-13 22:28:02 +02:00
# include "profiler/profiler.h"
2015-04-08 00:16:22 +02:00
# include "Common/ColorConv.h"
2015-12-30 13:29:29 -08:00
# include "Core/Config.h"
2013-12-29 23:44:35 +01:00
# include "Core/Host.h"
2013-02-21 21:37:19 +01:00
# include "Core/MemMap.h"
2013-03-24 09:42:49 -07:00
# include "Core/Reporting.h"
2013-02-21 21:37:19 +01:00
# include "GPU/ge_constants.h"
# include "GPU/GPUState.h"
2015-09-06 13:45:17 +02:00
# include "GPU/GLES/GLStateCache.h"
2013-02-21 21:37:19 +01:00
# include "GPU/GLES/TextureCache.h"
# include "GPU/GLES/Framebuffer.h"
2014-05-15 22:38:44 -07:00
# include "GPU/GLES/FragmentShaderGenerator.h"
2014-03-29 21:58:38 +01:00
# include "GPU/GLES/DepalettizeShader.h"
2014-05-31 18:55:00 -07:00
# include "GPU/GLES/ShaderManager.h"
2016-04-10 10:27:28 +02:00
# include "GPU/GLES/DrawEngineGLES.h"
2013-09-15 21:27:13 -07:00
# include "GPU/Common/TextureDecoder.h"
2015-12-30 13:29:29 -08:00
# include "UI/OnScreenDisplay.h"
2012-11-01 16:19:01 +01:00
2013-05-05 19:58:59 -07:00
# ifdef _M_SSE
# include <xmmintrin.h>
# endif
2013-02-15 00:30:02 +01:00
// If a texture hasn't been seen for this many frames, get rid of it.
2012-11-22 23:07:15 +01:00
# define TEXTURE_KILL_AGE 200
2013-05-05 00:00:22 -07:00
# define TEXTURE_KILL_AGE_LOWMEM 60
// Not used in lowmem mode.
2013-05-04 20:33:31 -07:00
# define TEXTURE_SECOND_KILL_AGE 100
2013-08-01 00:13:58 +02:00
// Try to be prime to other decimation intervals.
# define TEXCACHE_DECIMATION_INTERVAL 13
2014-01-19 20:54:48 -08:00
// Changes more frequent than this will be considered "frequent" and prevent texture scaling.
2014-06-14 10:47:59 -07:00
# define TEXCACHE_FRAME_CHANGE_FREQUENT 6
2015-12-30 13:43:10 -08:00
// Note: only used when hash backoff is disabled.
# define TEXCACHE_FRAME_CHANGE_FREQUENT_REGAIN_TRUST 33
2014-01-19 20:54:48 -08:00
2014-06-07 15:25:35 -07:00
# define TEXCACHE_NAME_CACHE_SIZE 16
2014-06-13 22:07:26 +02: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 GL
# define TEXCACHE_SECOND_MIN_PRESSURE 4 * 1024 * 1024
2013-11-03 01:08:48 -08:00
# ifndef GL_UNPACK_ROW_LENGTH
# define GL_UNPACK_ROW_LENGTH 0x0CF2
# endif
2015-11-28 15:41:15 -08:00
# define INVALID_TEX -1
2015-09-05 21:23:58 +02:00
// Hack!
2013-06-30 03:35:28 +08:00
extern int g_iNumVideos ;
2016-03-26 11:49:16 -07:00
TextureCache : : TextureCache ( ) : secondCacheSizeEstimate_ ( 0 ) , clearCacheNextFrame_ ( false ) , lowMemoryMode_ ( false ) , clutBuf_ ( NULL ) , texelsScaledThisFrame_ ( 0 ) {
2014-06-15 00:40:08 +02:00
timesInvalidatedAllThisFrame_ = 0 ;
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2013-08-01 00:13:58 +02:00
decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL ;
2014-04-01 14:14:56 +02:00
2013-02-11 20:48:07 +08:00
glGetFloatv ( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT , & maxAnisotropyLevel ) ;
2014-03-22 21:35:16 -07:00
SetupTextureDecoder ( ) ;
2015-03-15 19:10:33 -07:00
nextTexture_ = nullptr ;
2012-11-28 16:12:29 +01:00
}
2013-01-30 20:40:26 +01:00
TextureCache : : ~ TextureCache ( ) {
2015-01-24 20:36:01 -08:00
Clear ( true ) ;
2012-11-28 16:12:29 +01:00
}
2013-01-30 20:40:26 +01:00
void TextureCache : : Clear ( bool delete_them ) {
2013-01-26 21:38:27 +01:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2013-01-06 12:11:47 +01: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 %i " , iter - > second . textureName ) ;
glDeleteTextures ( 1 , & iter - > second . textureName ) ;
2012-11-01 16:19:01 +01:00
}
2013-05-04 20:33:31 -07:00
for ( TexCache : : iterator iter = secondCache . begin ( ) ; iter ! = secondCache . end ( ) ; + + iter ) {
2015-03-15 19:46:36 -07:00
DEBUG_LOG ( G3D , " Deleting texture %i " , iter - > second . textureName ) ;
glDeleteTextures ( 1 , & iter - > second . textureName ) ;
2013-05-04 20:33:31 -07:00
}
2014-06-07 15:25:35 -07:00
if ( ! nameCache_ . empty ( ) ) {
glDeleteTextures ( ( GLsizei ) nameCache_ . size ( ) , & nameCache_ [ 0 ] ) ;
nameCache_ . clear ( ) ;
}
2012-11-01 16:19:01 +01:00
}
2013-05-04 20:33:31 -07:00
if ( cache . size ( ) + secondCache . size ( ) ) {
INFO_LOG ( G3D , " Texture cached cleared from %i textures " , ( int ) ( cache . size ( ) + secondCache . size ( ) ) ) ;
2012-11-01 16:19:01 +01:00
cache . clear ( ) ;
2013-05-04 20:33:31 -07:00
secondCache . clear ( ) ;
2014-12-21 16:54:26 -08:00
cacheSizeEstimate_ = 0 ;
secondCacheSizeEstimate_ = 0 ;
2012-11-01 16:19:01 +01:00
}
2014-09-09 00:53:01 -07:00
fbTexInfo_ . clear ( ) ;
2012-11-01 16:19:01 +01:00
}
2014-06-08 13:52:05 -07:00
void TextureCache : : DeleteTexture ( TexCache : : iterator it ) {
2015-03-15 19:46:36 -07:00
glDeleteTextures ( 1 , & it - > second . textureName ) ;
2016-03-26 12:07:49 -07:00
auto fbInfo = fbTexInfo_ . find ( it - > first ) ;
2014-06-08 13:52:05 -07:00
if ( fbInfo ! = fbTexInfo_ . end ( ) ) {
fbTexInfo_ . erase ( fbInfo ) ;
}
2014-12-21 16:54:26 -08:00
cacheSizeEstimate_ - = EstimateTexMemoryUsage ( & it - > second ) ;
2014-06-08 13:52:05 -07:00
cache . erase ( it ) ;
}
2012-11-22 23:07:15 +01:00
// Removes old textures.
2013-01-30 20:40:26 +01:00
void TextureCache : : Decimate ( ) {
2013-08-01 00:13:58 +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_ ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2014-12-21 16:54:26 -08:00
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 14:43:45 -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_ ) ;
2012-11-22 23:07:15 +01:00
}
2013-12-29 14:43:45 -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 14:43:45 -08:00
for ( TexCache : : iterator iter = secondCache . begin ( ) ; iter ! = secondCache . end ( ) ; ) {
2013-12-29 14:46:46 -08:00
// In low memory mode, we kill them all.
if ( lowMemoryMode_ | | iter - > second . lastFrame + TEXTURE_SECOND_KILL_AGE < gpuStats . numFlips ) {
2015-03-15 19:46:36 -07:00
glDeleteTextures ( 1 , & iter - > second . textureName ) ;
2014-12-21 16:54:26 -08:00
secondCacheSizeEstimate_ - = EstimateTexMemoryUsage ( & iter - > second ) ;
2013-12-29 14:43:45 -08:00
secondCache . erase ( iter + + ) ;
} else {
+ + iter ;
}
2013-05-04 20:33:31 -07: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-05-04 20:33:31 -07:00
}
2012-11-22 23:07:15 +01:00
}
2013-05-05 22:52:01 -07:00
void TextureCache : : Invalidate ( u32 addr , int size , GPUInvalidationType type ) {
2013-12-29 14:43:45 -08:00
// If we're hashing every use, without backoff, then this isn't needed.
if ( ! g_Config . bTextureBackoffCache ) {
return ;
}
2014-06-14 09:50:54 -07:00
addr & = 0x3FFFFFFF ;
2014-04-14 07:57:28 -07:00
const u32 addr_end = addr + size ;
2012-12-21 23:43:48 +01:00
2013-06-30 20:52:15 -07:00
// They could invalidate inside the texture, let's just give a bit of leeway.
const int LARGEST_TEXTURE_SIZE = 512 * 512 * 4 ;
2014-04-14 07:57:28 -07:00
const u64 startKey = ( u64 ) ( addr - LARGEST_TEXTURE_SIZE ) < < 32 ;
2014-04-13 16:25:09 -07:00
u64 endKey = ( u64 ) ( addr + size + LARGEST_TEXTURE_SIZE ) < < 32 ;
2014-04-14 07:57:28 -07:00
if ( endKey < startKey ) {
endKey = ( u64 ) - 1 ;
}
2013-06-30 20:52:15 -07:00
for ( TexCache : : iterator iter = cache . lower_bound ( startKey ) , end = cache . upper_bound ( endKey ) ; iter ! = end ; + + iter ) {
2013-01-22 19:18:48 +01:00
u32 texAddr = iter - > second . addr ;
u32 texEnd = iter - > second . addr + iter - > second . sizeInRAM ;
2013-05-11 15:52:41 -07:00
2013-06-05 00:43:42 +08:00
if ( texAddr < addr_end & & addr < texEnd ) {
2014-04-13 12:13:02 -07:00
if ( iter - > second . GetHashStatus ( ) = = TexCacheEntry : : STATUS_RELIABLE ) {
iter - > second . SetHashStatus ( TexCacheEntry : : STATUS_HASHING ) ;
2013-02-10 14:11:53 -08:00
}
2013-05-05 22:52:01 -07:00
if ( type ! = GPU_INVALIDATE_ALL ) {
2013-01-06 12:27:01 +01:00
gpuStats . numTextureInvalidations + + ;
2013-05-05 22:52:01 -07:00
// Start it over from 0 (unless it's safe.)
iter - > second . numFrames = type = = GPU_INVALIDATE_SAFE ? 256 : 0 ;
2015-12-30 13:31:16 -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-02-10 23:29:44 -08:00
iter - > second . framesUntilNextFullHash = 0 ;
2013-10-20 20:32:02 -07:00
} else if ( ! iter - > second . framebuffer ) {
2013-01-05 17:04:56 -08:00
iter - > second . invalidHint + + ;
2013-01-02 23:21:02 -08:00
}
2012-12-21 23:43:48 +01:00
}
}
}
2013-06-05 00:43:42 +08:00
void TextureCache : : InvalidateAll ( GPUInvalidationType /*unused*/ ) {
2013-12-29 14:43:45 -08:00
// If we're hashing every use, without backoff, then this isn't needed.
if ( ! g_Config . bTextureBackoffCache ) {
return ;
}
2014-06-15 00:40:08 +02:00
if ( timesInvalidatedAllThisFrame_ > 5 ) {
return ;
}
timesInvalidatedAllThisFrame_ + + ;
2013-06-05 00:43:42 +08:00
for ( TexCache : : iterator iter = cache . begin ( ) , end = cache . end ( ) ; iter ! = end ; + + iter ) {
2014-04-13 12:13:02 -07:00
if ( iter - > second . GetHashStatus ( ) = = TexCacheEntry : : STATUS_RELIABLE ) {
iter - > second . SetHashStatus ( TexCacheEntry : : STATUS_HASHING ) ;
2013-06-05 00:43:42 +08:00
}
2013-10-20 20:32:02 -07:00
if ( ! iter - > second . framebuffer ) {
iter - > second . invalidHint + + ;
}
2013-06-05 00:43:42 +08:00
}
2012-11-22 23:07:15 +01:00
}
2013-04-30 17:20:28 +02:00
void TextureCache : : ClearNextFrame ( ) {
clearCacheNextFrame_ = true ;
}
2014-06-14 09:50:54 -07:00
bool TextureCache : : AttachFramebuffer ( TexCacheEntry * entry , u32 address , VirtualFramebuffer * framebuffer , u32 texaddrOffset ) {
static const u32 MAX_SUBAREA_Y_OFFSET_SAFE = 32 ;
2014-06-14 09:14:00 -07:00
2014-06-08 13:52:05 -07:00
AttachedFramebufferInfo fbInfo = { 0 } ;
2014-06-14 09:50:54 -07:00
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 ;
2014-06-26 00:48:48 -07:00
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 ;
2014-06-14 09:50:54 -07:00
2013-07-31 23:19:15 -07:00
// If they match exactly, it's non-CLUT and from the top left.
if ( exactMatch ) {
2013-12-11 16:59:20 +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-06-14 09:50:54 -07:00
return false ;
2013-12-11 16:59:20 +01:00
2013-09-07 13:31:14 -07:00
DEBUG_LOG ( G3D , " Render to texture detected at %08x! " , address ) ;
2014-06-21 08:23:10 -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 ) ;
}
2015-01-18 13:26:26 -08:00
if ( entry - > format ! = ( GETextureFormat ) framebuffer - > format ) {
2014-06-21 08:23:10 -07:00
WARN_LOG_REPORT_ONCE ( diffFormat1 , G3D , " Render to texture with different formats %d != %d " , entry - > format , framebuffer - > format ) ;
2014-06-22 21:02:43 -07:00
// 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 ) ;
}
2014-06-21 08:23:10 -07:00
} else {
AttachFramebufferValid ( entry , framebuffer , fbInfo ) ;
return true ;
2013-07-31 23:19:15 -07:00
}
2013-09-16 20:25:29 +08:00
} else {
2013-09-21 09:56:32 +08:00
// Apply to buffered mode only.
2013-09-16 20:25:29 +08:00
if ( ! ( g_Config . iRenderingMode = = FB_BUFFERED_MODE ) )
2014-06-14 09:50:54 -07:00
return false ;
2013-09-16 20:25:29 +08:00
2014-06-14 09:14:00 -07:00
const bool clutFormat =
( framebuffer - > format = = GE_FORMAT_8888 & & entry - > format = = GE_TFMT_CLUT32 ) | |
( framebuffer - > format ! = GE_FORMAT_8888 & & entry - > format = = GE_TFMT_CLUT16 ) ;
2014-06-14 09:50:54 -07:00
const u32 bitOffset = ( texaddr - addr ) * 8 ;
2014-06-14 09:14:00 -07:00
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 ;
2014-06-14 09:14:00 -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 ) ;
2014-06-14 09:50:54 -07:00
return false ;
2014-06-14 09:14:00 -07:00
}
}
2014-06-26 00:48:48 -07:00
if ( fbInfo . yOffset + minSubareaHeight > = framebuffer - > height ) {
2014-06-14 09:50:54 -07:00
// 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 ) {
2014-06-14 10:03:01 -07:00
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 ) ;
2014-06-14 09:50:54 -07:00
DetachFramebuffer ( entry , address , framebuffer ) ;
return false ;
}
2014-03-29 21:58:38 +01:00
// 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.
2014-06-14 09:14:00 -07:00
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 ) ;
}
2014-06-09 22:45:14 -07:00
AttachFramebufferValid ( entry , framebuffer , fbInfo ) ;
2014-05-10 13:15:39 -07:00
entry - > status | = TexCacheEntry : : STATUS_DEPALETTIZE ;
2014-06-14 09:50:54 -07:00
// We'll validate it compiles later.
return true ;
2014-04-01 14:14:56 +02:00
} 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 " ) ;
2014-03-29 21:58:38 +01:00
}
2013-07-31 23:19:15 -07:00
2014-06-14 09:50:54 -07:00
// This is either normal or we failed to generate a shader to depalettize
if ( framebuffer - > format = = entry - > format | | clutFormat ) {
if ( framebuffer - > format ! = entry - > format ) {
WARN_LOG_REPORT_ONCE ( diffFormat2 , G3D , " Render to texture with different formats %d != %d at %08x " , entry - > format , framebuffer - > format , address ) ;
AttachFramebufferValid ( entry , framebuffer , fbInfo ) ;
return true ;
2014-06-14 10:03:01 -07:00
} else {
2014-06-14 09:50:54 -07:00
WARN_LOG_REPORT_ONCE ( subarea , G3D , " Render to area containing texture at %08x +%dx%d " , address , fbInfo . xOffset , fbInfo . yOffset ) ;
// If "AttachFramebufferValid" , God of War Ghost of Sparta/Chains of Olympus will be missing special effect.
AttachFramebufferInvalid ( entry , framebuffer , fbInfo ) ;
return true ;
2013-07-31 23:19:15 -07:00
}
2014-06-14 09:50:54 -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-07-31 23:19:15 -07:00
}
}
2014-06-14 09:50:54 -07:00
return false ;
2013-07-31 23:19:15 -07:00
}
2014-01-19 16:13:55 -08:00
void * TextureCache : : ReadIndexedTex ( int level , const u8 * texptr , int bytesPerIndex , GLuint dstFmt , int bufw ) {
2013-07-31 01:52:29 +08:00
int w = gstate . getTextureWidth ( level ) ;
int h = gstate . getTextureHeight ( level ) ;
2013-04-28 13:14:25 -07:00
int length = bufw * h ;
2012-11-08 23:26:30 +01:00
void * buf = NULL ;
2013-07-21 19:34:55 -07:00
switch ( gstate . getClutPaletteFormat ( ) ) {
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_BGR5650 :
case GE_CMODE_16BIT_ABGR5551 :
case GE_CMODE_16BIT_ABGR4444 :
2012-11-01 16:19:01 +01:00
{
2013-04-28 13:14:25 -07:00
tmpTexBuf16 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
2013-05-11 15:45:17 -07:00
const u16 * clut = GetCurrentClut < u16 > ( ) ;
2013-08-24 10:51:17 -07:00
if ( ! gstate . isTextureSwizzled ( ) ) {
2013-01-06 12:11:47 +01:00
switch ( bytesPerIndex ) {
2012-11-08 23:26:30 +01:00
case 1 :
2014-01-19 16:13:55 -08:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( const u8 * ) texptr , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
case 2 :
2014-01-19 16:13:55 -08:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( const u16_le * ) texptr , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
case 4 :
2014-01-19 16:13:55 -08:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( const u32_le * ) texptr , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
}
2013-01-06 12:11:47 +01:00
} else {
2013-04-28 13:14:25 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2016-03-26 21:50:49 -07:00
UnswizzleFromMem ( tmpTexBuf32 . data ( ) , bufw * bytesPerIndex , texptr , bufw , h , bytesPerIndex ) ;
2013-01-06 12:11:47 +01:00
switch ( bytesPerIndex ) {
2012-11-08 23:26:30 +01:00
case 1 :
2013-05-11 15:45:17 -07:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( u8 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
case 2 :
2013-05-11 15:45:17 -07:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( u16 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
case 4 :
2013-05-11 15:45:17 -07:00
DeIndexTexture ( tmpTexBuf16 . data ( ) , ( u32 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
}
}
2013-04-09 01:52:19 -07:00
buf = tmpTexBuf16 . data ( ) ;
2012-11-08 23:26:30 +01:00
}
break ;
case GE_CMODE_32BIT_ABGR8888 :
{
2013-04-28 13:14:25 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
2013-05-11 15:45:17 -07:00
const u32 * clut = GetCurrentClut < u32 > ( ) ;
2013-08-24 10:51:17 -07:00
if ( ! gstate . isTextureSwizzled ( ) ) {
2013-01-06 12:11:47 +01:00
switch ( bytesPerIndex ) {
2012-11-08 23:26:30 +01:00
case 1 :
2014-01-19 16:13:55 -08:00
DeIndexTexture ( tmpTexBuf32 . data ( ) , ( const u8 * ) texptr , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
case 2 :
2014-01-19 16:13:55 -08:00
DeIndexTexture ( tmpTexBuf32 . data ( ) , ( const u16_le * ) texptr , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
case 4 :
2014-01-19 16:13:55 -08:00
DeIndexTexture ( tmpTexBuf32 . data ( ) , ( const u32_le * ) texptr , length , clut ) ;
2012-11-08 23:26:30 +01:00
break ;
}
2013-04-17 01:07:00 -07:00
buf = tmpTexBuf32 . data ( ) ;
2013-01-06 12:11:47 +01:00
} else {
2016-03-26 21:50:49 -07:00
UnswizzleFromMem ( tmpTexBuf32 . data ( ) , bufw * bytesPerIndex , texptr , bufw , h , bytesPerIndex ) ;
2013-04-17 01:07:00 -07:00
// Since we had to unswizzle to tmpTexBuf32, let's output to tmpTexBuf16.
2013-04-28 13:14:25 -07:00
tmpTexBuf16 . resize ( std : : max ( bufw , w ) * h * 2 ) ;
2013-04-17 01:07:00 -07:00
u32 * dest32 = ( u32 * ) tmpTexBuf16 . data ( ) ;
2013-01-06 12:11:47 +01:00
switch ( bytesPerIndex ) {
2012-11-08 23:26:30 +01:00
case 1 :
2013-05-11 15:45:17 -07:00
DeIndexTexture ( dest32 , ( u8 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
2013-04-17 01:07:00 -07:00
buf = dest32 ;
2012-11-08 23:26:30 +01:00
break ;
case 2 :
2013-05-11 15:45:17 -07:00
DeIndexTexture ( dest32 , ( u16 * ) tmpTexBuf32 . data ( ) , length , clut ) ;
2013-04-17 01:07:00 -07:00
buf = dest32 ;
2012-11-08 23:26:30 +01:00
break ;
case 4 :
2013-05-11 15:45:17 -07:00
// TODO: If a game actually uses this mode, check if using dest32 or tmpTexBuf32 is faster.
DeIndexTexture ( tmpTexBuf32 . data ( ) , tmpTexBuf32 . data ( ) , length , clut ) ;
2013-04-17 01:07:00 -07:00
buf = tmpTexBuf32 . data ( ) ;
2012-11-08 23:26:30 +01:00
break ;
}
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
}
break ;
2012-11-01 16:19:01 +01:00
2012-11-08 23:26:30 +01:00
default :
2013-09-07 13:43:07 -07:00
ERROR_LOG_REPORT ( G3D , " Unhandled clut texture mode %d!!! " , ( gstate . clutformat & 3 ) ) ;
2012-11-08 23:26:30 +01:00
break ;
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
return buf ;
}
2013-01-06 12:11:47 +01:00
GLenum getClutDestFormat ( GEPaletteFormat format ) {
switch ( format ) {
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_ABGR4444 :
2012-11-09 01:24:19 +01:00
return GL_UNSIGNED_SHORT_4_4_4_4 ;
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_ABGR5551 :
2012-11-09 01:24:19 +01:00
return GL_UNSIGNED_SHORT_5_5_5_1 ;
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_BGR5650 :
2012-11-09 01:24:19 +01:00
return GL_UNSIGNED_SHORT_5_6_5 ;
2012-11-08 23:26:30 +01:00
case GE_CMODE_32BIT_ABGR8888 :
return GL_UNSIGNED_BYTE ;
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2013-02-08 00:04:34 +01:00
static const u8 texByteAlignMap [ ] = { 2 , 2 , 2 , 4 } ;
2012-11-08 23:26:30 +01:00
2013-02-08 00:04:34 +01:00
static const GLuint MinFiltGL [ 8 ] = {
2013-01-06 17:44:14 +01:00
GL_NEAREST ,
GL_LINEAR ,
GL_NEAREST ,
GL_LINEAR ,
GL_NEAREST_MIPMAP_NEAREST ,
GL_LINEAR_MIPMAP_NEAREST ,
GL_NEAREST_MIPMAP_LINEAR ,
GL_LINEAR_MIPMAP_LINEAR ,
} ;
2013-02-08 00:04:34 +01:00
static const GLuint MagFiltGL [ 2 ] = {
2013-01-06 17:44:14 +01:00
GL_NEAREST ,
GL_LINEAR
} ;
2014-06-15 13:34:58 -07:00
// This should not have to be done per texture! OpenGL is silly yo
void TextureCache : : 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 ) {
if ( force | | entry . lodBias ! = lodBias ) {
2015-09-05 23:09:06 +02:00
if ( gstate_c . Supports ( GPU_SUPPORTS_TEXTURE_LOD_CONTROL ) ) {
GETexLevelMode mode = gstate . getTexLevelMode ( ) ;
switch ( mode ) {
case GE_TEXLEVEL_MODE_AUTO :
// TODO
break ;
case GE_TEXLEVEL_MODE_CONST :
// Sigh, LOD_BIAS is not even in ES 3.0..
2014-06-15 13:34:58 -07:00
# ifndef USING_GLES2
2015-09-05 23:09:06 +02:00
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_LOD_BIAS , lodBias ) ;
2014-06-15 13:34:58 -07:00
# endif
2015-09-05 23:09:06 +02:00
break ;
case GE_TEXLEVEL_MODE_SLOPE :
// TODO
break ;
}
}
2014-06-15 13:34:58 -07:00
entry . lodBias = lodBias ;
}
}
2013-06-17 20:45:08 +02:00
2013-01-06 17:44:14 +01:00
if ( force | | entry . minFilt ! = minFilt ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , MinFiltGL [ minFilt ] ) ;
entry . minFilt = minFilt ;
}
if ( force | | entry . magFilt ! = magFilt ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , MagFiltGL [ magFilt ] ) ;
entry . magFilt = magFilt ;
}
2013-10-08 15:00:48 +02:00
2014-06-15 13:34:58 -07:00
if ( entry . framebuffer ) {
WARN_LOG_REPORT_ONCE ( wrongFramebufAttach , G3D , " Framebuffer still attached in UpdateSamplingParams()? " ) ;
2014-06-07 12:21:52 -07:00
}
2013-10-08 15:00:48 +02:00
2013-09-29 10:30:25 +08:00
if ( force | | entry . sClamp ! = sClamp ) {
2013-01-06 17:44:14 +01:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT ) ;
entry . sClamp = sClamp ;
2013-09-29 10:30:25 +08:00
}
if ( force | | entry . tClamp ! = tClamp ) {
2013-01-06 17:44:14 +01:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT ) ;
entry . tClamp = tClamp ;
2012-12-28 23:23:42 +08:00
}
2012-11-01 16:19:01 +01:00
}
2014-06-15 13:34:58 -07:00
void TextureCache : : SetFramebufferSamplingParams ( u16 bufferWidth , u16 bufferHeight ) {
int minFilt ;
int magFilt ;
bool sClamp ;
bool tClamp ;
float lodBias ;
GetSamplingParams ( minFilt , magFilt , sClamp , tClamp , lodBias , 0 ) ;
2014-12-14 01:24:01 +01:00
minFilt & = 1 ; // framebuffers can't mipmap.
2014-06-15 13:34:58 -07:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , MinFiltGL [ minFilt ] ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , MagFiltGL [ 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 ;
}
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT ) ;
}
2013-05-26 13:31:58 -07:00
static void ConvertColors ( void * dstBuf , const void * srcBuf , GLuint dstFmt , int numPixels ) {
const u32 * src = ( const u32 * ) srcBuf ;
u32 * dst = ( u32 * ) dstBuf ;
2012-11-09 01:24:19 +01:00
switch ( dstFmt ) {
case GL_UNSIGNED_SHORT_4_4_4_4 :
2015-05-17 13:45:30 -07:00
ConvertRGBA4444ToABGR4444 ( ( u16 * ) dst , ( const u16 * ) src , numPixels ) ;
2012-11-09 01:24:19 +01:00
break ;
2013-10-31 00:34:18 -07:00
// Final Fantasy 2 uses this heavily in animated textures.
2012-11-09 01:24:19 +01:00
case GL_UNSIGNED_SHORT_5_5_5_1 :
2015-05-17 13:45:30 -07:00
ConvertRGBA5551ToABGR1555 ( ( u16 * ) dst , ( const u16 * ) src , numPixels ) ;
2012-11-09 01:24:19 +01:00
break ;
case GL_UNSIGNED_SHORT_5_6_5 :
2015-05-17 13:45:30 -07:00
ConvertRGB565ToBGR565 ( ( u16 * ) dst , ( const u16 * ) src , numPixels ) ;
2012-11-09 01:24:19 +01:00
break ;
default :
2014-04-13 18:34:40 -07:00
if ( UseBGRA8888 ( ) ) {
2015-05-17 13:45:30 -07:00
ConvertRGBA8888ToBGRA8888 ( dst , src , numPixels ) ;
2014-04-13 18:34:40 -07:00
} else {
2012-11-29 15:06:54 +01:00
// No need to convert RGBA8888, right order already
2013-06-22 10:16:38 -07:00
if ( dst ! = src )
memcpy ( dst , src , numPixels * sizeof ( u32 ) ) ;
2012-11-09 01:24:19 +01:00
}
break ;
}
}
2013-01-30 20:40:26 +01:00
void TextureCache : : StartFrame ( ) {
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2014-06-15 00:40:08 +02:00
timesInvalidatedAllThisFrame_ = 0 ;
2014-06-13 22:07:26 +02:00
2014-06-13 22:11:25 +02:00
if ( texelsScaledThisFrame_ ) {
// INFO_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_);
}
2014-06-13 22:07:26 +02:00
texelsScaledThisFrame_ = 0 ;
2013-12-29 15:10:07 -08:00
if ( clearCacheNextFrame_ ) {
2013-04-30 17:20:28 +02:00
Clear ( true ) ;
clearCacheNextFrame_ = false ;
} else {
Decimate ( ) ;
}
2013-01-10 23:49:33 +01:00
}
2013-02-10 13:37:05 -08: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 , TextureCache : : 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-02-10 13:37:05 -08:00
const u32 * checkp = ( const u32 * ) Memory : : GetPointer ( addr ) ;
2013-05-04 21:30:18 -07:00
2013-11-02 02:09:54 -07:00
return DoQuickTexHash ( checkp , sizeInRAM ) ;
2013-02-21 22:37:45 -08:00
}
2015-07-29 11:38:42 +02:00
void TextureCache : : UpdateCurrentClut ( GEPaletteFormat clutFormat , u32 clutBase , bool clutIndexIsSimple ) {
2015-07-20 22:18:26 +02:00
const u32 clutBaseBytes = clutFormat = = GE_CMODE_32BIT_ABGR8888 ? ( clutBase * sizeof ( u32 ) ) : ( clutBase * sizeof ( u16 ) ) ;
2013-06-15 23:04:43 -07:00
// 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:31:00 -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.
2015-04-26 00:43:36 -07:00
const u32 clutExtendedBytes = std : : min ( clutTotalBytes_ + clutBaseBytes , clutMaxBytes_ ) ;
2013-06-15 23:04:43 -07:00
2014-10-26 17:49:24 -07:00
clutHash_ = DoReliableHash32 ( ( const char * ) clutBufRaw_ , clutExtendedBytes , 0xC0108888 ) ;
2013-05-26 12:45:53 -07:00
// Avoid a copy when we don't need to convert colors.
2014-04-13 18:34:40 -07:00
if ( UseBGRA8888 ( ) | | clutFormat ! = GE_CMODE_32BIT_ABGR8888 ) {
2015-07-20 22:18:26 +02:00
const int numColors = clutFormat = = GE_CMODE_32BIT_ABGR8888 ? ( clutMaxBytes_ / sizeof ( u32 ) ) : ( clutMaxBytes_ / sizeof ( u16 ) ) ;
2014-08-19 22:28:26 -07:00
ConvertColors ( clutBufConverted_ , clutBufRaw_ , getClutDestFormat ( clutFormat ) , numColors ) ;
2013-05-26 12:45:53 -07:00
clutBuf_ = clutBufConverted_ ;
} else {
clutBuf_ = clutBufRaw_ ;
}
2013-05-12 10:57:41 -07: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-05-12 10:57:41 -07:00
clutAlphaLinear_ = true ;
clutAlphaLinearColor_ = clut [ 15 ] & 0xFFF0 ;
for ( int i = 0 ; i < 16 ; + + i ) {
2016-01-01 09:02:16 -08:00
u16 step = clutAlphaLinearColor_ | i ;
if ( clut [ i ] ! = step ) {
2013-05-12 10:57:41 -07:00
clutAlphaLinear_ = false ;
break ;
}
}
}
2013-05-26 12:45:53 -07:00
2013-06-15 23:04:43 -07:00
clutLastFormat_ = gstate . clutformat ;
2013-05-11 15:45:17 -07:00
}
template < typename T >
inline const T * TextureCache : : GetCurrentClut ( ) {
return ( const T * ) clutBuf_ ;
}
inline u32 TextureCache : : GetCurrentClutHash ( ) {
return clutHash_ ;
}
2013-06-22 10:35:34 -07:00
// #define DEBUG_TEXTURES
# ifdef DEBUG_TEXTURES
bool SetDebugTexture ( ) {
static const int highlightFrames = 30 ;
static int numTextures = 0 ;
static int lastFrames = 0 ;
static int mostTextures = 1 ;
2013-08-07 22:32:04 +02:00
if ( lastFrames ! = gpuStats . numFlips ) {
2013-06-22 10:35:34 -07:00
mostTextures = std : : max ( mostTextures , numTextures ) ;
numTextures = 0 ;
2013-08-07 22:32:04 +02:00
lastFrames = gpuStats . numFlips ;
2013-06-22 10:35:34 -07:00
}
static GLuint solidTexture = 0 ;
bool changed = false ;
2013-08-07 22:32:04 +02:00
if ( ( ( gpuStats . numFlips / highlightFrames ) % mostTextures ) = = numTextures ) {
if ( gpuStats . numFlips % highlightFrames = = 0 ) {
2013-09-07 13:31:14 -07:00
NOTICE_LOG ( G3D , " Highlighting texture # %d / %d " , numTextures , mostTextures ) ;
2013-06-22 10:35:34 -07:00
}
static const u32 solidTextureData [ ] = { 0x99AA99FF } ;
if ( solidTexture = = 0 ) {
glGenTextures ( 1 , & solidTexture ) ;
glBindTexture ( GL_TEXTURE_2D , solidTexture ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
glPixelStorei ( GL_PACK_ALIGNMENT , 1 ) ;
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , 1 , 1 , 0 , GL_RGBA , GL_UNSIGNED_BYTE , solidTextureData ) ;
} else {
glBindTexture ( GL_TEXTURE_2D , solidTexture ) ;
}
changed = true ;
}
+ + numTextures ;
return changed ;
}
# endif
2014-06-04 23:50:02 -07:00
void TextureCache : : SetTextureFramebuffer ( TexCacheEntry * entry , VirtualFramebuffer * framebuffer ) {
2014-07-09 08:21:56 -07:00
_dbg_assert_msg_ ( G3D , framebuffer ! = nullptr , " Framebuffer must not be null. " ) ;
2014-07-09 06:11:41 -04:00
2014-06-04 23:50:02 -07:00
framebuffer - > usageFlags | = FB_USAGE_TEXTURE ;
2013-09-01 11:40:35 -07: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 ( ) ;
2016-03-26 12:07:49 -07:00
const u64 cachekey = entry - > CacheKey ( ) ;
const auto & fbInfo = fbTexInfo_ [ cachekey ] ;
2015-03-15 14:43:07 -07:00
DepalShader * depal = nullptr ;
2014-07-08 23:32:41 -07:00
if ( ( entry - > status & TexCacheEntry : : STATUS_DEPALETTIZE ) & & ! g_Config . bDisableSlowFramebufEffects ) {
2015-07-29 14:28:40 +02:00
depal = depalShaderCache_ - > GetDepalettizeShader ( clutFormat , framebuffer - > drawnFormat ) ;
2014-05-10 13:15:39 -07:00
}
2015-03-15 14:43:07 -07:00
if ( depal ) {
2014-06-03 01:06:02 -07:00
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-06-03 01:06:02 -07:00
2015-07-29 14:28:40 +02:00
TexCacheEntry : : Status alphaStatus = CheckAlpha ( clutBuf_ , getClutDestFormat ( clutFormat ) , clutTotalColors , clutTotalColors , 1 ) ;
2014-06-03 01:06:02 -07:00
gstate_c . textureFullAlpha = alphaStatus = = TexCacheEntry : : STATUS_ALPHA_FULL ;
gstate_c . textureSimpleAlpha = alphaStatus = = TexCacheEntry : : STATUS_ALPHA_SIMPLE ;
2014-03-30 00:11:01 +01:00
} else {
2014-05-10 13:15:39 -07:00
entry - > status & = ~ TexCacheEntry : : STATUS_DEPALETTIZE ;
2014-06-03 00:45:15 -07:00
2015-02-21 18:08:50 -08:00
gstate_c . textureFullAlpha = gstate . getTextureFormat ( ) = = GE_TFMT_5650 ;
2014-06-03 00:45:15 -07:00
gstate_c . textureSimpleAlpha = gstate_c . textureFullAlpha ;
2014-03-29 21:58:38 +01:00
}
2014-01-19 17:28:11 -08:00
// Keep the framebuffer alive.
2014-06-04 23:50:02 -07:00
framebuffer - > last_frame_used = gpuStats . numFlips ;
2014-01-19 17:28:11 -08:00
2013-12-15 18:23:47 -08:00
// We need to force it, since we may have set it on a texture before attaching.
2014-06-04 23:50:02 -07:00
gstate_c . curTextureWidth = framebuffer - > bufferWidth ;
gstate_c . curTextureHeight = framebuffer - > bufferHeight ;
2016-03-26 12:07:49 -07:00
gstate_c . curTextureXOffset = fbInfo . xOffset ;
gstate_c . curTextureYOffset = fbInfo . yOffset ;
2014-06-19 00:47:58 -07:00
gstate_c . needShaderTexClamp = gstate_c . curTextureWidth ! = ( u32 ) gstate . getTextureWidth ( 0 ) | | gstate_c . curTextureHeight ! = ( u32 ) gstate . getTextureHeight ( 0 ) ;
2014-06-08 13:52:05 -07:00
if ( gstate_c . curTextureXOffset ! = 0 | | gstate_c . curTextureYOffset ! = 0 ) {
gstate_c . needShaderTexClamp = true ;
}
2015-03-15 19:10:33 -07:00
nextTexture_ = entry ;
2013-09-01 11:40:35 -07:00
} else {
2014-09-08 07:41:23 -07:00
if ( framebuffer - > fbo ) {
fbo_destroy ( framebuffer - > fbo ) ;
2014-06-04 23:50:02 -07:00
framebuffer - > fbo = 0 ;
2014-09-08 07:41:23 -07:00
}
2013-09-01 11:40:35 -07:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2014-06-08 14:00:13 -07:00
gstate_c . needShaderTexClamp = false ;
2013-09-01 11:40:35 -07:00
}
}
2015-03-15 19:10:33 -07:00
void TextureCache : : ApplyTexture ( ) {
if ( nextTexture_ = = nullptr ) {
return ;
}
if ( nextTexture_ - > framebuffer ) {
ApplyTextureFramebuffer ( nextTexture_ , nextTexture_ - > framebuffer ) ;
} else {
2016-01-22 18:48:54 -08:00
UpdateMaxSeenV ( gstate . isModeThrough ( ) ) ;
2015-03-15 21:38:01 -07:00
2015-10-01 22:56:31 -07:00
if ( nextTexture_ - > textureName ! = lastBoundTexture ) {
glBindTexture ( GL_TEXTURE_2D , nextTexture_ - > textureName ) ;
lastBoundTexture = nextTexture_ - > textureName ;
}
2015-03-15 19:10:33 -07:00
UpdateSamplingParams ( * nextTexture_ , false ) ;
}
nextTexture_ = nullptr ;
}
2016-01-04 21:29:03 -08:00
void TextureCache : : DownloadFramebufferForClut ( u32 clutAddr , u32 bytes ) {
2016-01-05 00:02:58 -08:00
framebufferManager_ - > DownloadFramebufferForClut ( clutAddr , bytes ) ;
2016-01-04 21:29:03 -08:00
}
2016-01-03 23:44:06 -08:00
class TextureShaderApplier {
public :
struct Pos {
Pos ( float x_ , float y_ , float z_ ) : x ( x_ ) , y ( y_ ) , z ( z_ ) {
}
Pos ( ) {
}
2015-09-13 11:14:51 -07:00
2016-01-03 23:44:06 -08:00
float x ;
float y ;
float z ;
} ;
struct UV {
UV ( float u_ , float v_ ) : u ( u_ ) , v ( v_ ) {
}
UV ( ) {
}
2015-09-13 11:14:51 -07:00
2016-01-03 23:44:06 -08:00
float u ;
float v ;
} ;
TextureShaderApplier ( DepalShader * shader , float bufferW , float bufferH , int renderW , int renderH )
: shader_ ( shader ) , bufferW_ ( bufferW ) , bufferH_ ( bufferH ) , renderW_ ( renderW ) , renderH_ ( renderH ) {
static const Pos pos [ 4 ] = {
2015-09-13 11:14:51 -07:00
{ - 1 , - 1 , - 1 } ,
{ 1 , - 1 , - 1 } ,
{ 1 , 1 , - 1 } ,
{ - 1 , 1 , - 1 } ,
} ;
2016-01-03 23:44:06 -08:00
memcpy ( pos_ , pos , sizeof ( pos_ ) ) ;
static const UV uv [ 4 ] = {
2015-09-13 11:14:51 -07:00
{ 0 , 0 } ,
{ 1 , 0 } ,
{ 1 , 1 } ,
{ 0 , 1 } ,
2015-03-15 19:10:33 -07:00
} ;
2016-01-03 23:44:06 -08:00
memcpy ( uv_ , uv , sizeof ( uv_ ) ) ;
}
2015-03-15 19:10:33 -07:00
2016-01-03 23:44:06 -08:00
void ApplyBounds ( const KnownVertexBounds & bounds , u32 uoff , u32 voff ) {
2015-09-13 11:14:51 -07:00
// If min is not < max, then we don't have values (wasn't set during decode.)
2016-01-03 23:44:06 -08:00
if ( bounds . minV < bounds . maxV ) {
const float invWidth = 1.0f / bufferW_ ;
const float invHeight = 1.0f / bufferH_ ;
2015-09-13 11:14:51 -07:00
// Inverse of half = double.
const float invHalfWidth = invWidth * 2.0f ;
const float invHalfHeight = invHeight * 2.0f ;
2016-01-03 23:44:06 -08:00
const int u1 = bounds . minU + uoff ;
const int v1 = bounds . minV + voff ;
const int u2 = bounds . maxU + uoff ;
const int v2 = bounds . maxV + voff ;
2015-09-12 19:43:02 -07:00
const float left = u1 * invHalfWidth - 1.0f ;
const float right = u2 * invHalfWidth - 1.0f ;
2015-10-31 23:59:23 +01:00
const float top = v1 * invHalfHeight - 1.0f ;
const float bottom = v2 * invHalfHeight - 1.0f ;
2015-09-13 11:14:51 -07:00
// Points are: BL, BR, TR, TL.
2016-01-03 23:44:06 -08:00
pos_ [ 0 ] = Pos ( left , bottom , - 1.0f ) ;
pos_ [ 1 ] = Pos ( right , bottom , - 1.0f ) ;
pos_ [ 2 ] = Pos ( right , top , - 1.0f ) ;
pos_ [ 3 ] = Pos ( left , top , - 1.0f ) ;
2015-09-13 11:14:51 -07:00
// 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-10-31 23:59:23 +01:00
const float uvtop = v1 * invHeight ;
const float uvbottom = v2 * invHeight ;
2016-01-03 23:44:06 -08:00
uv_ [ 0 ] = UV ( uvleft , uvbottom ) ;
uv_ [ 1 ] = UV ( uvright , uvbottom ) ;
uv_ [ 2 ] = UV ( uvright , uvtop ) ;
uv_ [ 3 ] = UV ( uvleft , uvtop ) ;
2015-09-13 11:14:51 -07:00
}
2016-01-03 23:44:06 -08:00
}
2015-09-13 11:14:51 -07:00
2016-04-10 10:21:48 +02:00
void Use ( DrawEngineGLES * transformDraw ) {
2016-01-03 23:44:06 -08:00
glUseProgram ( shader_ - > program ) ;
2015-03-15 19:10:33 -07:00
2015-10-07 22:11:24 -07:00
// Restore will rebind all of the state below.
2015-12-13 22:48:21 -08:00
if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) ) {
2016-01-03 23:44:06 -08:00
static const GLubyte indices [ 4 ] = { 0 , 1 , 3 , 2 } ;
transformDraw - > BindBuffer ( pos_ , sizeof ( pos_ ) , uv_ , sizeof ( uv_ ) ) ;
transformDraw - > BindElementBuffer ( indices , sizeof ( indices ) ) ;
2015-12-13 22:48:21 -08:00
} else {
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
}
2016-01-03 23:44:06 -08:00
glEnableVertexAttribArray ( shader_ - > a_position ) ;
glEnableVertexAttribArray ( shader_ - > a_texcoord0 ) ;
}
2015-03-15 19:10:33 -07:00
2016-01-03 23:44:06 -08:00
void Shade ( ) {
static const GLubyte indices [ 4 ] = { 0 , 1 , 3 , 2 } ;
2015-03-15 19:10:33 -07:00
2015-11-03 23:02:02 -08:00
glstate . blend . force ( false ) ;
glstate . colorMask . force ( true , true , true , true ) ;
glstate . scissorTest . force ( false ) ;
glstate . cullFace . force ( false ) ;
glstate . depthTest . force ( false ) ;
2015-11-04 19:40:53 -08:00
glstate . stencilTest . force ( false ) ;
2015-03-15 19:10:33 -07:00
# if !defined(USING_GLES2)
2015-11-03 23:02:02 -08:00
glstate . colorLogicOp . force ( false ) ;
2015-03-15 19:10:33 -07:00
# endif
2016-01-03 23:44:06 -08:00
glViewport ( 0 , 0 , renderW_ , renderH_ ) ;
2015-03-15 19:10:33 -07:00
2015-12-13 22:48:21 -08:00
if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) ) {
2016-01-03 23:44:06 -08:00
glVertexAttribPointer ( shader_ - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , 0 ) ;
glVertexAttribPointer ( shader_ - > a_texcoord0 , 2 , GL_FLOAT , GL_FALSE , 8 , ( void * ) sizeof ( pos_ ) ) ;
2015-12-13 22:48:21 -08:00
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_BYTE , 0 ) ;
} else {
2016-01-03 23:44:06 -08:00
glVertexAttribPointer ( shader_ - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , pos_ ) ;
glVertexAttribPointer ( shader_ - > a_texcoord0 , 2 , GL_FLOAT , GL_FALSE , 8 , uv_ ) ;
2015-12-13 22:48:21 -08:00
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_BYTE , indices ) ;
}
2016-01-03 23:44:06 -08:00
glDisableVertexAttribArray ( shader_ - > a_position ) ;
glDisableVertexAttribArray ( shader_ - > a_texcoord0 ) ;
2015-03-15 19:10:33 -07:00
glstate . Restore ( ) ;
2016-01-03 23:44:06 -08:00
}
protected :
DepalShader * shader_ ;
Pos pos_ [ 4 ] ;
UV uv_ [ 4 ] ;
float bufferW_ ;
float bufferH_ ;
int renderW_ ;
int renderH_ ;
} ;
void TextureCache : : ApplyTextureFramebuffer ( TexCacheEntry * entry , VirtualFramebuffer * framebuffer ) {
DepalShader * depal = nullptr ;
const GEPaletteFormat clutFormat = gstate . getClutPaletteFormat ( ) ;
if ( ( entry - > status & TexCacheEntry : : STATUS_DEPALETTIZE ) & & ! g_Config . bDisableSlowFramebufEffects ) {
depal = depalShaderCache_ - > GetDepalettizeShader ( clutFormat , framebuffer - > drawnFormat ) ;
}
if ( depal ) {
GLuint clutTexture = depalShaderCache_ - > GetClutTexture ( clutFormat , clutHash_ , clutBuf_ ) ;
FBO * depalFBO = framebufferManager_ - > GetTempFBO ( framebuffer - > renderWidth , framebuffer - > renderHeight , FBO_8888 ) ;
fbo_bind_as_render_target ( depalFBO ) ;
shaderManager_ - > DirtyLastShader ( ) ;
TextureShaderApplier shaderApply ( depal , framebuffer - > bufferWidth , framebuffer - > bufferHeight , framebuffer - > renderWidth , framebuffer - > renderHeight ) ;
shaderApply . ApplyBounds ( gstate_c . vertBounds , gstate_c . curTextureXOffset , gstate_c . curTextureYOffset ) ;
shaderApply . Use ( transformDraw_ ) ;
glActiveTexture ( GL_TEXTURE3 ) ;
glBindTexture ( GL_TEXTURE_2D , clutTexture ) ;
glActiveTexture ( GL_TEXTURE0 ) ;
framebufferManager_ - > BindFramebufferColor ( GL_TEXTURE0 , gstate . getFrameBufRawAddress ( ) , framebuffer , BINDFBCOLOR_SKIP_COPY ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
shaderApply . Shade ( ) ;
fbo_bind_color_as_texture ( depalFBO , 0 ) ;
2015-03-15 19:10:33 -07:00
} else {
2015-09-12 19:43:02 -07:00
framebufferManager_ - > BindFramebufferColor ( GL_TEXTURE0 , gstate . getFrameBufRawAddress ( ) , framebuffer , BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET ) ;
2015-03-15 19:10:33 -07:00
}
2015-09-13 11:14:51 -07:00
framebufferManager_ - > RebindFramebuffer ( ) ;
2015-03-15 19:10:33 -07:00
SetFramebufferSamplingParams ( framebuffer - > bufferWidth , framebuffer - > bufferHeight ) ;
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2015-03-15 19:10:33 -07:00
}
2014-06-04 23:50:02 -07:00
bool TextureCache : : 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 ) ;
2016-03-26 12:01:55 -07:00
u64 cachekey = TexCacheEntry : : CacheKey ( texaddr , gstate . getTextureFormat ( ) , dim , 0 ) ;
2014-06-04 23:50:02 -07:00
TexCache : : iterator iter = cache . find ( cachekey ) ;
if ( iter = = cache . end ( ) ) {
return false ;
}
TexCacheEntry * entry = & iter - > second ;
2014-06-14 09:50:54 -07:00
bool success = false ;
2014-06-04 23:50:02 -07:00
for ( size_t i = 0 , n = fbCache_ . size ( ) ; i < n ; + + i ) {
auto framebuffer = fbCache_ [ i ] ;
2014-06-14 09:50:54 -07:00
if ( AttachFramebuffer ( entry , framebuffer - > fb_address , framebuffer , offset ) ) {
success = true ;
2014-06-04 23:50:02 -07:00
}
}
2014-07-09 08:21:56 -07:00
if ( success & & entry - > framebuffer ) {
2015-03-15 19:10:33 -07:00
// This will not apply the texture immediately.
2014-06-21 09:04:39 -07:00
SetTextureFramebuffer ( entry , entry - > framebuffer ) ;
entry - > lastFrame = gpuStats . numFlips ;
2014-07-09 08:21:56 -07:00
return true ;
2014-06-21 09:04:39 -07:00
}
2014-07-09 08:21:56 -07:00
return false ;
2014-06-04 23:50:02 -07:00
}
2013-09-27 21:53:42 -07:00
void TextureCache : : SetTexture ( bool force ) {
2013-06-22 10:35:34 -07:00
# ifdef DEBUG_TEXTURES
if ( SetDebugTexture ( ) ) {
// A different texture was bound, let's rebind next time.
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2013-06-22 10:35:34 -07:00
return ;
}
# endif
2013-09-27 21:53:42 -07:00
if ( force ) {
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2013-09-27 21:53:42 -07:00
}
2013-09-15 21:39:28 -07:00
u32 texaddr = gstate . getTextureAddress ( 0 ) ;
2013-01-06 12:11:47 +01:00
if ( ! Memory : : IsValidAddress ( texaddr ) ) {
// Bind a null texture and return.
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2013-01-06 12:11:47 +01:00
return ;
}
2015-03-14 11:06:03 -07:00
const u16 dim = gstate . getTextureDimension ( 0 ) ;
2014-01-14 10:51:45 +01:00
int w = gstate . getTextureWidth ( 0 ) ;
int h = gstate . getTextureHeight ( 0 ) ;
2013-07-21 19:34:55 -07:00
GETextureFormat format = gstate . getTextureFormat ( ) ;
2013-02-01 00:02:50 +01:00
if ( format > = 11 ) {
2013-03-26 00:54:00 -07:00
ERROR_LOG_REPORT ( G3D , " Unknown texture format %i " , format ) ;
2013-07-21 19:34:55 -07:00
// TODO: Better assumption?
format = GE_TFMT_5650 ;
2013-02-01 00:02:50 +01:00
}
2013-07-21 19:34:55 -07:00
bool hasClut = gstate . isTextureFormatIndexed ( ) ;
2013-02-01 00:02:50 +01:00
2014-04-14 07:57:28 -07:00
// Ignore uncached/kernel when caching.
2013-07-07 18:37:00 -07:00
u32 cluthash ;
2013-02-12 21:00:51 +01:00
if ( hasClut ) {
2013-06-15 23:04:43 -07:00
if ( clutLastFormat_ ! = gstate . clutformat ) {
2013-05-12 10:57:41 -07:00
// 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-05-12 10:57:41 -07:00
}
2013-05-20 00:53:39 -07:00
cluthash = GetCurrentClutHash ( ) ^ gstate . clutformat ;
2013-02-18 08:34:51 -08:00
} else {
2013-05-04 20:33:31 -07:00
cluthash = 0 ;
2013-02-12 21:00:51 +01:00
}
2016-03-26 12:01:55 -07:00
u64 cachekey = TexCacheEntry : : CacheKey ( texaddr , format , dim , cluthash ) ;
2013-10-08 15:00:48 +02:00
2013-09-15 21:27:13 -07:00
int bufw = GetTextureBufw ( 0 , texaddr , format ) ;
2015-03-15 19:51:53 -07:00
u8 maxLevel = gstate . getTextureMaxLevel ( ) ;
2013-02-01 00:02:50 +01:00
2014-10-19 23:19:51 +02:00
u32 texhash = MiniHash ( ( const u32 * ) Memory : : GetPointerUnchecked ( texaddr ) ) ;
2013-05-11 14:58:00 -07:00
u32 fullhash = 0 ;
2013-02-02 12:37:41 +01:00
2012-11-24 10:58:10 -08:00
TexCache : : iterator iter = cache . find ( cachekey ) ;
2013-02-10 14:11:53 -08:00
TexCacheEntry * entry = NULL ;
2014-06-07 12:21:52 -07:00
gstate_c . needShaderTexClamp = false ;
2013-03-03 13:00:21 +01:00
gstate_c . skipDrawReason & = ~ SKIPDRAW_BAD_FB_TEXTURE ;
2013-06-11 20:21:19 +02:00
bool replaceImages = false ;
2013-10-08 15:00:48 +02:00
2013-01-06 12:11:47 +01:00
if ( iter ! = cache . end ( ) ) {
2013-02-10 14:11:53 -08:00
entry = & iter - > second ;
2013-09-06 23:40:23 -07: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-06 23:40:23 -07:00
2013-02-09 21:18:46 +01:00
// Check for FBO - slow!
2013-10-20 20:32:02 -07: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-06-04 23:50:02 -07:00
SetTextureFramebuffer ( entry , entry - > framebuffer ) ;
2013-10-20 20:32:02 -07: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-10-21 08:09:08 -07:00
match = false ;
2013-10-20 20:32:02 -07:00
}
2013-07-04 00:54:44 +08:00
}
2013-03-03 13:00:21 +01:00
2014-04-13 12:13:02 -07:00
bool rehash = entry - > GetHashStatus ( ) = = TexCacheEntry : : STATUS_UNRELIABLE ;
2013-05-04 20:33:31 -07:00
bool doDelete = true ;
2013-02-10 13:19:01 -08:00
2014-04-13 16:19:18 -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.
2014-04-13 14:02:00 -07:00
rehash = false ;
}
2013-02-10 13:19:01 -08:00
if ( match ) {
2013-08-07 22:32:04 +02:00
if ( entry - > lastFrame ! = gpuStats . numFlips ) {
2013-09-29 14:53:14 -07:00
u32 diff = gpuStats . numFlips - entry - > lastFrame ;
2013-02-10 14:11:53 -08:00
entry - > numFrames + + ;
2013-09-29 14:53:14 -07:00
if ( entry - > framesUntilNextFullHash < diff ) {
2013-09-29 15:26:56 -07:00
// Exponential backoff up to 512 frames. Textures are often reused.
2013-09-29 14:53:14 -07:00
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 ) + ( entry - > textureName & 15 ) ;
2013-09-29 14:53:14 -07:00
} else {
entry - > framesUntilNextFullHash = entry - > numFrames ;
}
rehash = true ;
} else {
entry - > framesUntilNextFullHash - = diff ;
}
2013-02-10 13:19:01 -08:00
}
2013-04-28 11:20:56 -07:00
// If it's not huge or has been invalidated many times, recheck the whole texture.
2014-06-14 00:18:37 +02:00
if ( entry - > invalidHint > 180 | | ( entry - > invalidHint > 15 & & ( dim > > 8 ) < 9 & & ( dim & 0xF ) < 9 ) ) {
2013-04-28 11:20:56 -07:00
entry - > invalidHint = 0 ;
rehash = true ;
}
2013-01-02 23:21:02 -08:00
2013-05-26 13:44:47 -07:00
bool hashFail = false ;
if ( texhash ! = entry - > hash ) {
2015-03-15 21:38:01 -07:00
fullhash = QuickTexHash ( texaddr , bufw , w , h , format , entry ) ;
2013-05-26 13:44:47 -07:00
hashFail = true ;
rehash = false ;
}
2014-04-13 12:13:02 -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-05-11 14:58:00 -07:00
if ( fullhash ! = entry - > fullhash ) {
2013-05-26 13:44:47 -07:00
hashFail = true ;
2015-12-30 13:43:10 -08:00
} else {
2013-12-29 14:43:45 -08:00
if ( g_Config . bTextureBackoffCache ) {
2015-12-30 13:43:10 -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 14:43:45 -08:00
}
2013-05-26 13:44:47 -07:00
}
}
2013-05-04 20:33:31 -07:00
2013-05-26 13:44:47 -07:00
if ( hashFail ) {
match = false ;
2015-03-14 17:05:44 -07:00
reason = " hash fail " ;
2013-05-26 13:44:47 -07:00
entry - > status | = TexCacheEntry : : STATUS_UNRELIABLE ;
2014-01-19 20:54:48 -08: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-01-19 20:54:48 -08:00
}
2013-05-26 13:44:47 -07: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 14:43:45 -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-05-26 13:44:47 -07:00
}
2013-12-29 14:43:45 -08:00
} else {
2014-06-14 00:18:37 +02:00
secondKey = entry - > fullhash | ( ( u64 ) entry - > cluthash < < 32 ) ;
2014-12-21 16:54:26 -08:00
secondCacheSizeEstimate_ + = EstimateTexMemoryUsage ( entry ) ;
2013-12-29 14:43:45 -08:00
secondCache [ secondKey ] = * entry ;
doDelete = false ;
2013-05-04 20:33:31 -07:00
}
}
2013-04-28 11:20:56 -07:00
}
2013-01-02 23:21:02 -08:00
}
}
2016-01-03 23:06:15 -08:00
if ( match & & ( entry - > status & TexCacheEntry : : STATUS_TO_SCALE ) & & standardScaleFactor_ ! = 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-06-13 22:07:26 +02:00
}
2012-11-18 23:35:02 +01:00
if ( match ) {
2013-02-10 14:11:53 -08:00
// TODO: Mark the entry reliable if it's been safe for long enough?
2012-11-01 16:19:01 +01:00
//got one!
2013-08-07 22:32:04 +02:00
entry - > lastFrame = gpuStats . numFlips ;
2015-03-15 19:46:36 -07:00
if ( entry - > textureName ! = lastBoundTexture ) {
2014-04-13 12:13:02 -07:00
gstate_c . textureFullAlpha = entry - > GetAlphaStatus ( ) = = TexCacheEntry : : STATUS_ALPHA_FULL ;
2014-05-11 14:11:36 -07:00
gstate_c . textureSimpleAlpha = entry - > GetAlphaStatus ( ) ! = TexCacheEntry : : STATUS_ALPHA_UNKNOWN ;
2013-01-10 23:49:33 +01:00
}
2015-10-01 22:56:31 -07:00
nextTexture_ = entry ;
2013-09-07 13:43:07 -07:00
VERBOSE_LOG ( G3D , " Texture at %08x Found in Cache, applying " , texaddr ) ;
2012-11-01 16:19:01 +01:00
return ; //Done!
2012-11-18 23:35:02 +01:00
} else {
2014-12-21 16:54:26 -08:00
cacheSizeEstimate_ - = EstimateTexMemoryUsage ( entry ) ;
2013-05-04 20:33:31 -07: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-05-04 20:33:31 -07:00
if ( doDelete ) {
2016-01-03 23:06:15 -08:00
if ( entry - > maxLevel = = maxLevel & & entry - > dim = = gstate . getTextureDimension ( 0 ) & & entry - > format = = format & & standardScaleFactor_ = = 1 ) {
2013-06-11 20:21:19 +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.
2013-06-14 00:46:49 -07:00
replaceImages = true ;
2013-06-11 20:21:19 +02:00
} else {
2015-03-15 19:46:36 -07:00
if ( entry - > textureName = = lastBoundTexture ) {
2015-11-28 15:41:15 -08:00
lastBoundTexture = INVALID_TEX ;
2013-06-13 23:04:11 -07:00
}
2015-03-15 19:46:36 -07:00
glDeleteTextures ( 1 , & entry - > textureName ) ;
2013-06-13 23:04:11 -07:00
}
2013-05-04 20:33:31 -07:00
}
2014-01-19 20:54:48 -08:00
// Clear the reliable bit if set.
2014-04-13 12:13:02 -07:00
if ( entry - > GetHashStatus ( ) = = TexCacheEntry : : STATUS_RELIABLE ) {
entry - > SetHashStatus ( TexCacheEntry : : STATUS_HASHING ) ;
2013-02-10 14:25:44 -08:00
}
2014-04-13 16:19:18 -07:00
// Also, mark any textures with the same address but different clut. They need rechecking.
if ( cluthash ! = 0 ) {
2014-06-14 09:50:54 -07:00
const u64 cachekeyMin = ( u64 ) ( texaddr & 0x3FFFFFFF ) < < 32 ;
2014-04-14 07:57:28 -07:00
const u64 cachekeyMax = cachekeyMin + ( 1ULL < < 32 ) ;
2014-04-13 16:19:18 -07:00
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 ;
}
}
}
2012-11-01 16:19:01 +01:00
}
2013-01-06 12:11:47 +01:00
} else {
2013-09-07 13:43:07 -07:00
VERBOSE_LOG ( G3D , " No texture in cache, decoding... " ) ;
2013-02-10 14:11:53 -08:00
TexCacheEntry entryNew = { 0 } ;
cache [ cachekey ] = entryNew ;
2013-02-10 14:25:44 -08:00
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-02-10 14:11:53 -08:00
entry = & cache [ cachekey ] ;
2013-12-29 14:43:45 -08:00
if ( g_Config . bTextureBackoffCache ) {
entry - > status = TexCacheEntry : : STATUS_HASHING ;
} else {
entry - > status = TexCacheEntry : : STATUS_UNRELIABLE ;
}
2012-11-01 16:19:01 +01:00
}
2014-03-01 16:11:56 -08:00
if ( ( bufw = = 0 | | ( gstate . texbufwidth [ 0 ] & 0xf800 ) ! = 0 ) & & texaddr > = PSP_GetKernelMemoryEnd ( ) ) {
2013-09-07 13:31:14 -07:00
ERROR_LOG_REPORT ( G3D , " Texture with unexpected bufw (full=%d) " , gstate . texbufwidth [ 0 ] & 0xffff ) ;
2015-10-14 22:13:50 +02:00
// Proceeding here can cause a crash.
return ;
2013-05-06 23:49:30 -07:00
}
2013-02-12 21:00:51 +01:00
2013-04-28 11:20:56 -07:00
// We have to decode it, let's setup the cache entry first.
2013-02-10 14:11:53 -08:00
entry - > addr = texaddr ;
entry - > hash = texhash ;
entry - > format = format ;
2013-08-07 22:32:04 +02:00
entry - > lastFrame = gpuStats . numFlips ;
2013-02-21 21:37:19 +01:00
entry - > framebuffer = 0 ;
2013-02-10 14:11:53 -08:00
entry - > maxLevel = maxLevel ;
entry - > lodBias = 0.0f ;
2013-10-08 15:00:48 +02:00
2013-09-08 16:08:31 +08:00
entry - > dim = gstate . getTextureDimension ( 0 ) ;
2013-07-27 20:20:39 -07:00
entry - > bufw = bufw ;
2012-11-01 16:19:01 +01:00
2013-01-22 19:18:48 +01:00
// 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-01-22 19:18:48 +01:00
2015-03-15 21:38:01 -07:00
entry - > fullhash = fullhash = = 0 ? QuickTexHash ( texaddr , bufw , w , h , format , entry ) : fullhash ;
2013-05-20 00:55:53 -07:00
entry - > cluthash = cluthash ;
2013-01-02 23:21:02 -08:00
2013-05-08 07:52:54 -07:00
entry - > status & = ~ TexCacheEntry : : STATUS_ALPHA_MASK ;
2013-02-08 00:04:34 +01:00
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-10-26 08:37:52 -07:00
// Always generate a texture name, we might need it if the texture is replaced later.
if ( ! replaceImages ) {
2015-03-15 19:46:36 -07:00
entry - > textureName = AllocTextureName ( ) ;
2013-10-26 08:37:52 -07:00
}
2013-09-01 11:44:49 -07: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-06-14 09:50:54 -07:00
AttachFramebuffer ( entry , framebuffer - > fb_address , framebuffer ) ;
2013-09-01 14:44:37 -07:00
}
2013-09-01 11:44:49 -07:00
2013-09-01 14:44:37 -07:00
// If we ended up with a framebuffer, attach it - no texture decoding needed.
if ( entry - > framebuffer ) {
2014-06-04 23:50:02 -07:00
SetTextureFramebuffer ( entry , entry - > framebuffer ) ;
2013-09-01 14:44:37 -07:00
entry - > lastFrame = gpuStats . numFlips ;
return ;
2013-09-01 11:44:49 -07:00
}
2015-03-15 19:46:36 -07:00
glBindTexture ( GL_TEXTURE_2D , entry - > textureName ) ;
lastBoundTexture = entry - > textureName ;
2013-10-08 15:00:48 +02:00
2013-02-12 21:00:51 +01:00
// Adjust maxLevel to actually present levels..
2014-02-17 11:42:03 +01:00
bool badMipSizes = false ;
2015-03-15 19:51:53 -07:00
for ( u32 i = 0 ; i < = maxLevel ; i + + ) {
2013-02-12 21:00:51 +01:00
// If encountering levels pointing to nothing, adjust max level.
2013-09-15 21:39:28 -07:00
u32 levelTexaddr = gstate . getTextureAddress ( i ) ;
2013-02-12 21:00:51 +01:00
if ( ! Memory : : IsValidAddress ( levelTexaddr ) ) {
maxLevel = i - 1 ;
break ;
}
2014-02-17 11:42:03 +01:00
2015-09-05 23:09:06 +02:00
if ( i > 0 & & gstate_c . Supports ( GPU_SUPPORTS_TEXTURE_LOD_CONTROL ) ) {
2014-02-17 11:42:03 +01:00
int tw = gstate . getTextureWidth ( i ) ;
2014-03-03 00:21:04 -08:00
int th = gstate . getTextureHeight ( i ) ;
2014-02-17 11:42:03 +01:00
if ( tw ! = 1 & & tw ! = ( gstate . getTextureWidth ( i - 1 ) > > 1 ) )
badMipSizes = true ;
else if ( th ! = 1 & & th ! = ( gstate . getTextureHeight ( i - 1 ) > > 1 ) )
badMipSizes = true ;
}
2013-02-12 21:00:51 +01:00
}
2013-09-01 08:28:09 +08:00
2013-10-08 15:00:48 +02:00
// 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.
GLenum dstFmt = GetDestFormat ( format , gstate . getClutPaletteFormat ( ) ) ;
2013-09-01 08:28:09 +08:00
2016-01-03 23:06:15 -08:00
int scaleFactor = standardScaleFactor_ ;
2014-04-02 00:13:50 +02:00
2015-12-30 13:23:05 -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-04-02 00:13:50 +02:00
// Don't scale the PPGe texture.
if ( entry - > addr > 0x05000000 & & entry - > addr < 0x08800000 )
scaleFactor = 1 ;
2015-12-30 16:38:07 -08:00
if ( ( entry - > status & TexCacheEntry : : STATUS_CHANGE_FREQUENT ) ! = 0 ) {
2015-12-30 13:43:10 -08:00
// Remember for later that we /wanted/ to scale this texture.
entry - > status | = TexCacheEntry : : STATUS_TO_SCALE ;
2015-12-30 13:23:05 -08:00
scaleFactor = 1 ;
2015-12-30 13:43:10 -08:00
}
2014-04-02 00:13:50 +02:00
2015-12-30 13:23:05 -08:00
if ( scaleFactor ! = 1 ) {
2014-06-13 22:07:26 +02:00
if ( texelsScaledThisFrame_ > = TEXCACHE_MAX_TEXELS_SCALED ) {
entry - > status | = TexCacheEntry : : STATUS_TO_SCALE ;
scaleFactor = 1 ;
} else {
2014-06-13 23:01:40 +02:00
entry - > status & = ~ TexCacheEntry : : STATUS_TO_SCALE ;
2014-06-13 22:07:26 +02:00
texelsScaledThisFrame_ + = w * h ;
}
}
2014-06-08 18:36:18 +02:00
// Disabled this due to issue #6075: https://github.com/hrydgard/ppsspp/issues/6075
// This breaks Dangan Ronpa 2 with mipmapping enabled. Why? No idea, it shouldn't.
// glTexStorage2D probably has few benefits for us anyway.
if ( false & & gl_extensions . GLES3 & & maxLevel > 0 ) {
2013-10-08 15:00:48 +02:00
// glTexStorage2D requires the use of sized formats.
GLenum storageFmt = GL_RGBA8 ;
switch ( dstFmt ) {
case GL_UNSIGNED_BYTE : storageFmt = GL_RGBA8 ; break ;
case GL_UNSIGNED_SHORT_5_6_5 : storageFmt = GL_RGB565 ; break ;
case GL_UNSIGNED_SHORT_4_4_4_4 : storageFmt = GL_RGBA4 ; break ;
case GL_UNSIGNED_SHORT_5_5_5_1 : storageFmt = GL_RGB5_A1 ; break ;
default :
ERROR_LOG ( G3D , " Unknown dstfmt %i " , ( int ) dstFmt ) ;
break ;
}
2013-12-29 16:13:26 -08:00
// TODO: This may cause bugs, since it hard-sets the texture w/h, and we might try to reuse it later with a different size.
2014-04-02 00:13:50 +02:00
glTexStorage2D ( GL_TEXTURE_2D , maxLevel + 1 , storageFmt , w * scaleFactor , h * scaleFactor ) ;
2013-10-08 15:00:48 +02:00
// Make sure we don't use glTexImage2D after glTexStorage2D.
replaceImages = true ;
}
2013-04-03 07:33:14 +08:00
2013-10-08 15:00:48 +02:00
// GLES2 doesn't have support for a "Max lod" which is critical as PSP games often
// don't specify mips all the way down. As a result, we either need to manually generate
// the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not
// be as good quality as the game's own (might even be better in some cases though).
2013-12-21 21:27:22 +08:00
// Always load base level texture here
2014-04-02 00:13:50 +02:00
LoadTextureLevel ( * entry , 0 , replaceImages , scaleFactor , dstFmt ) ;
2013-12-30 14:28:28 +08:00
// Mipmapping only enable when texture scaling disable
2015-12-30 16:54:25 -08:00
if ( maxLevel > 0 & & scaleFactor = = 1 ) {
2015-09-05 23:09:06 +02:00
if ( gstate_c . Supports ( GPU_SUPPORTS_TEXTURE_LOD_CONTROL ) ) {
if ( badMipSizes ) {
// WARN_LOG(G3D, "Bad mipmap for texture sized %dx%dx%d - autogenerating", w, h, (int)format);
glGenerateMipmap ( GL_TEXTURE_2D ) ;
} else {
for ( int i = 1 ; i < = maxLevel ; i + + ) {
LoadTextureLevel ( * entry , i , replaceImages , scaleFactor , dstFmt ) ;
}
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , maxLevel ) ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LOD , ( float ) maxLevel ) ;
2013-12-21 21:27:22 +08:00
}
2014-12-14 01:24:01 +01:00
} else {
2015-09-05 23:09:06 +02:00
// Avoid PowerVR driver bug
if ( w > 1 & & h > 1 & & ! ( h > w & & ( gl_extensions . bugs & BUG_PVR_GENMIPMAP_HEIGHT_GREATER ) ) ) { // Really! only seems to fail if height > width
// NOTICE_LOG(G3D, "Generating mipmap for texture sized %dx%d%d", w, h, (int)format);
glGenerateMipmap ( GL_TEXTURE_2D ) ;
} else {
entry - > maxLevel = 0 ;
}
2014-12-14 01:24:01 +01:00
}
2015-09-05 23:09:06 +02:00
} else if ( gstate_c . Supports ( GPU_SUPPORTS_TEXTURE_LOD_CONTROL ) ) {
2014-04-02 10:28:03 +02:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 0 ) ;
2013-04-03 07:33:14 +08:00
}
2016-03-17 21:56:04 -07:00
if ( gstate_c . Supports ( GPU_SUPPORTS_ANISOTROPY ) ) {
int aniso = 1 < < g_Config . iAnisotropyLevel ;
float anisotropyLevel = ( float ) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : ( float ) aniso ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_ANISOTROPY_EXT , anisotropyLevel ) ;
}
2012-11-09 00:51:04 +01:00
2014-05-15 22:38:44 -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
// This will rebind it, but that's okay.
nextTexture_ = entry ;
2013-02-10 14:11:53 -08:00
UpdateSamplingParams ( * entry , true ) ;
2013-02-08 00:04:34 +01:00
//glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
//glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei ( GL_PACK_ALIGNMENT , 1 ) ;
}
2014-06-07 15:25:35 -07:00
u32 TextureCache : : AllocTextureName ( ) {
if ( nameCache_ . empty ( ) ) {
nameCache_ . resize ( TEXCACHE_NAME_CACHE_SIZE ) ;
glGenTextures ( TEXCACHE_NAME_CACHE_SIZE , & nameCache_ [ 0 ] ) ;
}
u32 name = nameCache_ . back ( ) ;
nameCache_ . pop_back ( ) ;
return name ;
}
2013-10-08 15:00:48 +02:00
GLenum TextureCache : : 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 GL_UNSIGNED_SHORT_4_4_4_4 ;
case GE_TFMT_5551 :
return GL_UNSIGNED_SHORT_5_5_5_1 ;
case GE_TFMT_5650 :
return GL_UNSIGNED_SHORT_5_6_5 ;
case GE_TFMT_8888 :
case GE_TFMT_DXT1 :
case GE_TFMT_DXT3 :
case GE_TFMT_DXT5 :
default :
return GL_UNSIGNED_BYTE ;
}
}
2015-12-30 16:26:39 -08:00
void * TextureCache : : DecodeTextureLevel ( GETextureFormat format , GEPaletteFormat clutformat , int level , u32 & texByteAlign , GLenum dstFmt , int scaleFactor , int * bufwout ) {
2012-11-08 23:26:30 +01:00
void * finalBuf = NULL ;
2012-11-01 16:19:01 +01:00
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-06-14 09:53:50 -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-06-14 09:53:50 -07:00
}
2013-02-08 00:04:34 +01:00
2013-09-15 21:27:13 -07:00
int bufw = GetTextureBufw ( level , texaddr , format ) ;
2013-10-31 02:39:46 +01:00
if ( bufwout )
* bufwout = bufw ;
2013-07-31 01:52:29 +08:00
int w = gstate . getTextureWidth ( level ) ;
int h = gstate . getTextureHeight ( level ) ;
2013-02-08 00:04:34 +01:00
const u8 * texptr = Memory : : GetPointer ( texaddr ) ;
2013-08-18 22:43:26 -07:00
switch ( format ) {
2012-11-08 23:26:30 +01:00
case GE_TFMT_CLUT4 :
2013-05-12 09:04:50 -07:00
{
2013-12-08 23:11:56 -08:00
const bool mipmapShareClut = gstate . isClutSharedForMipmaps ( ) ;
2013-05-12 09:04:50 -07:00
const int clutSharingOffset = mipmapShareClut ? 0 : level * 16 ;
2013-04-28 11:20:56 -07:00
switch ( clutformat ) {
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_BGR5650 :
case GE_CMODE_16BIT_ABGR5551 :
case GE_CMODE_16BIT_ABGR4444 :
2012-11-01 16:19:01 +01:00
{
2013-04-28 13:14:25 -07:00
tmpTexBuf16 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
2013-05-12 09:04:50 -07:00
const u16 * clut = GetCurrentClut < u16 > ( ) + clutSharingOffset ;
2012-11-08 23:26:30 +01:00
texByteAlign = 2 ;
2015-04-18 12:39:04 -07:00
if ( ! swizzled ) {
2013-05-12 09:15:31 -07:00
if ( clutAlphaLinear_ & & mipmapShareClut ) {
2014-01-19 16:13:55 -08:00
DeIndexTexture4Optimal ( tmpTexBuf16 . data ( ) , texptr , bufw * h , clutAlphaLinearColor_ ) ;
2013-05-05 22:34:35 -07:00
} else {
2014-01-19 16:13:55 -08:00
DeIndexTexture4 ( tmpTexBuf16 . data ( ) , texptr , bufw * h , clut ) ;
2013-05-05 22:34:35 -07:00
}
2013-01-06 12:11:47 +01:00
} else {
2013-04-28 13:14:25 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2016-03-26 21:50:49 -07:00
UnswizzleFromMem ( tmpTexBuf32 . data ( ) , bufw / 2 , texptr , bufw , h , 0 ) ;
2013-05-12 09:15:31 -07:00
if ( clutAlphaLinear_ & & mipmapShareClut ) {
2014-01-19 16:13:55 -08:00
DeIndexTexture4Optimal ( tmpTexBuf16 . data ( ) , ( const u8 * ) tmpTexBuf32 . data ( ) , bufw * h , clutAlphaLinearColor_ ) ;
2013-05-05 22:34:35 -07:00
} else {
2014-01-19 16:13:55 -08:00
DeIndexTexture4 ( tmpTexBuf16 . data ( ) , ( const u8 * ) tmpTexBuf32 . data ( ) , bufw * h , clut ) ;
2013-05-05 22:34:35 -07:00
}
2012-11-01 16:19:01 +01:00
}
2013-04-09 01:52:19 -07:00
finalBuf = tmpTexBuf16 . data ( ) ;
2012-11-08 23:26:30 +01:00
}
2012-11-01 16:19:01 +01:00
break ;
2012-11-08 23:26:30 +01:00
case GE_CMODE_32BIT_ABGR8888 :
{
2013-04-28 13:14:25 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
2013-05-12 09:04:50 -07:00
const u32 * clut = GetCurrentClut < u32 > ( ) + clutSharingOffset ;
2015-04-18 12:39:04 -07:00
if ( ! swizzled ) {
2014-01-19 16:13:55 -08:00
DeIndexTexture4 ( tmpTexBuf32 . data ( ) , texptr , bufw * h , clut ) ;
2013-04-28 01:53:24 -07:00
finalBuf = tmpTexBuf32 . data ( ) ;
2013-01-06 12:11:47 +01:00
} else {
2016-03-26 21:50:49 -07:00
UnswizzleFromMem ( tmpTexBuf32 . data ( ) , bufw / 2 , texptr , bufw , h , 0 ) ;
2013-04-28 01:53:24 -07:00
// Let's reuse tmpTexBuf16, just need double the space.
2013-04-28 13:14:25 -07:00
tmpTexBuf16 . resize ( std : : max ( bufw , w ) * h * 2 ) ;
2013-05-12 09:04:50 -07:00
DeIndexTexture4 ( ( u32 * ) tmpTexBuf16 . data ( ) , ( u8 * ) tmpTexBuf32 . data ( ) , bufw * h , clut ) ;
2013-04-28 01:53:24 -07:00
finalBuf = tmpTexBuf16 . data ( ) ;
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
}
2012-11-01 16:19:01 +01:00
break ;
2012-11-08 23:26:30 +01:00
default :
2013-09-07 13:43:07 -07:00
ERROR_LOG_REPORT ( G3D , " Unknown CLUT4 texture mode %d " , gstate . getClutPaletteFormat ( ) ) ;
2013-04-28 11:20:56 -07:00
return NULL ;
2012-11-01 16:19:01 +01:00
}
2013-05-12 09:04:50 -07:00
}
2012-11-08 23:26:30 +01:00
break ;
2012-11-01 16:19:01 +01:00
case GE_TFMT_CLUT8 :
2013-07-21 19:34:55 -07:00
texByteAlign = texByteAlignMap [ gstate . getClutPaletteFormat ( ) ] ;
2014-01-19 16:13:55 -08:00
finalBuf = ReadIndexedTex ( level , texptr , 1 , dstFmt , bufw ) ;
2012-11-08 23:26:30 +01:00
break ;
case GE_TFMT_CLUT16 :
2013-07-21 19:34:55 -07:00
texByteAlign = texByteAlignMap [ gstate . getClutPaletteFormat ( ) ] ;
2014-01-19 16:13:55 -08:00
finalBuf = ReadIndexedTex ( level , texptr , 2 , dstFmt , bufw ) ;
2012-11-08 23:26:30 +01:00
break ;
case GE_TFMT_CLUT32 :
2013-07-21 19:34:55 -07:00
texByteAlign = texByteAlignMap [ gstate . getClutPaletteFormat ( ) ] ;
2014-01-19 16:13:55 -08:00
finalBuf = ReadIndexedTex ( level , texptr , 4 , dstFmt , bufw ) ;
2012-11-08 23:26:30 +01: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 ) {
2012-11-28 16:20:38 +01:00
int len = std : : max ( bufw , w ) * h ;
2013-04-09 01:52:19 -07:00
tmpTexBuf16 . resize ( len ) ;
tmpTexBufRearrange . resize ( len ) ;
finalBuf = tmpTexBuf16 . data ( ) ;
2014-01-19 16:13:55 -08:00
ConvertColors ( finalBuf , texptr , dstFmt , bufw * h ) ;
2013-06-30 20:36:27 -07:00
} else {
2013-04-28 10:49:06 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2016-03-26 21:50:49 -07:00
UnswizzleFromMem ( tmpTexBuf32 . data ( ) , bufw * 2 , texptr , bufw , h , 2 ) ;
2016-03-22 19:00:57 +01:00
finalBuf = tmpTexBuf32 . data ( ) ;
2013-06-30 20:36:27 -07:00
ConvertColors ( finalBuf , finalBuf , dstFmt , bufw * h ) ;
2013-04-28 10:49:06 -07:00
}
2012-11-08 23:26:30 +01:00
break ;
2012-11-01 16:19:01 +01:00
case GE_TFMT_8888 :
2015-04-18 12:39:04 -07:00
if ( ! swizzled ) {
2013-05-03 01:02:00 -07:00
// Special case: if we don't need to deal with packing, we don't need to copy.
2015-12-30 16:26:39 -08:00
if ( ( scaleFactor = = 1 & & gstate_c . Supports ( GPU_SUPPORTS_UNPACK_SUBIMAGE ) ) | | w = = bufw ) {
2014-04-13 18:34:40 -07:00
if ( UseBGRA8888 ( ) ) {
2015-05-24 11:05:44 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2014-04-13 18:34:40 -07:00
finalBuf = tmpTexBuf32 . data ( ) ;
ConvertColors ( finalBuf , texptr , dstFmt , bufw * h ) ;
} else {
finalBuf = ( void * ) texptr ;
}
2013-05-03 01:02:00 -07:00
} else {
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
finalBuf = tmpTexBuf32 . data ( ) ;
2014-04-13 18:34:40 -07:00
ConvertColors ( finalBuf , texptr , dstFmt , bufw * h ) ;
2013-05-03 01:02:00 -07:00
}
2014-09-09 00:53:01 -07:00
} else {
2013-04-28 13:14:25 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
2016-03-26 21:50:49 -07:00
UnswizzleFromMem ( tmpTexBuf32 . data ( ) , bufw * 4 , texptr , bufw , h , 4 ) ;
2016-03-22 19:00:57 +01:00
finalBuf = tmpTexBuf32 . data ( ) ;
2014-04-13 18:34:40 -07:00
ConvertColors ( finalBuf , finalBuf , dstFmt , bufw * h ) ;
2013-04-28 10:49:06 -07:00
}
2012-11-08 23:26:30 +01:00
break ;
2012-11-01 16:19:01 +01:00
case GE_TFMT_DXT1 :
{
2013-04-28 10:49:06 -07:00
int minw = std : : min ( bufw , w ) ;
2013-04-28 13:14:25 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
2013-04-09 01:52:19 -07:00
u32 * dst = tmpTexBuf32 . data ( ) ;
2012-11-01 16:19:01 +01:00
DXT1Block * src = ( DXT1Block * ) texptr ;
2013-01-06 12:11:47 +01:00
for ( int y = 0 ; y < h ; y + = 4 ) {
2012-11-28 16:12:29 +01:00
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
2013-04-28 10:49:06 -07:00
for ( int x = 0 ; x < minw ; x + = 4 ) {
2013-09-25 23:07:09 -07:00
DecodeDXT1Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
2012-11-28 16:12:29 +01:00
blockIndex + + ;
2012-11-01 16:19:01 +01:00
}
}
2013-04-09 01:52:19 -07:00
finalBuf = tmpTexBuf32 . data ( ) ;
2014-04-14 01:27:47 -07:00
ConvertColors ( finalBuf , finalBuf , dstFmt , bufw * h ) ;
2012-11-28 16:12:29 +01:00
w = ( w + 3 ) & ~ 3 ;
2012-11-01 16:19:01 +01:00
}
break ;
case GE_TFMT_DXT3 :
2012-11-28 16:12:29 +01:00
{
2013-04-28 10:49:06 -07:00
int minw = std : : min ( bufw , w ) ;
2013-04-28 13:14:25 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
2013-04-09 01:52:19 -07:00
u32 * dst = tmpTexBuf32 . data ( ) ;
2012-11-28 16:12:29 +01:00
DXT3Block * src = ( DXT3Block * ) texptr ;
2013-01-06 12:11:47 +01:00
for ( int y = 0 ; y < h ; y + = 4 ) {
2012-11-28 16:20:38 +01:00
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
2013-04-28 10:49:06 -07:00
for ( int x = 0 ; x < minw ; x + = 4 ) {
2013-09-25 23:07:09 -07:00
DecodeDXT3Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
2012-11-28 16:12:29 +01:00
blockIndex + + ;
}
}
w = ( w + 3 ) & ~ 3 ;
2013-04-09 01:52:19 -07:00
finalBuf = tmpTexBuf32 . data ( ) ;
2014-04-14 01:27:47 -07:00
ConvertColors ( finalBuf , finalBuf , dstFmt , bufw * h ) ;
2012-11-28 16:12:29 +01:00
}
break ;
2013-07-22 19:22:21 +02:00
case GE_TFMT_DXT5 :
2012-11-28 16:12:29 +01:00
{
2013-04-28 10:49:06 -07:00
int minw = std : : min ( bufw , w ) ;
2013-04-28 13:14:25 -07:00
tmpTexBuf32 . resize ( std : : max ( bufw , w ) * h ) ;
tmpTexBufRearrange . resize ( std : : max ( bufw , w ) * h ) ;
2013-04-09 01:52:19 -07:00
u32 * dst = tmpTexBuf32 . data ( ) ;
2012-11-28 16:12:29 +01:00
DXT5Block * src = ( DXT5Block * ) texptr ;
2013-04-28 10:49:06 -07:00
2013-01-06 12:11:47 +01:00
for ( int y = 0 ; y < h ; y + = 4 ) {
2012-11-28 16:12:29 +01:00
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
2013-04-28 10:49:06 -07:00
for ( int x = 0 ; x < minw ; x + = 4 ) {
2013-09-25 23:07:09 -07:00
DecodeDXT5Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
2012-11-28 16:12:29 +01:00
blockIndex + + ;
}
}
w = ( w + 3 ) & ~ 3 ;
2013-04-09 01:52:19 -07:00
finalBuf = tmpTexBuf32 . data ( ) ;
2014-04-14 01:27:47 -07:00
ConvertColors ( finalBuf , finalBuf , dstFmt , bufw * h ) ;
2012-11-28 16:12:29 +01:00
}
2012-11-08 23:26:30 +01:00
break ;
2012-11-01 16:19:01 +01:00
default :
2013-04-28 11:20:56 -07:00
ERROR_LOG_REPORT ( G3D , " Unknown Texture Format %d!!! " , format ) ;
return NULL ;
2012-11-01 16:19:01 +01:00
}
2012-11-28 16:12:29 +01:00
if ( ! finalBuf ) {
2013-04-28 10:49:06 -07:00
ERROR_LOG_REPORT ( G3D , " NO finalbuf! Will crash! " ) ;
2012-11-28 16:12:29 +01:00
}
2015-12-30 16:26:39 -08:00
if ( ! ( scaleFactor = = 1 & & gstate_c . Supports ( GPU_SUPPORTS_UNPACK_SUBIMAGE ) ) & & w ! = bufw ) {
2012-11-09 00:51:04 +01:00
int pixelSize ;
switch ( dstFmt ) {
2012-11-09 01:24:19 +01:00
case GL_UNSIGNED_SHORT_4_4_4_4 :
case GL_UNSIGNED_SHORT_5_5_5_1 :
case GL_UNSIGNED_SHORT_5_6_5 :
2012-11-09 00:51:04 +01:00
pixelSize = 2 ;
2012-11-09 01:24:19 +01:00
break ;
2012-11-09 00:51:04 +01:00
default :
pixelSize = 4 ;
2012-11-09 01:24:19 +01:00
break ;
2012-11-09 00:51:04 +01:00
}
2015-09-05 21:23:58 +02:00
2012-11-09 00:51:04 +01:00
// 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 ) ;
2012-11-09 00:51:04 +01:00
}
2013-04-28 11:20:56 -07:00
return finalBuf ;
}
2014-09-08 20:55:56 -07:00
TextureCache : : TexCacheEntry : : Status TextureCache : : CheckAlpha ( const u32 * pixelData , GLenum dstFmt , int stride , int w , int h ) {
2015-05-24 22:55:43 -07:00
CheckAlphaResult res ;
2013-05-08 07:52:54 -07:00
switch ( dstFmt ) {
case GL_UNSIGNED_SHORT_4_4_4_4 :
2015-05-24 22:55:43 -07:00
res = CheckAlphaABGR4444Basic ( pixelData , stride , w , h ) ;
2013-05-08 07:52:54 -07:00
break ;
case GL_UNSIGNED_SHORT_5_5_5_1 :
2015-05-24 22:55:43 -07:00
res = CheckAlphaABGR1555Basic ( pixelData , stride , w , h ) ;
2013-05-08 07:52:54 -07:00
break ;
case GL_UNSIGNED_SHORT_5_6_5 :
2015-05-24 22:55:43 -07:00
// Never has any alpha.
res = CHECKALPHA_FULL ;
2013-05-08 07:52:54 -07:00
break ;
default :
2015-05-24 22:55:43 -07:00
res = CheckAlphaRGBA8888Basic ( pixelData , stride , w , h ) ;
2013-05-08 07:52:54 -07:00
break ;
}
2015-05-24 22:55:43 -07:00
return ( TexCacheEntry : : Status ) res ;
2013-05-08 07:52:54 -07:00
}
2014-04-02 00:13:50 +02:00
void TextureCache : : LoadTextureLevel ( TexCacheEntry & entry , int level , bool replaceImages , int scaleFactor , GLenum dstFmt ) {
2015-05-15 17:14:54 +02:00
int w = gstate . getTextureWidth ( level ) ;
int h = gstate . getTextureHeight ( level ) ;
bool useUnpack = false ;
bool useBGRA ;
u32 * pixelData ;
{
PROFILE_THIS_SCOPE ( " decodetex " ) ;
2015-05-13 23:46:59 +02:00
2013-04-28 11:20:56 -07:00
// TODO: only do this once
u32 texByteAlign = 1 ;
2013-07-21 19:34:55 -07:00
GEPaletteFormat clutformat = gstate . getClutPaletteFormat ( ) ;
2013-10-31 02:39:46 +01:00
int bufw ;
2015-12-30 16:26:39 -08:00
void * finalBuf = DecodeTextureLevel ( GETextureFormat ( entry . format ) , clutformat , level , texByteAlign , dstFmt , scaleFactor , & bufw ) ;
2013-04-28 11:20:56 -07:00
if ( finalBuf = = NULL ) {
return ;
}
2012-12-21 21:49:09 +01:00
gpuStats . numTexturesDecoded + + ;
2013-07-22 19:22:21 +02:00
// Can restore these and remove the fixup at the end of DecodeTextureLevel on desktop GL and GLES 3.
2015-12-30 13:23:05 -08:00
if ( scaleFactor = = 1 & & gstate_c . Supports ( GPU_SUPPORTS_UNPACK_SUBIMAGE ) & & w ! = bufw ) {
2013-10-31 02:39:46 +01:00
glPixelStorei ( GL_UNPACK_ROW_LENGTH , bufw ) ;
useUnpack = true ;
}
2013-06-11 20:21:19 +02:00
2012-12-22 00:38:17 +01:00
glPixelStorei ( GL_UNPACK_ALIGNMENT , texByteAlign ) ;
2012-12-21 21:49:09 +01:00
2015-05-15 17:14:54 +02:00
useBGRA = UseBGRA8888 ( ) & & dstFmt = = GL_UNSIGNED_BYTE ;
2014-04-14 07:33:30 -07:00
2015-05-15 17:14:54 +02:00
pixelData = ( u32 * ) finalBuf ;
2015-12-30 13:23:05 -08:00
if ( scaleFactor > 1 )
2015-01-22 19:53:32 +01:00
scaler . Scale ( pixelData , dstFmt , w , h , scaleFactor ) ;
2014-01-19 20:54:48 -08:00
2014-06-03 00:59:34 -07:00
if ( ( entry . status & TexCacheEntry : : STATUS_CHANGE_FREQUENT ) = = 0 ) {
TexCacheEntry : : Status alphaStatus = CheckAlpha ( pixelData , dstFmt , useUnpack ? bufw : w , w , h ) ;
entry . SetAlphaStatus ( alphaStatus , level ) ;
} else {
2014-04-13 12:13:02 -07:00
entry . SetAlphaStatus ( TexCacheEntry : : STATUS_ALPHA_UNKNOWN ) ;
2014-06-03 00:59:34 -07:00
}
2015-05-15 17:14:54 +02:00
}
2013-04-30 03:49:12 +02:00
2012-11-26 01:21:14 +01:00
GLuint components = dstFmt = = GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA ;
2013-05-05 00:00:22 -07:00
2014-04-13 18:34:40 -07:00
GLuint components2 = components ;
2014-04-14 07:33:30 -07:00
if ( useBGRA ) {
2014-04-13 18:34:40 -07:00
components2 = GL_BGRA_EXT ;
}
2013-06-11 20:21:19 +02:00
if ( replaceImages ) {
2015-05-15 17:14:54 +02:00
PROFILE_THIS_SCOPE ( " repltex " ) ;
2014-04-13 18:34:40 -07:00
glTexSubImage2D ( GL_TEXTURE_2D , level , 0 , 0 , w , h , components2 , dstFmt , pixelData ) ;
2013-06-11 20:21:19 +02:00
} else {
2015-05-15 17:14:54 +02:00
PROFILE_THIS_SCOPE ( " loadtex " ) ;
2014-04-13 18:34:40 -07:00
glTexImage2D ( GL_TEXTURE_2D , level , components , w , h , 0 , components2 , dstFmt , pixelData ) ;
2015-03-08 18:36:05 -07:00
if ( ! lowMemoryMode_ ) {
GLenum err = glGetError ( ) ;
if ( err = = GL_OUT_OF_MEMORY ) {
WARN_LOG_REPORT ( G3D , " Texture cache ran out of GPU memory; switching to low memory mode " ) ;
lowMemoryMode_ = true ;
decimationCounter_ = 0 ;
Decimate ( ) ;
// Try again, now that we've cleared out textures in lowMemoryMode_.
glTexImage2D ( GL_TEXTURE_2D , level , components , w , h , 0 , components2 , dstFmt , pixelData ) ;
2015-12-30 13:29:29 -08:00
I18NCategory * err = GetI18NCategory ( " Error " ) ;
if ( scaleFactor > 1 ) {
osm . Show ( err - > T ( " Warning: Video memory FULL, reducing upscaling and switching to slow caching mode " ) , 2.0f ) ;
} else {
osm . Show ( err - > T ( " Warning: Video memory FULL, switching to slow caching mode " ) , 2.0f ) ;
}
} else if ( err ! = GL_NO_ERROR ) {
// We checked the err anyway, might as well log if there is one.
WARN_LOG ( G3D , " Got an error in texture upload: %08x " , err ) ;
2015-03-08 18:36:05 -07:00
}
2013-06-11 20:21:19 +02:00
}
2013-05-05 00:00:22 -07:00
}
2013-10-31 02:39:46 +01:00
if ( useUnpack ) {
glPixelStorei ( GL_UNPACK_ROW_LENGTH , 0 ) ;
}
2013-04-30 03:49:12 +02:00
}
2013-05-02 10:56:30 +02:00
// Only used by Qt UI?
2015-01-17 11:57:07 -08:00
bool TextureCache : : DecodeTexture ( u8 * output , const GPUgstate & state ) {
2013-02-17 01:06:06 +01:00
GPUgstate oldState = gstate ;
gstate = state ;
2013-09-15 21:39:28 -07:00
u32 texaddr = gstate . getTextureAddress ( 0 ) ;
2013-02-17 01:06:06 +01:00
if ( ! Memory : : IsValidAddress ( texaddr ) ) {
return false ;
}
2013-04-28 11:20:56 -07:00
u32 texByteAlign = 1 ;
GLenum dstFmt = 0 ;
2013-02-17 01:06:06 +01:00
2013-07-21 19:34:55 -07:00
GETextureFormat format = gstate . getTextureFormat ( ) ;
GEPaletteFormat clutformat = gstate . getClutPaletteFormat ( ) ;
2013-04-28 11:20:56 -07:00
u8 level = 0 ;
2013-02-17 01:06:06 +01:00
2013-09-15 21:27:13 -07:00
int bufw = GetTextureBufw ( level , texaddr , format ) ;
2013-07-31 01:52:29 +08:00
int w = gstate . getTextureWidth ( level ) ;
int h = gstate . getTextureHeight ( level ) ;
2013-02-17 01:06:06 +01:00
2015-12-30 16:26:39 -08:00
void * finalBuf = DecodeTextureLevel ( format , clutformat , level , texByteAlign , dstFmt , 1 ) ;
2013-04-28 11:20:56 -07:00
if ( finalBuf = = NULL ) {
2013-02-17 01:06:06 +01:00
return false ;
}
2013-10-08 15:00:48 +02:00
switch ( dstFmt ) {
2013-04-28 11:20:56 -07:00
case GL_UNSIGNED_SHORT_4_4_4_4 :
2015-01-22 19:53:32 +01:00
for ( int y = 0 ; y < h ; y + + )
for ( int x = 0 ; x < bufw ; x + + ) {
u32 val = ( ( u16 * ) finalBuf ) [ y * bufw + x ] ;
u32 r = ( ( val > > 12 ) & 0xF ) * 17 ;
u32 g = ( ( val > > 8 ) & 0xF ) * 17 ;
u32 b = ( ( val > > 4 ) & 0xF ) * 17 ;
u32 a = ( ( val > > 0 ) & 0xF ) * 17 ;
( ( u32 * ) output ) [ y * w + x ] = ( a < < 24 ) | ( r < < 16 ) | ( g < < 8 ) | b ;
}
2013-04-28 11:20:56 -07:00
break ;
case GL_UNSIGNED_SHORT_5_5_5_1 :
2015-01-22 19:53:32 +01:00
for ( int y = 0 ; y < h ; y + + )
for ( int x = 0 ; x < bufw ; x + + ) {
u32 val = ( ( u16 * ) finalBuf ) [ y * bufw + x ] ;
u32 r = Convert5To8 ( ( val > > 11 ) & 0x1F ) ;
u32 g = Convert5To8 ( ( val > > 6 ) & 0x1F ) ;
u32 b = Convert5To8 ( ( val > > 1 ) & 0x1F ) ;
u32 a = ( val & 0x1 ) * 255 ;
( ( u32 * ) output ) [ y * w + x ] = ( a < < 24 ) | ( r < < 16 ) | ( g < < 8 ) | b ;
}
2013-04-28 11:20:56 -07:00
break ;
case GL_UNSIGNED_SHORT_5_6_5 :
2015-01-22 19:53:32 +01:00
for ( int y = 0 ; y < h ; y + + )
for ( int x = 0 ; x < bufw ; x + + ) {
u32 val = ( ( u16 * ) finalBuf ) [ y * bufw + x ] ;
u32 a = 0xFF ;
u32 r = Convert5To8 ( ( val > > 11 ) & 0x1F ) ;
u32 g = Convert6To8 ( ( val > > 5 ) & 0x3F ) ;
u32 b = Convert5To8 ( ( val ) & 0x1F ) ;
( ( u32 * ) output ) [ y * w + x ] = ( a < < 24 ) | ( r < < 16 ) | ( g < < 8 ) | b ;
}
2013-04-28 11:20:56 -07:00
break ;
default :
2015-01-22 19:53:32 +01:00
for ( int y = 0 ; y < h ; y + + )
for ( int x = 0 ; x < bufw ; x + + ) {
u32 val = ( ( u32 * ) finalBuf ) [ y * bufw + x ] ;
( ( u32 * ) output ) [ y * w + x ] = ( ( val & 0xFF000000 ) ) | ( ( val & 0x00FF0000 ) > > 16 ) | ( ( val & 0x0000FF00 ) ) | ( ( val & 0x000000FF ) < < 16 ) ;
}
2013-04-28 11:20:56 -07:00
break ;
2013-02-17 01:06:06 +01:00
}
gstate = oldState ;
return true ;
}