2010-04-25 00:31:27 +00:00
/*
2009-02-09 21:15:56 +00:00
* Copyright ( C ) 2007 - 2009 Gabest
* http : //www.gabest.org
*
* 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 ; either version 2 , or ( at your option )
* any later version .
2010-04-25 00:31:27 +00:00
*
2009-02-09 21:15:56 +00: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 for more details .
2010-04-25 00:31:27 +00:00
*
2009-02-09 21:15:56 +00:00
* You should have received a copy of the GNU General Public License
* along with GNU Make ; see the file COPYING . If not , write to
2012-09-09 18:16:11 +00:00
* the Free Software Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA USA .
2009-02-09 21:15:56 +00:00
* http : //www.gnu.org/copyleft/gpl.html
*
*/
# include "stdafx.h"
2010-04-25 00:31:27 +00:00
# include "GSRendererHW.h"
2012-01-05 02:40:24 +00:00
2012-01-19 04:53:36 +00:00
GSRendererHW : : GSRendererHW ( GSTextureCache * tc )
2015-06-07 15:57:55 +00:00
: m_width ( 1280 )
, m_height ( 1024 )
2016-08-12 20:48:20 +00:00
, m_custom_width ( 1280 )
, m_custom_height ( 1024 )
2012-01-05 02:40:24 +00:00
, m_reset ( false )
, m_upscale_multiplier ( 1 )
2013-06-28 17:32:37 +00:00
, m_tc ( tc )
2016-04-28 17:39:57 +00:00
, m_channel_shuffle ( false )
2016-10-01 15:37:55 +00:00
, m_lod ( GSVector2i ( 0 , 0 ) )
2012-01-05 02:40:24 +00:00
{
2016-11-05 16:49:17 +00:00
m_mipmap = theApp . GetConfigI ( " mipmap_hw " ) ;
2016-05-24 19:52:06 +00:00
m_upscale_multiplier = theApp . GetConfigI ( " upscale_multiplier " ) ;
2016-06-17 13:25:09 +00:00
m_large_framebuffer = theApp . GetConfigB ( " large_framebuffer " ) ;
2016-05-24 19:52:06 +00:00
if ( theApp . GetConfigB ( " UserHacks " ) ) {
m_userhacks_align_sprite_X = theApp . GetConfigB ( " UserHacks_align_sprite_X " ) ;
m_userhacks_round_sprite_offset = theApp . GetConfigI ( " UserHacks_round_sprite_offset " ) ;
m_userhacks_disable_gs_mem_clear = theApp . GetConfigB ( " UserHacks_DisableGsMemClear " ) ;
} else {
m_userhacks_align_sprite_X = false ;
m_userhacks_round_sprite_offset = 0 ;
m_userhacks_disable_gs_mem_clear = false ;
}
2015-06-07 15:57:55 +00:00
2015-09-21 20:26:00 +00:00
if ( ! m_upscale_multiplier ) { //Custom Resolution
2016-06-26 06:03:16 +00:00
m_custom_width = m_width = theApp . GetConfigI ( " resx " ) ;
m_custom_height = m_height = theApp . GetConfigI ( " resy " ) ;
2015-06-07 15:57:55 +00:00
}
2015-09-08 12:55:01 +00:00
if ( m_upscale_multiplier = = 1 ) { // hacks are only needed for upscaling issues.
2015-06-07 15:57:55 +00:00
m_userhacks_round_sprite_offset = 0 ;
2016-05-24 19:52:06 +00:00
m_userhacks_align_sprite_X = 0 ;
2015-06-07 15:57:55 +00:00
}
2016-07-14 17:41:21 +00:00
m_dump_root = root_hw ;
2015-05-13 18:00:25 +00:00
}
2015-09-20 12:41:21 +00:00
void GSRendererHW : : SetScaling ( )
{
2016-03-09 20:30:38 +00:00
GSVector2i crtc_size ( GetDisplayRect ( ) . width ( ) , GetDisplayRect ( ) . height ( ) ) ;
2015-09-20 12:41:21 +00:00
2016-05-23 17:23:04 +00:00
// Details of (potential) perf impact of a big framebuffer
// 1/ extra memory
// 2/ texture cache framebuffer rescaling/copy
// 3/ upload of framebuffer (preload hack)
// 4/ framebuffer clear (color/depth/stencil)
// 5/ read back of the frambuffer
// 6/ MSAA
//
// With the solution
// 1/ Nothing to do.Except the texture cache bug (channel shuffle effect)
// most of the market is 1GB of VRAM (and soon 2GB)
// 2/ limit rescaling/copy to the valid data of the framebuffer
// 3/ ??? no solution so far
// 4a/ stencil can be limited to valid data.
// 4b/ is it useful to clear color? depth? (in any case, it ought to be few operation)
// 5/ limit the read to the valid data
// 6/ not support on openGL
2016-02-07 11:08:02 +00:00
// Framebuffer width is always a multiple of 64 so at certain cases it can't cover some weird width values.
// 480P , 576P use width as 720 which is not referencable by FBW * 64. so it produces 704 ( the closest value multiple by 64).
// In such cases, let's just use the CRTC width.
2016-03-09 20:30:38 +00:00
int fb_width = max ( { ( int ) m_context - > FRAME . FBW * 64 , crtc_size . x , 512 } ) ;
2016-02-07 11:08:02 +00:00
// GS doesn't have a specific register for the FrameBuffer height. so we get the height
// from physical units of the display rectangle in case the game uses a heigher value of height.
2016-03-28 10:09:59 +00:00
//
// Gregory: the framebuffer must have enough room to draw
// * at least 2 frames such as FMV (see OI_BlitFMV)
// * high resolution game such as snowblind engine game
//
// Autodetection isn't a good idea because it will create flickering
// If memory consumption is an issue, there are 2 possibilities
// * 1/ Avoid to create hundreds of RT
// * 2/ Use sparse texture (requires recent HW)
2016-05-17 18:01:00 +00:00
//
// Avoid to alternate between 640x1280 and 1280x1024 on snow blind engine game
// int fb_height = (fb_width < 1024) ? 1280 : 1024;
2016-05-23 17:27:13 +00:00
//
// Until performance issue is properly fixed, let's keep an option to reduce the framebuffer size.
int fb_height = m_large_framebuffer ? 1280 :
( fb_width < 1024 ) ? max ( 512 , crtc_size . y ) : 1024 ;
2016-03-09 20:30:38 +00:00
int upscaled_fb_w = fb_width * m_upscale_multiplier ;
int upscaled_fb_h = fb_height * m_upscale_multiplier ;
bool good_rt_size = m_width > = upscaled_fb_w & & m_height > = upscaled_fb_h ;
2016-06-26 06:03:16 +00:00
bool initialized_register_state = ( m_context - > FRAME . FBW > 1 ) & & ( crtc_size . y > 1 ) ;
if ( ! m_upscale_multiplier & & initialized_register_state )
{
if ( m_height = = m_custom_height )
{
float ratio = ceil ( static_cast < float > ( m_height ) / crtc_size . y ) ;
float buffer_scale_offset = ( m_large_framebuffer ) ? ratio : 0.5f ;
ratio = round ( ratio + buffer_scale_offset ) ;
m_tc - > RemovePartial ( ) ;
2016-07-01 12:21:07 +00:00
m_width = max ( m_width , 1280 ) ;
m_height = max ( static_cast < int > ( crtc_size . y * ratio ) , 1024 ) ;
2016-06-26 06:03:16 +00:00
}
}
2012-01-05 02:40:24 +00:00
2016-02-07 11:08:02 +00:00
// No need to resize for native/custom resolutions as default size will be enough for native and we manually get RT Buffer size for custom.
// don't resize until the display rectangle and register states are stabilized.
2016-03-09 20:30:38 +00:00
if ( m_upscale_multiplier < = 1 | | good_rt_size )
2016-02-07 11:08:02 +00:00
return ;
2015-04-13 16:31:48 +00:00
2016-02-07 11:08:02 +00:00
m_tc - > RemovePartial ( ) ;
2016-03-09 20:30:38 +00:00
m_width = upscaled_fb_w ;
m_height = upscaled_fb_h ;
printf ( " Frame buffer size set to %dx%d (%dx%d) \n " , fb_width , fb_height , m_width , m_height ) ;
2012-01-05 02:40:24 +00:00
}
GSRendererHW : : ~ GSRendererHW ( )
{
delete m_tc ;
}
void GSRendererHW : : SetGameCRC ( uint32 crc , int options )
{
GSRenderer : : SetGameCRC ( crc , options ) ;
m_hacks . SetGameCRC ( m_game ) ;
}
bool GSRendererHW : : CanUpscale ( )
{
2015-06-14 08:19:15 +00:00
if ( m_hacks . m_cu & & ! ( this - > * m_hacks . m_cu ) ( ) )
2012-01-05 02:40:24 +00:00
{
return false ;
}
2015-09-08 12:55:01 +00:00
return m_upscale_multiplier ! = 1 & & m_regs - > PMODE . EN ! = 0 ; // upscale ratio depends on the display size, with no output it may not be set correctly (ps2 logo to game transition)
2012-01-05 02:40:24 +00:00
}
int GSRendererHW : : GetUpscaleMultiplier ( )
{
2016-06-26 06:03:16 +00:00
return m_upscale_multiplier ;
}
GSVector2i GSRendererHW : : GetCustomResolution ( )
{
return GSVector2i ( m_custom_width , m_custom_height ) ;
2012-01-05 02:40:24 +00:00
}
void GSRendererHW : : Reset ( )
{
// TODO: GSreset can come from the main thread too => crash
// m_tc->RemoveAll();
m_reset = true ;
GSRenderer : : Reset ( ) ;
}
void GSRendererHW : : VSync ( int field )
{
2015-05-13 18:00:25 +00:00
//Check if the frame buffer width or display width has changed
SetScaling ( ) ;
2012-01-05 02:40:24 +00:00
if ( m_reset )
{
m_tc - > RemoveAll ( ) ;
m_reset = false ;
}
2012-01-18 11:47:31 +00:00
GSRenderer : : VSync ( field ) ;
m_tc - > IncAge ( ) ;
2015-07-10 19:11:14 +00:00
m_tc - > PrintMemoryUsage ( ) ;
m_dev - > PrintMemoryUsage ( ) ;
2012-01-18 11:47:31 +00:00
m_skip = 0 ;
2012-01-05 02:40:24 +00:00
}
void GSRendererHW : : ResetDevice ( )
{
m_tc - > RemoveAll ( ) ;
GSRenderer : : ResetDevice ( ) ;
}
2016-03-19 11:37:25 +00:00
GSTexture * GSRendererHW : : GetOutput ( int i , int & y_offset )
2012-01-05 02:40:24 +00:00
{
const GSRegDISPFB & DISPFB = m_regs - > DISP [ i ] . DISPFB ;
GIFRegTEX0 TEX0 ;
TEX0 . TBP0 = DISPFB . Block ( ) ;
TEX0 . TBW = DISPFB . FBW ;
TEX0 . PSM = DISPFB . PSM ;
// TRACE(_T("[%d] GetOutput %d %05x (%d)\n"), (int)m_perfmon.GetFrame(), i, (int)TEX0.TBP0, (int)TEX0.PSM);
GSTexture * t = NULL ;
2016-12-12 08:00:25 +00:00
if ( GSTextureCache : : Target * rt = m_tc - > LookupTarget ( TEX0 , m_width , m_height , GetFramebufferHeight ( ) ) )
2012-01-05 02:40:24 +00:00
{
t = rt - > m_texture ;
2016-03-19 11:37:25 +00:00
int delta = TEX0 . TBP0 - rt - > m_TEX0 . TBP0 ;
2016-11-04 22:02:49 +00:00
if ( delta > 0 & & DISPFB . FBW ! = 0 ) {
2016-05-05 10:17:57 +00:00
int pages = delta > > 5u ;
int y_pages = pages / DISPFB . FBW ;
y_offset = y_pages * GSLocalMemory : : m_psm [ DISPFB . PSM ] . pgs . y ;
2016-03-19 11:37:25 +00:00
GL_CACHE ( " Frame y offset %d pixels, unit %d " , y_offset , i ) ;
}
2016-04-23 10:09:57 +00:00
# ifdef ENABLE_OGL_DEBUG
2012-01-05 02:40:24 +00:00
if ( s_dump )
{
2015-05-31 15:38:52 +00:00
if ( s_savef & & s_n > = s_saven )
2012-01-05 02:40:24 +00:00
{
2016-09-05 18:34:29 +00:00
t - > Save ( m_dump_root + format ( " %05d_f%lld_fr%d_%05x_%s.bmp " , s_n , m_perfmon . GetFrame ( ) , i , ( int ) TEX0 . TBP0 , psm_str ( TEX0 . PSM ) ) ) ;
2012-01-05 02:40:24 +00:00
}
}
2016-03-31 07:50:06 +00:00
# endif
2012-01-05 02:40:24 +00:00
}
return t ;
}
2016-10-09 12:46:55 +00:00
GSTexture * GSRendererHW : : GetFeedbackOutput ( )
{
GIFRegTEX0 TEX0 ;
TEX0 . TBP0 = m_regs - > EXTBUF . EXBP ;
TEX0 . TBW = m_regs - > EXTBUF . EXBW ;
TEX0 . PSM = m_regs - > DISP [ m_regs - > EXTBUF . FBIN & 1 ] . DISPFB . PSM ;
GSTextureCache : : Target * rt = m_tc - > LookupTarget ( TEX0 , m_width , m_height , /*GetFrameRect(i).bottom*/ 0 ) ;
GSTexture * t = rt - > m_texture ;
# ifdef ENABLE_OGL_DEBUG
if ( s_dump & & s_savef & & s_n > = s_saven )
t - > Save ( m_dump_root + format ( " %05d_f%lld_fr%d_%05x_%s.bmp " , s_n , m_perfmon . GetFrame ( ) , 3 , ( int ) TEX0 . TBP0 , psm_str ( TEX0 . PSM ) ) ) ;
# endif
return t ;
}
2012-01-05 02:40:24 +00:00
void GSRendererHW : : InvalidateVideoMem ( const GIFRegBITBLTBUF & BITBLTBUF , const GSVector4i & r )
{
// printf("[%d] InvalidateVideoMem %d,%d - %d,%d %05x (%d)\n", (int)m_perfmon.GetFrame(), r.left, r.top, r.right, r.bottom, (int)BITBLTBUF.DBP, (int)BITBLTBUF.DPSM);
m_tc - > InvalidateVideoMem ( m_mem . GetOffset ( BITBLTBUF . DBP , BITBLTBUF . DBW , BITBLTBUF . DPSM ) , r ) ;
}
void GSRendererHW : : InvalidateLocalMem ( const GIFRegBITBLTBUF & BITBLTBUF , const GSVector4i & r , bool clut )
{
// printf("[%d] InvalidateLocalMem %d,%d - %d,%d %05x (%d)\n", (int)m_perfmon.GetFrame(), r.left, r.top, r.right, r.bottom, (int)BITBLTBUF.SBP, (int)BITBLTBUF.SPSM);
if ( clut ) return ; // FIXME
2016-03-19 10:43:27 +00:00
2012-01-05 02:40:24 +00:00
m_tc - > InvalidateLocalMem ( m_mem . GetOffset ( BITBLTBUF . SBP , BITBLTBUF . SBW , BITBLTBUF . SPSM ) , r ) ;
}
2016-04-14 10:00:58 +00:00
uint16 GSRendererHW : : Interpolate_UV ( float alpha , int t0 , int t1 )
2015-04-09 17:51:50 +00:00
{
float t = ( 1.0f - alpha ) * t0 + alpha * t1 ;
2016-04-14 10:00:58 +00:00
return ( uint16 ) t & ~ 0xF ; // cheap rounding
2015-04-09 17:51:50 +00:00
}
2015-04-12 15:10:45 +00:00
float GSRendererHW : : alpha0 ( int L , int X0 , int X1 )
2015-04-09 17:51:50 +00:00
{
2016-04-14 10:00:58 +00:00
int x = ( X0 + 15 ) & ~ 0xF ; // Round up
return float ( x - X0 ) / ( float ) L ;
2015-04-09 17:51:50 +00:00
}
2015-04-12 15:10:45 +00:00
float GSRendererHW : : alpha1 ( int L , int X0 , int X1 )
2015-04-09 17:51:50 +00:00
{
2016-04-14 10:00:58 +00:00
int x = ( X1 - 1 ) & ~ 0xF ; // Round down. Note -1 because right pixel isn't included in primitive so 0x100 must return 0.
return float ( x - X0 ) / ( float ) L ;
2015-04-09 17:51:50 +00:00
}
2015-04-12 10:24:07 +00:00
template < bool linear >
void GSRendererHW : : RoundSpriteOffset ( )
{
//#define DEBUG_U
//#define DEBUG_V
2015-04-16 16:27:27 +00:00
# if defined(DEBUG_V) || defined(DEBUG_U)
2015-04-12 10:24:07 +00:00
bool debug = linear ;
2015-04-16 16:27:27 +00:00
# endif
2015-04-12 10:24:07 +00:00
size_t count = m_vertex . next ;
GSVertex * v = & m_vertex . buff [ 0 ] ;
for ( size_t i = 0 ; i < count ; i + = 2 ) {
2015-04-12 15:10:45 +00:00
// Performance note: if it had any impact on perf, someone would port it to SSE (AKA GSVector)
2015-04-12 10:24:07 +00:00
// Compute the coordinate of first and last texels (in native with a linear filtering)
2016-04-14 10:00:58 +00:00
int ox = m_context - > XYOFFSET . OFX ;
int X0 = v [ i ] . XYZ . X - ox ;
int X1 = v [ i + 1 ] . XYZ . X - ox ;
int Lx = ( v [ i + 1 ] . XYZ . X - v [ i ] . XYZ . X ) ;
float ax0 = alpha0 ( Lx , X0 , X1 ) ;
float ax1 = alpha1 ( Lx , X0 , X1 ) ;
uint16 tx0 = Interpolate_UV ( ax0 , v [ i ] . U , v [ i + 1 ] . U ) ;
uint16 tx1 = Interpolate_UV ( ax1 , v [ i ] . U , v [ i + 1 ] . U ) ;
2015-04-12 10:24:07 +00:00
# ifdef DEBUG_U
if ( debug ) {
fprintf ( stderr , " u0:%d and u1:%d \n " , v [ i ] . U , v [ i + 1 ] . U ) ;
fprintf ( stderr , " a0:%f and a1:%f \n " , ax0 , ax1 ) ;
fprintf ( stderr , " t0:%d and t1:%d \n " , tx0 , tx1 ) ;
}
# endif
2016-04-14 10:00:58 +00:00
int oy = m_context - > XYOFFSET . OFY ;
int Y0 = v [ i ] . XYZ . Y - oy ;
int Y1 = v [ i + 1 ] . XYZ . Y - oy ;
int Ly = ( v [ i + 1 ] . XYZ . Y - v [ i ] . XYZ . Y ) ;
float ay0 = alpha0 ( Ly , Y0 , Y1 ) ;
float ay1 = alpha1 ( Ly , Y0 , Y1 ) ;
uint16 ty0 = Interpolate_UV ( ay0 , v [ i ] . V , v [ i + 1 ] . V ) ;
uint16 ty1 = Interpolate_UV ( ay1 , v [ i ] . V , v [ i + 1 ] . V ) ;
2015-04-12 10:24:07 +00:00
# ifdef DEBUG_V
if ( debug ) {
fprintf ( stderr , " v0:%d and v1:%d \n " , v [ i ] . V , v [ i + 1 ] . V ) ;
fprintf ( stderr , " a0:%f and a1:%f \n " , ay0 , ay1 ) ;
fprintf ( stderr , " t0:%d and t1:%d \n " , ty0 , ty1 ) ;
}
# endif
# ifdef DEBUG_U
if ( debug )
fprintf ( stderr , " GREP_BEFORE %d => %d \n " , v [ i ] . U , v [ i + 1 ] . U ) ;
# endif
# ifdef DEBUG_V
if ( debug )
fprintf ( stderr , " GREP_BEFORE %d => %d \n " , v [ i ] . V , v [ i + 1 ] . V ) ;
# endif
# if 1
// Use rounded value of the newly computed texture coordinate. It ensures
// that sampling will remains inside texture boundary
//
2015-04-16 16:27:27 +00:00
// Note for bilinear: by definition it will never work correctly! A sligh modification
// of interpolation migth trigger a discard (with alpha testing)
// Let's use something simple that correct really bad case (for a couple of 2D games).
// I hope it won't create too much glitches.
2015-04-12 15:57:37 +00:00
if ( linear ) {
2015-04-16 16:27:27 +00:00
int Lu = v [ i + 1 ] . U - v [ i ] . U ;
// Note 32 is based on taisho-mononoke
if ( ( Lu > 0 ) & & ( Lu < = ( Lx + 32 ) ) ) {
v [ i + 1 ] . U - = 8 ;
2015-04-12 15:57:37 +00:00
}
2015-04-12 10:24:07 +00:00
} else {
2015-04-12 15:57:37 +00:00
if ( tx0 < = tx1 ) {
v [ i ] . U = tx0 ;
v [ i + 1 ] . U = tx1 + 16 ;
} else {
v [ i ] . U = tx0 + 15 ;
2015-04-14 16:39:26 +00:00
v [ i + 1 ] . U = tx1 ;
2015-04-12 15:57:37 +00:00
}
2015-04-12 10:24:07 +00:00
}
# endif
# if 1
2015-04-12 15:57:37 +00:00
if ( linear ) {
2015-04-16 16:27:27 +00:00
int Lv = v [ i + 1 ] . V - v [ i ] . V ;
if ( ( Lv > 0 ) & & ( Lv < = ( Ly + 32 ) ) ) {
v [ i + 1 ] . V - = 8 ;
2015-04-12 15:57:37 +00:00
}
2015-04-12 10:24:07 +00:00
} else {
2015-04-12 15:57:37 +00:00
if ( ty0 < = ty1 ) {
v [ i ] . V = ty0 ;
v [ i + 1 ] . V = ty1 + 16 ;
} else {
v [ i ] . V = ty0 + 15 ;
2015-04-14 16:39:26 +00:00
v [ i + 1 ] . V = ty1 ;
2015-04-12 15:57:37 +00:00
}
2015-04-12 10:24:07 +00:00
}
# endif
# ifdef DEBUG_U
if ( debug )
fprintf ( stderr , " GREP_AFTER %d => %d \n \n " , v [ i ] . U , v [ i + 1 ] . U ) ;
# endif
# ifdef DEBUG_V
if ( debug )
fprintf ( stderr , " GREP_AFTER %d => %d \n \n " , v [ i ] . V , v [ i + 1 ] . V ) ;
# endif
}
}
2012-01-05 02:40:24 +00:00
void GSRendererHW : : Draw ( )
{
2016-05-05 14:22:14 +00:00
if ( m_dev - > IsLost ( ) | | IsBadFrame ( ) ) {
2015-05-24 10:53:23 +00:00
GL_INS ( " Warning skipping a draw call (%d) " , s_n ) ;
2015-05-05 15:48:11 +00:00
return ;
}
2015-05-16 17:28:22 +00:00
GL_PUSH ( " HW Draw %d " , s_n ) ;
2012-01-05 02:40:24 +00:00
GSDrawingEnvironment & env = m_env ;
GSDrawingContext * context = m_context ;
2016-09-25 16:56:48 +00:00
const GSLocalMemory : : psm_t & tex_psm = GSLocalMemory : : m_psm [ m_context - > TEX0 . PSM ] ;
// skip alpha test if possible
// Note: do it first so we know if frame/depth writes are masked
GIFRegTEST TEST = context - > TEST ;
GIFRegFRAME FRAME = context - > FRAME ;
GIFRegZBUF ZBUF = context - > ZBUF ;
uint32 fm = context - > FRAME . FBMSK ;
uint32 zm = context - > ZBUF . ZMSK | | context - > TEST . ZTE = = 0 ? 0xffffffff : 0 ;
// Note required to compute TryAlphaTest below. So do it now.
if ( PRIM - > TME & & tex_psm . pal > 0 )
m_mem . m_clut . Read32 ( context - > TEX0 , env . TEXA ) ;
// Test if we can optimize Alpha Test as a NOP
2016-10-05 07:41:28 +00:00
context - > TEST . ATE = context - > TEST . ATE & & ! GSRenderer : : TryAlphaTest ( fm , zm ) ;
2016-09-25 16:56:48 +00:00
context - > FRAME . FBMSK = fm ;
context - > ZBUF . ZMSK = zm ! = 0 ;
2012-01-05 02:40:24 +00:00
2015-06-05 20:37:34 +00:00
// It is allowed to use the depth and rt at the same location. However at least 1 must
2016-09-25 17:00:33 +00:00
// be disabled. Or the written value must be the same on both channels.
2015-07-04 08:52:43 +00:00
// 1/ GoW uses a Cd blending on a 24 bits buffer (no alpha)
2015-08-04 17:30:12 +00:00
// 2/ SuperMan really draws (0,0,0,0) color and a (0) 32-bits depth
// 3/ 50cents really draws (0,0,0,128) color and a (0) 24 bits depth
2015-07-09 14:19:11 +00:00
// Note: FF DoC has both buffer at same location but disable the depth test (write?) with ZTE = 0
2015-08-04 17:30:12 +00:00
const bool no_rt = ( context - > ALPHA . IsCd ( ) & & PRIM - > ABE & & ( context - > FRAME . PSM = = 1 ) ) ;
2016-02-17 18:58:27 +00:00
const bool no_ds = ! no_rt & & (
2016-09-25 17:00:33 +00:00
// Depth is always pass/fail (no read) and write are discarded (tekken 5). (Note: DATE is currently implemented with a stencil buffer => a depth/stencil buffer)
( zm ! = 0 & & m_context - > TEST . ZTST < = ZTST_ALWAYS & & ! m_context - > TEST . DATE ) | |
2016-02-17 18:58:27 +00:00
// Depth will be written through the RT
2016-09-25 17:00:33 +00:00
( context - > FRAME . FBP = = context - > ZBUF . ZBP & & ! PRIM - > TME & & zm = = 0 & & fm = = 0 & & context - > TEST . ZTE )
2016-02-17 18:58:27 +00:00
) ;
2015-06-05 20:37:34 +00:00
2016-04-29 21:38:46 +00:00
const bool draw_sprite_tex = PRIM - > TME & & ( m_vt . m_primclass = = GS_SPRITE_CLASS ) ;
const GSVector4 delta_p = m_vt . m_max . p - m_vt . m_min . p ;
2016-04-29 23:37:53 +00:00
bool single_page = ( delta_p . x < = 64.0f ) & & ( delta_p . y < = 64.0f ) ;
2016-04-29 21:38:46 +00:00
2016-04-29 16:00:23 +00:00
if ( m_channel_shuffle ) {
2016-04-29 21:38:46 +00:00
m_channel_shuffle = draw_sprite_tex & & ( m_context - > TEX0 . PSM = = PSM_PSMT8 ) & & single_page ;
2016-04-29 16:00:23 +00:00
if ( m_channel_shuffle ) {
2016-05-05 09:18:48 +00:00
GL_CACHE ( " Channel shuffle effect detected SKIP " ) ;
2016-04-29 16:00:23 +00:00
return ;
}
2016-04-29 21:38:46 +00:00
} else if ( draw_sprite_tex & & m_context - > FRAME . Block ( ) = = m_context - > TEX0 . TBP0 ) {
2016-04-28 17:39:57 +00:00
// Special post-processing effect
2016-10-02 16:28:40 +00:00
if ( ( m_context - > TEX0 . PSM = = PSM_PSMT8 ) & & single_page ) {
2016-05-03 06:52:35 +00:00
GL_INS ( " Channel shuffle effect detected " ) ;
m_channel_shuffle = true ;
2016-04-28 17:39:57 +00:00
} else {
2016-10-14 18:24:52 +00:00
GL_DBG ( " Special post-processing effect not supported " ) ;
2016-04-28 17:39:57 +00:00
m_channel_shuffle = false ;
}
} else {
m_channel_shuffle = false ;
}
2012-01-05 02:40:24 +00:00
GIFRegTEX0 TEX0 ;
TEX0 . TBP0 = context - > FRAME . Block ( ) ;
TEX0 . TBW = context - > FRAME . FBW ;
TEX0 . PSM = context - > FRAME . PSM ;
2015-06-05 20:37:34 +00:00
2016-05-05 16:01:44 +00:00
GSTextureCache : : Target * rt = NULL ;
GSTexture * rt_tex = NULL ;
if ( ! no_rt ) {
2016-10-03 18:34:04 +00:00
rt = m_tc - > LookupTarget ( TEX0 , m_width , m_height , GSTextureCache : : RenderTarget , true , fm ) ;
2016-05-05 16:01:44 +00:00
rt_tex = rt - > m_texture ;
}
2012-01-05 02:40:24 +00:00
TEX0 . TBP0 = context - > ZBUF . Block ( ) ;
TEX0 . TBW = context - > FRAME . FBW ;
TEX0 . PSM = context - > ZBUF . PSM ;
2016-05-05 16:01:44 +00:00
GSTextureCache : : Target * ds = NULL ;
GSTexture * ds_tex = NULL ;
if ( ! no_ds ) {
ds = m_tc - > LookupTarget ( TEX0 , m_width , m_height , GSTextureCache : : DepthStencil , context - > DepthWrite ( ) ) ;
ds_tex = ds - > m_texture ;
2012-01-05 02:40:24 +00:00
}
GSTextureCache : : Source * tex = NULL ;
2015-06-17 18:02:03 +00:00
m_texture_shuffle = false ;
2012-01-05 02:40:24 +00:00
if ( PRIM - > TME )
{
2016-09-24 07:57:01 +00:00
GIFRegCLAMP MIP_CLAMP = context - > CLAMP ;
2016-10-01 15:37:55 +00:00
int mxl = std : : min < int > ( ( int ) m_context - > TEX1 . MXL , 6 ) ;
m_lod = GSVector2i ( 0 , 0 ) ;
2016-09-24 07:57:01 +00:00
// Code from the SW renderer
if ( IsMipMapActive ( ) ) {
2016-09-26 17:42:07 +00:00
int interpolation = ( context - > TEX1 . MMIN & 1 ) + 1 ; // 1: round, 2: tri
2016-09-24 07:57:01 +00:00
2016-09-24 18:05:39 +00:00
int k = ( m_context - > TEX1 . K + 8 ) > > 4 ;
2016-09-24 07:57:01 +00:00
int lcm = m_context - > TEX1 . LCM ;
if ( ( int ) m_vt . m_lod . x > = mxl ) {
k = mxl ; // set lod to max level
lcm = 1 ; // constant lod
}
if ( PRIM - > FST ) {
ASSERT ( lcm = = 1 ) ;
ASSERT ( ( ( m_vt . m_min . t . uph ( m_vt . m_max . t ) = = GSVector4 : : zero ( ) ) . mask ( ) & 3 ) = = 3 ) ; // ratchet and clank (menu)
lcm = 1 ;
}
if ( lcm = = 1 ) {
2016-10-01 15:37:55 +00:00
m_lod . x = std : : max < int > ( k , 0 ) ;
m_lod . y = m_lod . x ;
2016-09-24 07:57:01 +00:00
} else {
// Not constant but who care !
2016-09-26 17:42:07 +00:00
if ( interpolation = = 2 ) {
// Mipmap Linear. Both layers are sampled, only take the big one
2016-10-01 15:37:55 +00:00
m_lod . x = std : : max < int > ( ( int ) floor ( m_vt . m_lod . x ) , 0 ) ;
2016-09-26 17:42:07 +00:00
} else {
2016-09-28 07:55:04 +00:00
// On GS lod is a fixed float number 7:4 (4 bit for the frac part)
2016-09-28 17:35:46 +00:00
#if 0
2016-10-01 15:37:55 +00:00
m_lod . x = std : : max < int > ( ( int ) round ( m_vt . m_lod . x + 0.0625 ) , 0 ) ;
2016-09-28 17:35:46 +00:00
# else
// Same as above with a bigger margin on rounding
// The goal is to avoid 1 undrawn pixels around the edge which trigger the load of the big
// layer.
if ( ceil ( m_vt . m_lod . x ) < m_vt . m_lod . y )
2016-10-01 15:37:55 +00:00
m_lod . x = std : : max < int > ( ( int ) round ( m_vt . m_lod . x + 0.0625 + 0.01 ) , 0 ) ;
2016-09-28 17:35:46 +00:00
else
2016-10-01 15:37:55 +00:00
m_lod . x = std : : max < int > ( ( int ) round ( m_vt . m_lod . x + 0.0625 ) , 0 ) ;
2016-09-28 17:35:46 +00:00
# endif
2016-09-26 17:42:07 +00:00
}
2016-10-01 15:37:55 +00:00
m_lod . y = std : : max < int > ( ( int ) ceil ( m_vt . m_lod . y ) , 0 ) ;
2016-09-24 07:57:01 +00:00
}
2016-10-01 15:37:55 +00:00
m_lod . x = std : : min < int > ( m_lod . x , mxl ) ;
m_lod . y = std : : min < int > ( m_lod . y , mxl ) ;
2016-09-26 17:42:07 +00:00
2016-10-01 15:37:55 +00:00
TEX0 = GetTex0Layer ( m_lod . x ) ;
2016-09-25 09:51:48 +00:00
2016-10-01 15:37:55 +00:00
MIP_CLAMP . MINU > > = m_lod . x ;
MIP_CLAMP . MINV > > = m_lod . x ;
MIP_CLAMP . MAXU > > = m_lod . x ;
MIP_CLAMP . MAXV > > = m_lod . x ;
2016-09-24 07:57:01 +00:00
2016-10-01 15:37:55 +00:00
for ( int i = 0 ; i < m_lod . x ; i + + ) {
2016-09-24 07:57:01 +00:00
m_vt . m_min . t * = 0.5f ;
m_vt . m_max . t * = 0.5f ;
}
2016-10-01 15:37:55 +00:00
GL_CACHE ( " Mipmap LOD %d %d (%f %f) new size %dx%d (K %d L %u) " , m_lod . x , m_lod . y , m_vt . m_lod . x , m_vt . m_lod . y , 1 < < TEX0 . TW , 1 < < TEX0 . TH , m_context - > TEX1 . K , m_context - > TEX1 . L ) ;
2016-09-25 09:51:48 +00:00
} else {
TEX0 = GetTex0Layer ( 0 ) ;
2016-09-24 07:57:01 +00:00
}
2016-09-26 18:34:38 +00:00
m_context - > offset . tex = m_mem . GetOffset ( TEX0 . TBP0 , TEX0 . TBW , TEX0 . PSM ) ;
2016-04-22 17:46:44 +00:00
2012-01-05 02:40:24 +00:00
GSVector4i r ;
2016-09-24 16:57:47 +00:00
GetTextureMinMax ( r , TEX0 , MIP_CLAMP , m_vt . IsLinear ( ) ) ;
2012-01-05 02:40:24 +00:00
2016-09-24 07:57:01 +00:00
tex = tex_psm . depth ? m_tc - > LookupDepthSource ( TEX0 , env . TEXA , r ) : m_tc - > LookupSource ( TEX0 , env . TEXA , r ) ;
2012-01-05 02:40:24 +00:00
2016-10-01 15:38:38 +00:00
// Round 2
2016-10-02 08:51:29 +00:00
if ( IsMipMapActive ( ) & & m_mipmap = = 2 & & ! tex_psm . depth ) {
2016-10-01 15:38:38 +00:00
// Upload remaining texture layers
GSVector4 tmin = m_vt . m_min . t ;
GSVector4 tmax = m_vt . m_max . t ;
for ( int layer = m_lod . x + 1 ; layer < = m_lod . y ; layer + + ) {
const GIFRegTEX0 & MIP_TEX0 = GetTex0Layer ( layer ) ;
m_context - > offset . tex = m_mem . GetOffset ( MIP_TEX0 . TBP0 , MIP_TEX0 . TBW , MIP_TEX0 . PSM ) ;
MIP_CLAMP . MINU > > = 1 ;
MIP_CLAMP . MINV > > = 1 ;
MIP_CLAMP . MAXU > > = 1 ;
MIP_CLAMP . MAXV > > = 1 ;
m_vt . m_min . t * = 0.5f ;
m_vt . m_max . t * = 0.5f ;
GetTextureMinMax ( r , MIP_TEX0 , MIP_CLAMP , m_vt . IsLinear ( ) ) ;
tex - > UpdateLayer ( MIP_TEX0 , r , layer - m_lod . x ) ;
}
m_vt . m_min . t = tmin ;
m_vt . m_max . t = tmax ;
}
2015-07-30 07:15:04 +00:00
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
// Initially code also tested the RT but it gives too much false-positive
//
// Both input and output are 16 bits and texture was initially 32 bits!
2016-04-22 17:46:44 +00:00
m_texture_shuffle = ( GSLocalMemory : : m_psm [ context - > FRAME . PSM ] . bpp = = 16 ) & & ( tex_psm . bpp = = 16 )
2016-04-29 21:38:46 +00:00
& & draw_sprite_tex & & tex - > m_32_bits_fmt ;
2015-07-30 07:15:04 +00:00
// Texture shuffle is not yet supported with strange clamp mode
ASSERT ( ! m_texture_shuffle | | ( context - > CLAMP . WMS < 3 & & context - > CLAMP . WMT < 3 ) ) ;
2016-04-29 16:00:23 +00:00
2016-04-29 21:59:42 +00:00
if ( tex - > m_target & & m_context - > TEX0 . PSM = = PSM_PSMT8 & & single_page & & draw_sprite_tex ) {
2016-04-29 16:00:23 +00:00
GL_INS ( " Channel shuffle effect detected (2nd shot) " ) ;
m_channel_shuffle = true ;
} else {
m_channel_shuffle = false ;
}
2015-07-30 07:15:04 +00:00
}
if ( rt ) {
// Be sure texture shuffle detection is properly propagated
// Otherwise set or clear the flag (Code in texture cache only set the flag)
// Note: it is important to clear the flag when RT is used as a real 16 bits target.
2016-04-22 17:46:44 +00:00
rt - > m_32_bits_fmt = m_texture_shuffle | | ( GSLocalMemory : : m_psm [ context - > FRAME . PSM ] . bpp ! = 16 ) ;
2012-01-05 02:40:24 +00:00
}
if ( s_dump )
{
uint64 frame = m_perfmon . GetFrame ( ) ;
string s ;
2015-05-23 10:23:05 +00:00
if ( s_n > = s_saven ) {
// Dump Register state
s = format ( " %05d_context.txt " , s_n ) ;
2016-07-14 17:41:21 +00:00
m_env . Dump ( m_dump_root + s ) ;
m_context - > Dump ( m_dump_root + s ) ;
2015-05-23 10:23:05 +00:00
}
2015-05-01 11:35:21 +00:00
if ( s_savet & & s_n > = s_saven & & tex )
2012-01-05 02:40:24 +00:00
{
2016-09-27 16:59:06 +00:00
s = format ( " %05d_f%lld_itex_%05x_%s_%d%d_%02x_%02x_%02x_%02x.dds " ,
2016-09-05 18:34:29 +00:00
s_n , frame , ( int ) context - > TEX0 . TBP0 , psm_str ( context - > TEX0 . PSM ) ,
2012-01-05 02:40:24 +00:00
( int ) context - > CLAMP . WMS , ( int ) context - > CLAMP . WMT ,
( int ) context - > CLAMP . MINU , ( int ) context - > CLAMP . MAXU ,
( int ) context - > CLAMP . MINV , ( int ) context - > CLAMP . MAXV ) ;
2016-08-20 12:05:53 +00:00
tex - > m_texture - > Save ( m_dump_root + s , true ) ;
2012-01-05 02:40:24 +00:00
if ( tex - > m_palette )
{
2016-09-27 16:59:06 +00:00
s = format ( " %05d_f%lld_itpx_%05x_%s.dds " , s_n , frame , context - > TEX0 . CBP , psm_str ( context - > TEX0 . CPSM ) ) ;
2012-01-05 02:40:24 +00:00
2016-08-20 12:05:53 +00:00
tex - > m_palette - > Save ( m_dump_root + s , true ) ;
2012-01-05 02:40:24 +00:00
}
}
if ( s_save & & s_n > = s_saven )
{
2016-09-05 18:34:29 +00:00
s = format ( " %05d_f%lld_rt0_%05x_%s.bmp " , s_n , frame , context - > FRAME . Block ( ) , psm_str ( context - > FRAME . PSM ) ) ;
2012-01-05 02:40:24 +00:00
2015-06-05 20:37:34 +00:00
if ( rt )
2016-07-14 17:41:21 +00:00
rt - > m_texture - > Save ( m_dump_root + s ) ;
2012-01-05 02:40:24 +00:00
}
if ( s_savez & & s_n > = s_saven )
{
2016-09-05 18:34:29 +00:00
s = format ( " %05d_f%lld_rz0_%05x_%s.bmp " , s_n , frame , context - > ZBUF . Block ( ) , psm_str ( context - > ZBUF . PSM ) ) ;
2012-01-05 02:40:24 +00:00
2015-09-10 12:42:54 +00:00
if ( ds_tex )
2016-07-14 17:41:21 +00:00
ds_tex - > Save ( m_dump_root + s ) ;
2012-01-05 02:40:24 +00:00
}
}
2016-03-19 10:43:27 +00:00
// The rectangle of the draw
GSVector4i r = GSVector4i ( m_vt . m_min . p . xyxy ( m_vt . m_max . p ) ) . rintersect ( GSVector4i ( context - > scissor . in ) ) ;
2015-08-04 17:30:12 +00:00
if ( m_hacks . m_oi & & ! ( this - > * m_hacks . m_oi ) ( rt_tex , ds_tex , tex ) )
2012-01-05 02:40:24 +00:00
{
2016-03-19 10:43:27 +00:00
GL_INS ( " Warning skipping a draw call (%d) " , s_n ) ;
return ;
}
if ( ! OI_BlitFMV ( rt , tex , r ) ) {
GL_INS ( " Warning skipping a draw call (%d) " , s_n ) ;
2012-01-05 02:40:24 +00:00
return ;
}
2016-03-19 12:35:23 +00:00
if ( ! m_userhacks_disable_gs_mem_clear ) {
2016-09-20 16:57:07 +00:00
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
2016-09-30 17:15:25 +00:00
if ( ( m_vt . m_primclass = = GS_SPRITE_CLASS ) & & ! PRIM - > TME // Direct write
& & ( ! PRIM - > ABE | | m_context - > ALPHA . IsOpaque ( ) ) // No transparency
2016-09-20 16:57:07 +00:00
& & ( m_context - > FRAME . FBMSK = = 0 ) // no color mask
& & ! m_context - > TEST . ATE // no alpha test
& & ( ! m_context - > TEST . ZTE | | m_context - > TEST . ZTST = = ZTST_ALWAYS ) // no depth test
& & ( m_vt . m_eq . rgba = = 0xFFFF ) // constant color write
& & r . x = = 0 & & r . y = = 0 ) { // Likely full buffer write
OI_GsMemClear ( ) ;
OI_DoubleHalfClear ( rt_tex , ds_tex ) ;
}
2016-03-19 12:35:23 +00:00
}
2015-05-16 17:28:22 +00:00
// A couple of hack to avoid upscaling issue. So far it seems to impacts mostly sprite
2016-05-09 07:43:24 +00:00
// Note: first hack corrects both position and texture coordinate
// Note: second hack corrects only the texture coordinate
if ( ( m_upscale_multiplier > 1 ) & & ( m_vt . m_primclass = = GS_SPRITE_CLASS ) ) {
2015-04-07 17:18:24 +00:00
size_t count = m_vertex . next ;
GSVertex * v = & m_vertex . buff [ 0 ] ;
2015-04-03 23:21:46 +00:00
// Hack to avoid vertical black line in various games (ace combat/tekken)
if ( m_userhacks_align_sprite_X ) {
// Note for performance reason I do the check only once on the first
// primitive
int win_position = v [ 1 ] . XYZ . X - context - > XYOFFSET . OFX ;
const bool unaligned_position = ( ( win_position & 0xF ) = = 8 ) ;
2015-04-16 16:08:32 +00:00
const bool unaligned_texture = ( ( v [ 1 ] . U & 0xF ) = = 0 ) & & PRIM - > FST ; // I'm not sure this check is useful
const bool hole_in_vertex = ( count < 4 ) | | ( v [ 1 ] . XYZ . X ! = v [ 2 ] . XYZ . X ) ;
if ( hole_in_vertex & & unaligned_position & & ( unaligned_texture | | ! PRIM - > FST ) ) {
2015-04-03 23:21:46 +00:00
// Normaly vertex are aligned on full pixels and texture in half
// pixels. Let's extend the coverage of an half-pixel to avoid
// hole after upscaling
for ( size_t i = 0 ; i < count ; i + = 2 ) {
v [ i + 1 ] . XYZ . X + = 8 ;
2015-04-16 16:08:32 +00:00
// I really don't know if it is a good idea. Neither what to do for !PRIM->FST
if ( unaligned_texture )
2015-04-03 23:21:46 +00:00
v [ i + 1 ] . U + = 8 ;
}
2015-04-03 19:02:25 +00:00
}
}
2016-05-09 07:43:24 +00:00
// Noting to do if no texture is sampled
if ( PRIM - > FST & & draw_sprite_tex ) {
2015-04-15 07:40:06 +00:00
if ( ( m_userhacks_round_sprite_offset > 1 ) | | ( m_userhacks_round_sprite_offset = = 1 & & ! m_vt . IsLinear ( ) ) ) {
if ( m_vt . IsLinear ( ) )
RoundSpriteOffset < true > ( ) ;
else
RoundSpriteOffset < false > ( ) ;
}
} else {
; // vertical line in Yakuza (note check m_userhacks_align_sprite_X behavior)
2015-04-09 17:51:50 +00:00
}
2015-04-03 16:11:47 +00:00
}
2012-01-05 02:40:24 +00:00
//
2015-08-04 17:30:12 +00:00
DrawPrims ( rt_tex , ds_tex , tex ) ;
2012-01-05 02:40:24 +00:00
//
context - > TEST = TEST ;
context - > FRAME = FRAME ;
context - > ZBUF = ZBUF ;
//
2016-03-10 17:28:08 +00:00
// Help to detect rendering outside of the framebuffer
# if _DEBUG
if ( m_upscale_multiplier * r . z > m_width ) {
GL_INS ( " ERROR: RT width is too small only %d but require %d " , m_width , m_upscale_multiplier * r . z ) ;
}
if ( m_upscale_multiplier * r . w > m_height ) {
GL_INS ( " ERROR: RT height is too small only %d but require %d " , m_height , m_upscale_multiplier * r . w ) ;
}
# endif
2015-06-05 20:37:34 +00:00
if ( fm ! = 0xffffffff & & rt )
2012-01-05 02:40:24 +00:00
{
2016-03-06 13:24:20 +00:00
//rt->m_valid = rt->m_valid.runion(r);
rt - > UpdateValidity ( r ) ;
2012-01-05 02:40:24 +00:00
m_tc - > InvalidateVideoMem ( context - > offset . fb , r , false ) ;
2015-06-05 21:37:06 +00:00
m_tc - > InvalidateVideoMemType ( GSTextureCache : : DepthStencil , context - > FRAME . Block ( ) ) ;
2012-01-05 02:40:24 +00:00
}
2015-08-04 17:30:12 +00:00
if ( zm ! = 0xffffffff & & ds )
2012-01-05 02:40:24 +00:00
{
2016-03-06 13:24:20 +00:00
//ds->m_valid = ds->m_valid.runion(r);
ds - > UpdateValidity ( r ) ;
2012-01-05 02:40:24 +00:00
m_tc - > InvalidateVideoMem ( context - > offset . zb , r , false ) ;
2015-06-05 21:37:06 +00:00
m_tc - > InvalidateVideoMemType ( GSTextureCache : : RenderTarget , context - > ZBUF . Block ( ) ) ;
2012-01-05 02:40:24 +00:00
}
//
2015-06-14 08:19:15 +00:00
if ( m_hacks . m_oo )
2012-01-05 02:40:24 +00:00
{
( this - > * m_hacks . m_oo ) ( ) ;
}
if ( s_dump )
{
uint64 frame = m_perfmon . GetFrame ( ) ;
string s ;
if ( s_save & & s_n > = s_saven )
{
2016-09-05 18:34:29 +00:00
s = format ( " %05d_f%lld_rt1_%05x_%s.bmp " , s_n , frame , context - > FRAME . Block ( ) , psm_str ( context - > FRAME . PSM ) ) ;
2012-01-05 02:40:24 +00:00
2015-06-05 20:37:34 +00:00
if ( rt )
2016-07-14 17:41:21 +00:00
rt - > m_texture - > Save ( m_dump_root + s ) ;
2012-01-05 02:40:24 +00:00
}
if ( s_savez & & s_n > = s_saven )
{
2016-09-05 18:34:29 +00:00
s = format ( " %05d_f%lld_rz1_%05x_%s.bmp " , s_n , frame , context - > ZBUF . Block ( ) , psm_str ( context - > ZBUF . PSM ) ) ;
2012-01-05 02:40:24 +00:00
2015-09-25 18:59:36 +00:00
if ( ds_tex )
2016-07-14 17:41:21 +00:00
ds_tex - > Save ( m_dump_root + s ) ;
2012-01-05 02:40:24 +00:00
}
2015-08-05 17:10:55 +00:00
if ( s_savel > 0 & & ( s_n - s_saven ) > s_savel )
{
2015-05-05 15:48:11 +00:00
s_dump = 0 ;
}
2012-01-05 02:40:24 +00:00
}
# ifdef DISABLE_HW_TEXTURE_CACHE
2016-03-31 07:50:06 +00:00
2015-06-05 20:37:34 +00:00
if ( rt )
m_tc - > Read ( rt , r ) ;
2012-01-05 02:40:24 +00:00
# endif
}
// hacks
GSRendererHW : : Hacks : : Hacks ( )
: m_oi_map ( m_oi_list )
, m_oo_map ( m_oo_list )
, m_cu_map ( m_cu_list )
, m_oi ( NULL )
, m_oo ( NULL )
, m_cu ( NULL )
{
2016-05-24 19:52:06 +00:00
bool is_opengl = ( static_cast < GSRendererType > ( theApp . GetConfigI ( " Renderer " ) ) = = GSRendererType : : OGL_HW ) ;
bool can_handle_depth = ( ! theApp . GetConfigB ( " UserHacks " ) | | ! theApp . GetConfigB ( " UserHacks_DisableDepthSupport " ) ) & & is_opengl ;
2016-04-11 20:41:19 +00:00
2012-01-05 02:40:24 +00:00
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : FFXII , CRC : : EU , & GSRendererHW : : OI_FFXII ) ) ;
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : FFX , CRC : : RegionCount , & GSRendererHW : : OI_FFX ) ) ;
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : MetalSlug6 , CRC : : RegionCount , & GSRendererHW : : OI_MetalSlug6 ) ) ;
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : RozenMaidenGebetGarden , CRC : : RegionCount , & GSRendererHW : : OI_RozenMaidenGebetGarden ) ) ;
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : StarWarsForceUnleashed , CRC : : RegionCount , & GSRendererHW : : OI_StarWarsForceUnleashed ) ) ;
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : SpyroNewBeginning , CRC : : RegionCount , & GSRendererHW : : OI_SpyroNewBeginning ) ) ;
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : SpyroEternalNight , CRC : : RegionCount , & GSRendererHW : : OI_SpyroEternalNight ) ) ;
2015-07-05 15:32:57 +00:00
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : SuperManReturns , CRC : : RegionCount , & GSRendererHW : : OI_SuperManReturns ) ) ;
2017-01-12 00:27:03 +00:00
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : TalesOfLegendia , CRC : : RegionCount , & GSRendererHW : : OI_TalesOfLegendia ) ) ;
2016-05-16 08:55:02 +00:00
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : ArTonelico2 , CRC : : RegionCount , & GSRendererHW : : OI_ArTonelico2 ) ) ;
2016-09-04 17:41:31 +00:00
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : ItadakiStreet , CRC : : RegionCount , & GSRendererHW : : OI_ItadakiStreet ) ) ;
2016-09-18 15:25:53 +00:00
2016-09-20 17:09:36 +00:00
if ( ! can_handle_depth ) {
2016-04-11 20:41:19 +00:00
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : SMTNocturne , CRC : : RegionCount , & GSRendererHW : : OI_SMTNocturne ) ) ;
2016-09-20 17:09:36 +00:00
m_oi_list . push_back ( HackEntry < OI_Ptr > ( CRC : : GodOfWar2 , CRC : : RegionCount , & GSRendererHW : : OI_GodOfWar2 ) ) ;
}
2012-01-05 02:40:24 +00:00
m_oo_list . push_back ( HackEntry < OO_Ptr > ( CRC : : DBZBT2 , CRC : : RegionCount , & GSRendererHW : : OO_DBZBT2 ) ) ;
m_oo_list . push_back ( HackEntry < OO_Ptr > ( CRC : : MajokkoALaMode2 , CRC : : RegionCount , & GSRendererHW : : OO_MajokkoALaMode2 ) ) ;
2016-10-01 16:47:21 +00:00
m_oo_list . push_back ( HackEntry < OO_Ptr > ( CRC : : Jak2 , CRC : : RegionCount , & GSRendererHW : : OO_Jak ) ) ;
2016-09-30 17:22:16 +00:00
m_oo_list . push_back ( HackEntry < OO_Ptr > ( CRC : : Jak3 , CRC : : RegionCount , & GSRendererHW : : OO_Jak ) ) ;
2016-10-02 09:57:09 +00:00
m_oo_list . push_back ( HackEntry < OO_Ptr > ( CRC : : JakX , CRC : : RegionCount , & GSRendererHW : : OO_Jak ) ) ;
2012-01-05 02:40:24 +00:00
m_cu_list . push_back ( HackEntry < CU_Ptr > ( CRC : : DBZBT2 , CRC : : RegionCount , & GSRendererHW : : CU_DBZBT2 ) ) ;
m_cu_list . push_back ( HackEntry < CU_Ptr > ( CRC : : MajokkoALaMode2 , CRC : : RegionCount , & GSRendererHW : : CU_MajokkoALaMode2 ) ) ;
m_cu_list . push_back ( HackEntry < CU_Ptr > ( CRC : : TalesOfAbyss , CRC : : RegionCount , & GSRendererHW : : CU_TalesOfAbyss ) ) ;
}
void GSRendererHW : : Hacks : : SetGameCRC ( const CRC : : Game & game )
{
uint32 hash = ( uint32 ) ( ( game . region < < 24 ) | game . title ) ;
m_oi = m_oi_map [ hash ] ;
m_oo = m_oo_map [ hash ] ;
m_cu = m_cu_map [ hash ] ;
2015-06-14 17:42:11 +00:00
if ( game . flags & CRC : : PointListPalette ) {
2012-01-05 02:40:24 +00:00
ASSERT ( m_oi = = NULL ) ;
m_oi = & GSRendererHW : : OI_PointListPalette ;
}
2015-06-14 17:42:11 +00:00
}
2016-09-20 16:57:07 +00:00
// Trick to do a fast clear on the GS
// Set frame buffer pointer on the start of the buffer. Set depth buffer pointer on the half buffer
// FB + depth write will fill the full buffer.
void GSRendererHW : : OI_DoubleHalfClear ( GSTexture * rt , GSTexture * ds )
2015-06-14 17:42:11 +00:00
{
2016-09-20 16:57:07 +00:00
// Note gs mem clear must be tested before calling this function
// Limit further to unmask Z write
if ( ! m_context - > ZBUF . ZMSK & & rt & & ds ) {
2016-09-18 10:20:09 +00:00
const GSVertex * v = & m_vertex . buff [ 0 ] ;
const GSLocalMemory : : psm_t & frame_psm = GSLocalMemory : : m_psm [ m_context - > FRAME . PSM ] ;
2016-10-02 08:18:15 +00:00
//const GSLocalMemory::psm_t& depth_psm = GSLocalMemory::m_psm[m_context->ZBUF.PSM];
2016-09-18 10:20:09 +00:00
// Z and color must be constant and the same
2016-09-18 15:24:33 +00:00
if ( m_vt . m_eq . rgba ! = 0xFFFF | | ! m_vt . m_eq . z | | v [ 1 ] . XYZ . Z ! = v [ 1 ] . RGBAQ . u32 [ 0 ] )
2016-09-20 16:57:07 +00:00
return ;
2016-09-17 08:28:20 +00:00
2016-09-30 20:25:24 +00:00
// Format doesn't have the same size. It smells fishy (xmen...)
//if (frame_psm.trbpp != depth_psm.trbpp)
// return;
2016-09-17 08:28:20 +00:00
2016-09-18 10:20:09 +00:00
// Size of the current draw
2016-11-04 18:12:12 +00:00
uint32 w_pages = static_cast < uint32 > ( roundf ( m_vt . m_max . p . x / frame_psm . pgs . x ) ) ;
uint32 h_pages = static_cast < uint32 > ( roundf ( m_vt . m_max . p . y / frame_psm . pgs . y ) ) ;
2016-09-18 10:20:09 +00:00
uint32 written_pages = w_pages * h_pages ;
2016-09-16 21:57:33 +00:00
2016-09-18 10:20:09 +00:00
// Frame and depth pointer can be inverted
2016-09-16 21:57:33 +00:00
uint32 base ;
uint32 half ;
if ( m_context - > FRAME . FBP > m_context - > ZBUF . ZBP ) {
base = m_context - > ZBUF . ZBP ;
half = m_context - > FRAME . FBP ;
} else {
base = m_context - > FRAME . FBP ;
half = m_context - > ZBUF . ZBP ;
}
2016-09-18 10:20:09 +00:00
// If both buffers are side by side we can expect a fast clear in on-going
if ( half < = ( base + written_pages ) ) {
uint32 color = v [ 1 ] . RGBAQ . u32 [ 0 ] ;
2016-09-17 08:28:20 +00:00
2016-09-18 10:20:09 +00:00
GL_INS ( " OI_DoubleHalfClear: base %x half %x. w_pages %d h_pages %d fbw %d. Color %x " , base < < 5 , half < < 5 , w_pages , h_pages , m_context - > FRAME . FBW , color ) ;
if ( m_context - > FRAME . FBP > m_context - > ZBUF . ZBP ) {
// Only pure clear are supported for depth
ASSERT ( color = = 0 ) ;
m_dev - > ClearDepth ( ds ) ;
2016-09-16 21:57:33 +00:00
} else {
2016-09-18 10:20:09 +00:00
m_dev - > ClearRenderTarget ( rt , color ) ;
2016-09-16 21:57:33 +00:00
}
}
}
}
2016-03-19 12:35:23 +00:00
// Note: hack is safe, but it could impact the perf a little (normally games do only a couple of clear by frame)
void GSRendererHW : : OI_GsMemClear ( )
{
2016-09-20 16:57:07 +00:00
// Note gs mem clear must be tested before calling this function
// Limit it further to a full screen 0 write
if ( ( m_vertex . next = = 2 ) & & m_vt . m_min . c . eq ( GSVector4i ( 0 ) ) ) {
2016-03-26 10:27:31 +00:00
GSOffset * off = m_context - > offset . fb ;
GSVector4i r = GSVector4i ( m_vt . m_min . p . xyxy ( m_vt . m_max . p ) ) . rintersect ( GSVector4i ( m_context - > scissor . in ) ) ;
2016-08-03 09:20:56 +00:00
// Limit the hack to a single fullscreen clear. Some games might use severals column to clear a screen
// but hopefully it will be enough.
if ( r . width ( ) < = 128 | | r . height ( ) < = 128 )
return ;
2016-03-26 10:27:31 +00:00
2016-08-03 09:20:56 +00:00
GL_INS ( " OI_GsMemClear (%d,%d => %d,%d) " , r . x , r . y , r . z , r . w ) ;
2016-03-28 08:43:34 +00:00
int format = GSLocalMemory : : m_psm [ m_context - > FRAME . PSM ] . fmt ;
2016-04-28 15:13:14 +00:00
// FIXME: loop can likely be optimized with AVX/SSE. Pixels aren't
// linear but the value will be done for all pixels of a block.
2016-05-23 17:23:04 +00:00
// FIXME: maybe we could limit the write to the top and bottom row page.
2016-03-28 08:43:34 +00:00
if ( format = = 0 ) {
// Based on WritePixel32
for ( int y = r . top ; y < r . bottom ; y + + )
{
uint32 * RESTRICT d = & m_mem . m_vm32 [ off - > pixel . row [ y ] ] ;
int * RESTRICT col = off - > pixel . col [ 0 ] ;
for ( int x = r . left ; x < r . right ; x + + )
{
d [ col [ x ] ] = 0 ; // Here the constant color
}
}
} else if ( format = = 1 ) {
// Based on WritePixel24
for ( int y = r . top ; y < r . bottom ; y + + )
{
uint32 * RESTRICT d = & m_mem . m_vm32 [ off - > pixel . row [ y ] ] ;
int * RESTRICT col = off - > pixel . col [ 0 ] ;
2016-03-19 12:35:23 +00:00
2016-03-28 08:43:34 +00:00
for ( int x = r . left ; x < r . right ; x + + )
{
d [ col [ x ] ] & = 0xff000000 ; // Clear the color
}
}
} else if ( format = = 2 ) {
; // Hack is used for FMV which are likely 24/32 bits. Let's keep the for reference
#if 0
// Based on WritePixel16
for ( int y = r . top ; y < r . bottom ; y + + )
2016-03-26 10:27:31 +00:00
{
2016-03-28 08:43:34 +00:00
uint32 * RESTRICT d = & m_mem . m_vm16 [ off - > pixel . row [ y ] ] ;
int * RESTRICT col = off - > pixel . col [ 0 ] ;
for ( int x = r . left ; x < r . right ; x + + )
{
d [ col [ x ] ] = 0 ; // Here the constant color
}
2016-03-19 12:35:23 +00:00
}
2016-03-28 08:43:34 +00:00
# endif
2016-03-19 12:35:23 +00:00
}
}
}
2016-03-19 10:43:27 +00:00
bool GSRendererHW : : OI_BlitFMV ( GSTextureCache : : Target * _rt , GSTextureCache : : Source * tex , const GSVector4i & r_draw )
{
2017-01-02 20:14:54 +00:00
if ( r_draw . w > 1024 & & ( m_vt . m_primclass = = GS_SPRITE_CLASS ) & & ( m_vertex . next = = 2 ) & & PRIM - > TME & & ! PRIM - > ABE & & tex & & ! tex - > m_target & & m_context - > TEX0 . TBW > 0 ) {
2016-03-19 10:43:27 +00:00
GL_PUSH ( " OI_BlitFMV " ) ;
GL_INS ( " OI_BlitFMV " ) ;
// The draw is done past the RT at the location of the texture. To avoid various upscaling mess
// We will blit the data from the top to the bottom of the texture manually.
// Expected memory representation
// -----------------------------------------------------------------
// RT (2 half frame)
// -----------------------------------------------------------------
// Top of Texture (full height frame)
//
// Bottom of Texture (half height frame, will be the copy of Top texture after the draw)
// -----------------------------------------------------------------
// sRect is the top of texture
int tw = ( int ) ( 1 < < m_context - > TEX0 . TW ) ;
int th = ( int ) ( 1 < < m_context - > TEX0 . TH ) ;
GSVector4 sRect ;
sRect . x = m_vt . m_min . t . x / tw ;
sRect . y = m_vt . m_min . t . y / th ;
sRect . z = m_vt . m_max . t . x / tw ;
sRect . w = m_vt . m_max . t . y / th ;
// Compute the Bottom of texture rectangle
ASSERT ( m_context - > TEX0 . TBP0 > m_context - > FRAME . Block ( ) ) ;
int offset = ( m_context - > TEX0 . TBP0 - m_context - > FRAME . Block ( ) ) / m_context - > TEX0 . TBW ;
GSVector4i r_texture ( r_draw ) ;
r_texture . y - = offset ;
r_texture . w - = offset ;
GSVector4 dRect ( r_texture ) ;
// Do the blit. With a Copy mess to avoid issue with limited API (dx)
// m_dev->StretchRect(tex->m_texture, sRect, tex->m_texture, dRect);
GSVector4i r_full ( 0 , 0 , tw , th ) ;
if ( GSTexture * rt = m_dev - > CreateRenderTarget ( tw , th , false ) ) {
m_dev - > CopyRect ( tex - > m_texture , rt , r_full ) ;
m_dev - > StretchRect ( tex - > m_texture , sRect , rt , dRect ) ;
m_dev - > CopyRect ( rt , tex - > m_texture , r_full ) ;
m_dev - > Recycle ( rt ) ;
}
// Copy back the texture into the GS mem. I don't know why but it will be
// reuploaded again later
m_tc - > Read ( tex , r_texture ) ;
m_tc - > InvalidateVideoMemSubTarget ( _rt ) ;
return false ; // skip current draw
}
// Nothing to see keep going
return true ;
}
2015-06-04 16:52:58 +00:00
// OI (others input?/implementation?) hacks replace current draw call
2012-01-05 02:40:24 +00:00
bool GSRendererHW : : OI_FFXII ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
static uint32 * video = NULL ;
static size_t lines = 0 ;
if ( lines = = 0 )
{
2012-01-19 04:53:36 +00:00
if ( m_vt . m_primclass = = GS_LINE_CLASS & & ( m_vertex . next = = 448 * 2 | | m_vertex . next = = 512 * 2 ) )
2012-01-05 02:40:24 +00:00
{
2012-01-06 00:17:52 +00:00
lines = m_vertex . next / 2 ;
2012-01-05 02:40:24 +00:00
}
}
else
{
2012-01-19 04:53:36 +00:00
if ( m_vt . m_primclass = = GS_POINT_CLASS )
2012-01-05 02:40:24 +00:00
{
2012-01-06 00:17:52 +00:00
if ( m_vertex . next > = 16 * 512 )
2012-01-05 02:40:24 +00:00
{
// incoming pixels are stored in columns, one column is 16x512, total res 448x512 or 448x454
if ( ! video ) video = new uint32 [ 512 * 512 ] ;
2012-03-23 21:00:22 +00:00
int ox = m_context - > XYOFFSET . OFX - 8 ;
int oy = m_context - > XYOFFSET . OFY - 8 ;
2012-01-05 02:40:24 +00:00
2012-01-19 04:53:36 +00:00
const GSVertex * RESTRICT v = m_vertex . buff ;
2012-01-05 02:40:24 +00:00
2012-03-23 21:00:22 +00:00
for ( int i = ( int ) m_vertex . next ; i > 0 ; i - - , v + + )
2012-01-05 02:40:24 +00:00
{
2012-01-19 04:53:36 +00:00
int x = ( v - > XYZ . X - ox ) > > 4 ;
int y = ( v - > XYZ . Y - oy ) > > 4 ;
2016-03-19 10:43:27 +00:00
2012-03-23 21:00:22 +00:00
if ( x < 0 | | x > = 448 | | y < 0 | | y > = ( int ) lines ) return false ; // le sigh
2016-03-19 10:43:27 +00:00
2012-01-19 04:53:36 +00:00
video [ ( y < < 8 ) + ( y < < 7 ) + ( y < < 6 ) + x ] = v - > RGBAQ . u32 [ 0 ] ;
2012-01-05 02:40:24 +00:00
}
return false ;
}
else
{
lines = 0 ;
}
}
2012-01-19 04:53:36 +00:00
else if ( m_vt . m_primclass = = GS_LINE_CLASS )
2012-01-05 02:40:24 +00:00
{
2012-01-06 00:17:52 +00:00
if ( m_vertex . next = = lines * 2 )
2012-01-05 02:40:24 +00:00
{
// normally, this step would copy the video onto screen with 512 texture mapped horizontal lines,
// but we use the stored video data to create a new texture, and replace the lines with two triangles
m_dev - > Recycle ( t - > m_texture ) ;
t - > m_texture = m_dev - > CreateTexture ( 512 , 512 ) ;
t - > m_texture - > Update ( GSVector4i ( 0 , 0 , 448 , lines ) , video , 448 * 4 ) ;
2012-01-19 04:53:36 +00:00
m_vertex . buff [ 2 ] = m_vertex . buff [ m_vertex . next - 2 ] ;
m_vertex . buff [ 3 ] = m_vertex . buff [ m_vertex . next - 1 ] ;
2012-01-05 02:40:24 +00:00
m_index . buff [ 0 ] = 0 ;
m_index . buff [ 1 ] = 1 ;
m_index . buff [ 2 ] = 2 ;
m_index . buff [ 3 ] = 1 ;
m_index . buff [ 4 ] = 2 ;
m_index . buff [ 5 ] = 3 ;
2012-01-06 00:17:52 +00:00
m_vertex . head = m_vertex . tail = m_vertex . next = 4 ;
2012-01-05 02:40:24 +00:00
m_index . tail = 6 ;
2016-11-11 22:37:13 +00:00
m_vt . Update ( m_vertex . buff , m_index . buff , m_vertex . tail , m_index . tail , GS_TRIANGLE_CLASS ) ;
2012-01-05 02:40:24 +00:00
}
else
{
lines = 0 ;
}
}
}
return true ;
}
bool GSRendererHW : : OI_FFX ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 ZBP = m_context - > ZBUF . Block ( ) ;
uint32 TBP = m_context - > TEX0 . TBP0 ;
if ( ( FBP = = 0x00d00 | | FBP = = 0x00000 ) & & ZBP = = 0x02100 & & PRIM - > TME & & TBP = = 0x01a00 & & m_context - > TEX0 . PSM = = PSM_PSMCT16S )
{
// random battle transition (z buffer written directly, clear it now)
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( ds ) ;
2012-01-05 02:40:24 +00:00
}
return true ;
}
bool GSRendererHW : : OI_MetalSlug6 ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
// missing red channel fix (looks alright in pcsx2 r5000+)
2012-01-19 04:53:36 +00:00
GSVertex * RESTRICT v = m_vertex . buff ;
2012-01-05 02:40:24 +00:00
2012-03-23 21:00:22 +00:00
for ( int i = ( int ) m_vertex . next ; i > 0 ; i - - , v + + )
2012-01-05 02:40:24 +00:00
{
2012-01-19 04:53:36 +00:00
uint32 c = v - > RGBAQ . u32 [ 0 ] ;
2012-01-05 02:40:24 +00:00
uint32 r = ( c > > 0 ) & 0xff ;
uint32 g = ( c > > 8 ) & 0xff ;
uint32 b = ( c > > 16 ) & 0xff ;
if ( r = = 0 & & g ! = 0 & & b ! = 0 )
{
2012-01-19 04:53:36 +00:00
v - > RGBAQ . u32 [ 0 ] = ( c & 0xffffff00 ) | ( ( g + b + 1 ) > > 1 ) ;
2012-01-05 02:40:24 +00:00
}
}
2016-11-11 22:37:13 +00:00
m_vt . Update ( m_vertex . buff , m_index . buff , m_vertex . tail , m_index . tail , m_vt . m_primclass ) ;
2016-03-19 10:43:27 +00:00
2012-01-05 02:40:24 +00:00
return true ;
}
bool GSRendererHW : : OI_GodOfWar2 ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 FBW = m_context - > FRAME . FBW ;
uint32 FPSM = m_context - > FRAME . PSM ;
if ( ( FBP = = 0x00f00 | | FBP = = 0x00100 | | FBP = = 0x01280 ) & & FPSM = = PSM_PSMZ24 ) // ntsc 0xf00, pal 0x100, ntsc "HD" 0x1280
{
// z buffer clear
GIFRegTEX0 TEX0 ;
TEX0 . TBP0 = FBP ;
TEX0 . TBW = FBW ;
TEX0 . PSM = FPSM ;
2016-04-14 10:00:58 +00:00
if ( GSTextureCache : : Target * tmp_ds = m_tc - > LookupTarget ( TEX0 , m_width , m_height , GSTextureCache : : DepthStencil , true ) )
2012-01-05 02:40:24 +00:00
{
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( tmp_ds - > m_texture ) ;
2012-01-05 02:40:24 +00:00
}
return false ;
}
return true ;
}
bool GSRendererHW : : OI_RozenMaidenGebetGarden ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
if ( ! PRIM - > TME )
{
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 ZBP = m_context - > ZBUF . Block ( ) ;
if ( FBP = = 0x008c0 & & ZBP = = 0x01a40 )
{
// frame buffer clear, atst = fail, afail = write z only, z buffer points to frame buffer
GIFRegTEX0 TEX0 ;
TEX0 . TBP0 = ZBP ;
TEX0 . TBW = m_context - > FRAME . FBW ;
TEX0 . PSM = m_context - > FRAME . PSM ;
2016-04-14 10:00:58 +00:00
if ( GSTextureCache : : Target * tmp_rt = m_tc - > LookupTarget ( TEX0 , m_width , m_height , GSTextureCache : : RenderTarget , true ) )
2012-01-05 02:40:24 +00:00
{
2016-04-14 10:00:58 +00:00
m_dev - > ClearRenderTarget ( tmp_rt - > m_texture , 0 ) ;
2012-01-05 02:40:24 +00:00
}
return false ;
}
else if ( FBP = = 0x00000 & & m_context - > ZBUF . Block ( ) = = 0x01180 )
{
// z buffer clear, frame buffer now points to the z buffer (how can they be so clever?)
GIFRegTEX0 TEX0 ;
TEX0 . TBP0 = FBP ;
TEX0 . TBW = m_context - > FRAME . FBW ;
TEX0 . PSM = m_context - > ZBUF . PSM ;
2016-04-14 10:00:58 +00:00
if ( GSTextureCache : : Target * tmp_ds = m_tc - > LookupTarget ( TEX0 , m_width , m_height , GSTextureCache : : DepthStencil , true ) )
2012-01-05 02:40:24 +00:00
{
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( tmp_ds - > m_texture ) ;
2012-01-05 02:40:24 +00:00
}
return false ;
}
}
return true ;
}
bool GSRendererHW : : OI_StarWarsForceUnleashed ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 FPSM = m_context - > FRAME . PSM ;
if ( ! PRIM - > TME )
{
if ( FPSM = = PSM_PSMCT24 & & FBP = = 0x2bc0 )
{
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( ds ) ;
2012-01-05 02:40:24 +00:00
return false ;
}
}
else if ( PRIM - > TME )
{
2012-01-19 04:53:36 +00:00
if ( ( FBP = = 0x0 | | FBP = = 0x01180 ) & & FPSM = = PSM_PSMCT32 & & ( m_vt . m_eq . z & & m_vt . m_max . p . z = = 0 ) )
2012-01-05 02:40:24 +00:00
{
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( ds ) ;
2012-01-05 02:40:24 +00:00
}
}
return true ;
}
bool GSRendererHW : : OI_SpyroNewBeginning ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 FPSM = m_context - > FRAME . PSM ;
2016-09-20 17:12:43 +00:00
if ( PRIM - > TME )
2012-01-05 02:40:24 +00:00
{
2012-01-19 04:53:36 +00:00
if ( ( FBP = = 0x0 | | FBP = = 0x01180 ) & & FPSM = = PSM_PSMCT32 & & ( m_vt . m_eq . z & & m_vt . m_min . p . z = = 0 ) )
2012-01-05 02:40:24 +00:00
{
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( ds ) ;
2012-01-05 02:40:24 +00:00
}
}
return true ;
}
bool GSRendererHW : : OI_SpyroEternalNight ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 FPSM = m_context - > FRAME . PSM ;
2016-09-20 17:12:43 +00:00
if ( PRIM - > TME )
2012-01-05 02:40:24 +00:00
{
2012-01-19 04:53:36 +00:00
if ( ( FBP = = 0x0 | | FBP = = 0x01180 ) & & FPSM = = PSM_PSMCT32 & & ( m_vt . m_eq . z & & m_vt . m_min . p . z = = 0 ) )
2012-01-05 02:40:24 +00:00
{
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( ds ) ;
2012-01-05 02:40:24 +00:00
}
}
return true ;
}
bool GSRendererHW : : OI_TalesOfLegendia ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 FPSM = m_context - > FRAME . PSM ;
2012-01-19 04:53:36 +00:00
if ( FPSM = = PSM_PSMCT32 & & FBP = = 0x01c00 & & ! m_context - > TEST . ATE & & m_vt . m_eq . z )
2012-01-05 02:40:24 +00:00
{
m_context - > TEST . ZTST = ZTST_ALWAYS ;
2016-07-27 21:22:46 +00:00
//m_dev->ClearDepth(ds);
2012-01-05 02:40:24 +00:00
}
return true ;
}
2014-01-21 15:27:05 +00:00
bool GSRendererHW : : OI_SMTNocturne ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
uint32 FBMSK = m_context - > FRAME . FBMSK ;
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 FBW = m_context - > FRAME . FBW ;
uint32 FPSM = m_context - > FRAME . PSM ;
if ( FBMSK = = 16777215 & & m_vertex . head ! = 2 & & m_vertex . tail ! = 4 & & m_vertex . next ! = 4 )
{
GIFRegTEX0 TEX0 ;
TEX0 . TBP0 = FBP ;
TEX0 . TBW = FBW ;
TEX0 . PSM = FPSM ;
2016-04-14 10:00:58 +00:00
if ( GSTextureCache : : Target * tmp_ds = m_tc - > LookupTarget ( TEX0 , m_width , m_height , GSTextureCache : : DepthStencil , true ) )
2014-01-21 15:27:05 +00:00
{
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( tmp_ds - > m_texture ) ;
2014-01-21 15:27:05 +00:00
}
return false ;
}
return true ;
}
2012-01-05 02:40:24 +00:00
bool GSRendererHW : : OI_PointListPalette ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
2012-01-19 04:53:36 +00:00
if ( m_vt . m_primclass = = GS_POINT_CLASS & & ! PRIM - > TME )
2012-01-05 02:40:24 +00:00
{
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 FBW = m_context - > FRAME . FBW ;
if ( FBP > = 0x03f40 & & ( FBP & 0x1f ) = = 0 )
{
2012-01-06 00:17:52 +00:00
if ( m_vertex . next = = 16 )
2012-01-05 02:40:24 +00:00
{
2012-01-19 04:53:36 +00:00
GSVertex * RESTRICT v = m_vertex . buff ;
2012-01-05 02:40:24 +00:00
2012-01-19 04:53:36 +00:00
for ( int i = 0 ; i < 16 ; i + + , v + + )
2012-01-05 02:40:24 +00:00
{
2012-01-19 04:53:36 +00:00
uint32 c = v - > RGBAQ . u32 [ 0 ] ;
2012-01-05 02:40:24 +00:00
uint32 a = c > > 24 ;
c = ( a > = 0x80 ? 0xff000000 : ( a < < 25 ) ) | ( c & 0x00ffffff ) ;
2012-01-19 04:53:36 +00:00
v - > RGBAQ . u32 [ 0 ] = c ;
2012-01-05 02:40:24 +00:00
m_mem . WritePixel32 ( i & 7 , i > > 3 , c , FBP , FBW ) ;
}
m_mem . m_clut . Invalidate ( ) ;
return false ;
}
2012-01-06 00:17:52 +00:00
else if ( m_vertex . next = = 256 )
2012-01-05 02:40:24 +00:00
{
2012-01-19 04:53:36 +00:00
GSVertex * RESTRICT v = m_vertex . buff ;
2012-01-05 02:40:24 +00:00
2012-01-19 04:53:36 +00:00
for ( int i = 0 ; i < 256 ; i + + , v + + )
2012-01-05 02:40:24 +00:00
{
2012-01-19 04:53:36 +00:00
uint32 c = v - > RGBAQ . u32 [ 0 ] ;
2012-01-05 02:40:24 +00:00
uint32 a = c > > 24 ;
c = ( a > = 0x80 ? 0xff000000 : ( a < < 25 ) ) | ( c & 0x00ffffff ) ;
2012-01-19 04:53:36 +00:00
v - > RGBAQ . u32 [ 0 ] = c ;
2012-01-05 02:40:24 +00:00
m_mem . WritePixel32 ( i & 15 , i > > 4 , c , FBP , FBW ) ;
}
m_mem . m_clut . Invalidate ( ) ;
return false ;
}
else
{
ASSERT ( 0 ) ;
}
}
}
return true ;
}
2015-07-07 15:56:04 +00:00
bool GSRendererHW : : OI_SuperManReturns ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
2015-07-04 08:52:43 +00:00
{
// Instead to use a fullscreen rectangle they use a 32 pixels, 4096 pixels with a FBW of 1.
// Technically the FB wrap/overlap on itself...
GSDrawingContext * ctx = m_context ;
2016-04-28 21:29:52 +00:00
# ifndef NDEBUG
2015-07-04 08:52:43 +00:00
GSVertex * v = & m_vertex . buff [ 0 ] ;
2016-04-28 21:29:52 +00:00
# endif
2015-07-04 08:52:43 +00:00
2015-08-04 17:30:12 +00:00
if ( ! ( ctx - > FRAME . FBP = = ctx - > ZBUF . ZBP & & ! PRIM - > TME & & ! ctx - > ZBUF . ZMSK & & ! ctx - > FRAME . FBMSK & & m_vt . m_eq . rgba = = 0xFFFF ) )
2015-07-04 08:52:43 +00:00
return true ;
// Please kill those crazy devs!
ASSERT ( m_vertex . next = = 2 ) ;
ASSERT ( m_vt . m_primclass = = GS_SPRITE_CLASS ) ;
ASSERT ( ( v - > RGBAQ . A < < 24 | v - > RGBAQ . B < < 16 | v - > RGBAQ . G < < 8 | v - > RGBAQ . R ) = = ( int ) v - > XYZ . Z ) ;
// Do a direct write
2015-08-04 17:30:12 +00:00
m_dev - > ClearRenderTarget ( rt , GSVector4 ( m_vt . m_min . c ) ) ;
2015-07-04 08:52:43 +00:00
2015-08-04 17:30:12 +00:00
m_tc - > InvalidateVideoMemType ( GSTextureCache : : DepthStencil , ctx - > FRAME . Block ( ) ) ;
2015-07-04 08:52:43 +00:00
return false ;
}
2016-05-16 08:55:02 +00:00
bool GSRendererHW : : OI_ArTonelico2 ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
// world map clipping
//
// The bad draw call is a sprite rendering to clear the z buffer
/*
Depth buffer description
* width is 10 pages
* texture / scissor size is 640 x448
* depth is 16 bits so it writes 70 ( 10 w * 7 h ) pages of data .
following draw calls will use the buffer as 6 pages width with a scissor
test of 384 x672 . So the above texture can be seen as a
* texture width : 6 pages * 64 pixels / page = 384
* texture height : 70 / 6 pages * 64 pixels / page = 746
So as you can see the GS issue a write of 640 x448 but actually it
expects to clean a 384 x746 area . Ideally the fix will transform the
buffer to adapt the page width properly .
*/
GSVertex * v = & m_vertex . buff [ 0 ] ;
if ( m_vertex . next = = 2 & & ! PRIM - > TME & & m_context - > FRAME . FBW = = 10 & & v - > XYZ . Z = = 0 & & m_context - > TEST . ZTST = = ZTST_ALWAYS ) {
GL_INS ( " OI_ArTonelico2 " ) ;
2016-07-27 21:22:46 +00:00
m_dev - > ClearDepth ( ds ) ;
2016-05-16 08:55:02 +00:00
}
return true ;
}
2015-07-04 08:52:43 +00:00
2016-09-04 17:41:31 +00:00
bool GSRendererHW : : OI_ItadakiStreet ( GSTexture * rt , GSTexture * ds , GSTextureCache : : Source * t )
{
if ( m_context - > TEST . ATST = = ATST_NOTEQUAL & & m_context - > TEST . AREF = = 0 ) {
// It is also broken on the SW renderer. Issue appears because fragment alpha is 0
// I suspect the game expect low value of alpha, and due to bad rounding on the core
// you have wrongly 0.
// Otherwise some draws calls are empty (all pixels are discarded).
// It fixes missing element on the board
GL_INS ( " OI_ItadakiStreetSpecial disable alpha test " ) ;
m_context - > TEST . ATST = ATST_ALWAYS ;
#if 0 // Not enough
uint32 dummy_fm ;
uint32 dummy_zm ;
if ( ! TryAlphaTest ( dummy_fm , dummy_zm ) ) {
GL_INS ( " OI_ItadakiStreetSpecial disable alpha test " ) ;
m_context - > TEST . ATST = ATST_ALWAYS ;
}
# endif
}
return true ;
}
2015-06-04 16:52:58 +00:00
// OO (others output?) hacks: invalidate extra local memory after the draw call
2012-01-05 02:40:24 +00:00
void GSRendererHW : : OO_DBZBT2 ( )
{
// palette readback (cannot detect yet, when fetching the texture later)
uint32 FBP = m_context - > FRAME . Block ( ) ;
uint32 TBP0 = m_context - > TEX0 . TBP0 ;
if ( PRIM - > TME & & ( FBP = = 0x03c00 & & TBP0 = = 0x03c80 | | FBP = = 0x03ac0 & & TBP0 = = 0x03b40 ) )
{
GIFRegBITBLTBUF BITBLTBUF ;
BITBLTBUF . SBP = FBP ;
BITBLTBUF . SBW = 1 ;
BITBLTBUF . SPSM = PSM_PSMCT32 ;
InvalidateLocalMem ( BITBLTBUF , GSVector4i ( 0 , 0 , 64 , 64 ) ) ;
}
}
void GSRendererHW : : OO_MajokkoALaMode2 ( )
{
// palette readback
uint32 FBP = m_context - > FRAME . Block ( ) ;
if ( ! PRIM - > TME & & FBP = = 0x03f40 )
{
GIFRegBITBLTBUF BITBLTBUF ;
BITBLTBUF . SBP = FBP ;
BITBLTBUF . SBW = 1 ;
BITBLTBUF . SPSM = PSM_PSMCT32 ;
InvalidateLocalMem ( BITBLTBUF , GSVector4i ( 0 , 0 , 16 , 16 ) ) ;
}
}
2016-09-30 17:22:16 +00:00
void GSRendererHW : : OO_Jak ( )
{
// FIXME might need a CU_Jak too
GSVector4i r = GSVector4i ( m_vt . m_min . p . xyxy ( m_vt . m_max . p ) ) . rintersect ( GSVector4i ( m_context - > scissor . in ) ) ;
GSVector4i r_p = GSVector4i ( 0 , 0 , 16 , 16 ) ;
if ( ! PRIM - > FST & & PRIM - > TME & & ( r = = r_p ) . alltrue ( ) & & m_context - > TEX0 . TW = = 4 & & m_context - > TEX0 . TH = = 4 & & m_context - > TEX0 . PSM = = PSM_PSMCT32 ) {
// Game will render a texture directly into a palette.
uint32 FBP = m_context - > FRAME . Block ( ) ;
GL_INS ( " OO_Jak read back 0x%x " , FBP ) ;
GIFRegBITBLTBUF BITBLTBUF ;
BITBLTBUF . SBP = FBP ;
BITBLTBUF . SBW = 1 ;
BITBLTBUF . SPSM = PSM_PSMCT32 ;
InvalidateLocalMem ( BITBLTBUF , GSVector4i ( 0 , 0 , 16 , 16 ) ) ;
}
}
2015-06-04 16:52:58 +00:00
// Can Upscale hacks: disable upscaling for some draw calls
2012-01-05 02:40:24 +00:00
bool GSRendererHW : : CU_DBZBT2 ( )
{
// palette should stay 64 x 64
uint32 FBP = m_context - > FRAME . Block ( ) ;
return FBP ! = 0x03c00 & & FBP ! = 0x03ac0 ;
}
bool GSRendererHW : : CU_MajokkoALaMode2 ( )
{
// palette should stay 16 x 16
uint32 FBP = m_context - > FRAME . Block ( ) ;
return FBP ! = 0x03f40 ;
}
bool GSRendererHW : : CU_TalesOfAbyss ( )
{
// full image blur and brightening
uint32 FBP = m_context - > FRAME . Block ( ) ;
return FBP ! = 0x036e0 & & FBP ! = 0x03560 & & FBP ! = 0x038e0 ;
}