2015-05-24 04:32:32 +00:00
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
2015-12-21 02:49:49 +00:00
# include <memory>
2016-01-17 21:54:31 +00:00
# include "Common/BitSet.h"
# include "Common/ChunkFile.h"
2014-09-08 01:06:58 +00:00
# include "Common/CommonTypes.h"
2016-01-17 21:54:31 +00:00
# include "Common/Logging/Log.h"
2014-02-17 10:18:15 +00:00
2016-01-17 21:54:31 +00:00
# include "VideoCommon/BPMemory.h"
# include "VideoCommon/DataReader.h"
2014-02-17 10:18:15 +00:00
# include "VideoCommon/Debugger.h"
2014-12-14 20:23:13 +00:00
# include "VideoCommon/GeometryShaderManager.h"
2014-02-17 10:18:15 +00:00
# include "VideoCommon/IndexGenerator.h"
# include "VideoCommon/NativeVertexFormat.h"
# include "VideoCommon/OpcodeDecoding.h"
# include "VideoCommon/PerfQueryBase.h"
# include "VideoCommon/PixelShaderManager.h"
# include "VideoCommon/RenderBase.h"
# include "VideoCommon/TextureCacheBase.h"
2015-01-23 14:15:09 +00:00
# include "VideoCommon/VertexLoaderManager.h"
2014-02-17 10:18:15 +00:00
# include "VideoCommon/VertexManagerBase.h"
# include "VideoCommon/VertexShaderManager.h"
# include "VideoCommon/VideoConfig.h"
# include "VideoCommon/XFMemory.h"
2010-10-03 08:20:24 +00:00
2015-12-21 02:49:49 +00:00
std : : unique_ptr < VertexManagerBase > g_vertex_manager ;
2010-10-03 08:20:24 +00:00
2015-11-01 21:54:41 +00:00
u8 * VertexManagerBase : : s_pCurBufferPointer ;
u8 * VertexManagerBase : : s_pBaseBufferPointer ;
u8 * VertexManagerBase : : s_pEndBufferPointer ;
2010-10-03 08:20:24 +00:00
2015-11-01 21:54:41 +00:00
PrimitiveType VertexManagerBase : : current_primitive_type ;
2014-01-15 20:44:46 +00:00
2015-11-01 21:54:41 +00:00
Slope VertexManagerBase : : s_zslope ;
2015-01-13 09:55:25 +00:00
2015-11-01 21:54:41 +00:00
bool VertexManagerBase : : s_is_flushed ;
bool VertexManagerBase : : s_cull_all ;
2014-01-23 12:11:38 +00:00
2014-01-15 20:44:46 +00:00
static const PrimitiveType primitive_from_gx [ 8 ] = {
PRIMITIVE_TRIANGLES , // GX_DRAW_QUADS
2014-05-08 23:53:18 +00:00
PRIMITIVE_TRIANGLES , // GX_DRAW_QUADS_2
2014-01-15 20:44:46 +00:00
PRIMITIVE_TRIANGLES , // GX_DRAW_TRIANGLES
PRIMITIVE_TRIANGLES , // GX_DRAW_TRIANGLE_STRIP
PRIMITIVE_TRIANGLES , // GX_DRAW_TRIANGLE_FAN
PRIMITIVE_LINES , // GX_DRAW_LINES
PRIMITIVE_LINES , // GX_DRAW_LINE_STRIP
PRIMITIVE_POINTS , // GX_DRAW_POINTS
} ;
2015-11-01 21:54:41 +00:00
VertexManagerBase : : VertexManagerBase ( )
2010-10-03 08:20:24 +00:00
{
2015-01-24 20:12:52 +00:00
s_is_flushed = true ;
s_cull_all = false ;
2010-10-03 08:20:24 +00:00
}
2015-11-01 21:54:41 +00:00
VertexManagerBase : : ~ VertexManagerBase ( )
2013-04-24 13:21:54 +00:00
{
}
2010-10-03 08:20:24 +00:00
2015-11-01 21:54:41 +00:00
u32 VertexManagerBase : : GetRemainingSize ( )
2010-10-03 08:20:24 +00:00
{
2013-02-27 04:47:50 +00:00
return ( u32 ) ( s_pEndBufferPointer - s_pCurBufferPointer ) ;
}
2015-11-01 21:54:41 +00:00
DataReader VertexManagerBase : : PrepareForAdditionalData ( int primitive , u32 count , u32 stride , bool cullall )
2013-10-29 05:23:17 +00:00
{
2014-11-11 09:48:38 +00:00
// The SSE vertex loader can write up to 4 bytes past the end
u32 const needed_vertex_bytes = count * stride + 4 ;
2013-10-29 05:23:17 +00:00
2014-01-15 20:44:46 +00:00
// We can't merge different kinds of primitives, so we have to flush here
if ( current_primitive_type ! = primitive_from_gx [ primitive ] )
Flush ( ) ;
current_primitive_type = primitive_from_gx [ primitive ] ;
// Check for size in buffer, if the buffer gets full, call Flush()
2015-01-24 20:12:52 +00:00
if ( ! s_is_flushed & & ( count > IndexGenerator : : GetRemainingIndices ( ) | |
count > GetRemainingIndices ( primitive ) | | needed_vertex_bytes > GetRemainingSize ( ) ) )
2013-02-27 04:47:50 +00:00
{
Flush ( ) ;
2013-10-29 05:23:17 +00:00
2014-03-10 11:30:55 +00:00
if ( count > IndexGenerator : : GetRemainingIndices ( ) )
2013-03-31 23:10:21 +00:00
ERROR_LOG ( VIDEO , " Too little remaining index values. Use 32-bit or reset them on flush. " ) ;
2013-03-06 11:33:02 +00:00
if ( count > GetRemainingIndices ( primitive ) )
ERROR_LOG ( VIDEO , " VertexManager: Buffer not large enough for all indices! "
2013-03-31 23:10:21 +00:00
" Increase MAXIBUFFERSIZE or we need primitive breaking after all. " ) ;
2013-03-22 23:18:35 +00:00
if ( needed_vertex_bytes > GetRemainingSize ( ) )
ERROR_LOG ( VIDEO , " VertexManager: Buffer not large enough for all vertices! "
2013-03-31 23:10:21 +00:00
" Increase MAXVBUFFERSIZE or we need primitive breaking after all. " ) ;
2013-02-27 04:47:50 +00:00
}
2010-10-03 08:20:24 +00:00
2015-01-24 20:12:52 +00:00
s_cull_all = cullall ;
2015-01-24 01:37:20 +00:00
2014-01-23 12:11:38 +00:00
// need to alloc new buffer
2015-01-24 20:12:52 +00:00
if ( s_is_flushed )
2014-01-23 12:11:38 +00:00
{
g_vertex_manager - > ResetBuffer ( stride ) ;
2015-01-24 20:12:52 +00:00
s_is_flushed = false ;
2014-01-23 12:11:38 +00:00
}
2014-12-09 07:35:04 +00:00
return DataReader ( s_pCurBufferPointer , s_pEndBufferPointer ) ;
}
2015-11-01 21:54:41 +00:00
void VertexManagerBase : : FlushData ( u32 count , u32 stride )
2014-12-09 07:35:04 +00:00
{
s_pCurBufferPointer + = count * stride ;
2013-02-22 07:41:52 +00:00
}
2015-11-01 21:54:41 +00:00
u32 VertexManagerBase : : GetRemainingIndices ( int primitive )
2010-10-03 08:20:24 +00:00
{
2014-01-15 20:44:46 +00:00
u32 index_len = MAXIBUFFERSIZE - IndexGenerator : : GetIndexLen ( ) ;
2013-10-29 05:23:17 +00:00
2014-03-10 11:30:55 +00:00
if ( g_Config . backend_info . bSupportsPrimitiveRestart )
2013-04-24 13:21:54 +00:00
{
2013-03-29 13:27:33 +00:00
switch ( primitive )
{
case GX_DRAW_QUADS :
2014-05-08 23:53:18 +00:00
case GX_DRAW_QUADS_2 :
2014-01-15 20:44:46 +00:00
return index_len / 5 * 4 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_TRIANGLES :
2014-01-15 20:44:46 +00:00
return index_len / 4 * 3 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_TRIANGLE_STRIP :
2014-01-15 20:44:46 +00:00
return index_len / 1 - 1 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_TRIANGLE_FAN :
2014-01-15 20:44:46 +00:00
return index_len / 6 * 4 + 1 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_LINES :
2014-01-15 20:44:46 +00:00
return index_len ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_LINE_STRIP :
2014-01-15 20:44:46 +00:00
return index_len / 2 + 1 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_POINTS :
2014-01-15 20:44:46 +00:00
return index_len ;
2013-03-29 13:27:33 +00:00
default :
return 0 ;
}
2013-04-24 13:21:54 +00:00
}
else
{
2013-03-29 13:27:33 +00:00
switch ( primitive )
{
case GX_DRAW_QUADS :
2014-05-08 23:53:18 +00:00
case GX_DRAW_QUADS_2 :
2014-01-15 20:44:46 +00:00
return index_len / 6 * 4 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_TRIANGLES :
2014-01-15 20:44:46 +00:00
return index_len ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_TRIANGLE_STRIP :
2014-01-15 20:44:46 +00:00
return index_len / 3 + 2 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_TRIANGLE_FAN :
2014-01-15 20:44:46 +00:00
return index_len / 3 + 2 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_LINES :
2014-01-15 20:44:46 +00:00
return index_len ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_LINE_STRIP :
2014-01-15 20:44:46 +00:00
return index_len / 2 + 1 ;
2013-03-29 13:27:33 +00:00
case GX_DRAW_POINTS :
2014-01-15 20:44:46 +00:00
return index_len ;
2013-03-29 13:27:33 +00:00
default :
return 0 ;
}
2013-10-29 05:23:17 +00:00
}
2010-10-03 08:20:24 +00:00
}
2015-11-01 21:54:41 +00:00
void VertexManagerBase : : Flush ( )
2010-10-03 08:20:24 +00:00
{
2015-01-24 20:12:52 +00:00
if ( s_is_flushed )
2014-09-08 01:06:58 +00:00
return ;
2013-03-20 01:51:12 +00:00
2012-12-21 20:04:53 +00:00
// loading a state will invalidate BP, so check for it
2012-12-23 12:32:23 +00:00
g_video_backend - > CheckInvalidState ( ) ;
2013-03-20 01:51:12 +00:00
2014-01-21 09:47:00 +00:00
# if defined(_DEBUG) || defined(DEBUGFAST)
2014-04-27 18:59:04 +00:00
PRIM_LOG ( " frame%d: \n texgen=%d, numchan=%d, dualtex=%d, ztex=%d, cole=%d, alpe=%d, ze=%d " , g_ActiveConfig . iSaveTargetId , xfmem . numTexGen . numTexGens ,
xfmem . numChan . numColorChans , xfmem . dualTexTrans . enabled , bpmem . ztex2 . op ,
2014-06-11 18:34:15 +00:00
( int ) bpmem . blendmode . colorupdate , ( int ) bpmem . blendmode . alphaupdate , ( int ) bpmem . zmode . updateenable ) ;
2014-01-21 09:47:00 +00:00
2014-04-27 18:59:04 +00:00
for ( unsigned int i = 0 ; i < xfmem . numChan . numColorChans ; + + i )
2014-01-21 09:47:00 +00:00
{
2014-04-27 18:59:04 +00:00
LitChannel * ch = & xfmem . color [ i ] ;
2014-01-21 09:47:00 +00:00
PRIM_LOG ( " colchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d " , i , ch - > matsource , ch - > GetFullLightMask ( ) , ch - > ambsource , ch - > diffusefunc , ch - > attnfunc ) ;
2014-04-27 18:59:04 +00:00
ch = & xfmem . alpha [ i ] ;
2014-01-21 09:47:00 +00:00
PRIM_LOG ( " alpchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d " , i , ch - > matsource , ch - > GetFullLightMask ( ) , ch - > ambsource , ch - > diffusefunc , ch - > attnfunc ) ;
}
2014-04-27 18:59:04 +00:00
for ( unsigned int i = 0 ; i < xfmem . numTexGen . numTexGens ; + + i )
2014-01-21 09:47:00 +00:00
{
2014-04-27 18:59:04 +00:00
TexMtxInfo tinfo = xfmem . texMtxInfo [ i ] ;
2014-01-21 09:47:00 +00:00
if ( tinfo . texgentype ! = XF_TEXGEN_EMBOSS_MAP ) tinfo . hex & = 0x7ff ;
if ( tinfo . texgentype ! = XF_TEXGEN_REGULAR ) tinfo . projection = 0 ;
PRIM_LOG ( " txgen%d: proj=%d, input=%d, gentype=%d, srcrow=%d, embsrc=%d, emblght=%d, postmtx=%d, postnorm=%d " ,
i , tinfo . projection , tinfo . inputform , tinfo . texgentype , tinfo . sourcerow , tinfo . embosssourceshift , tinfo . embosslightshift ,
2014-04-27 18:59:04 +00:00
xfmem . postMtxInfo [ i ] . index , xfmem . postMtxInfo [ i ] . normalize ) ;
2014-01-21 09:47:00 +00:00
}
2014-06-11 18:34:15 +00:00
PRIM_LOG ( " pixel: tev=%d, ind=%d, texgen=%d, dstalpha=%d, alphatest=0x%x " , ( int ) bpmem . genMode . numtevstages + 1 , ( int ) bpmem . genMode . numindstages ,
( int ) bpmem . genMode . numtexgens , ( u32 ) bpmem . dstalpha . enable , ( bpmem . alpha_test . hex > > 16 ) & 0xff ) ;
2014-01-21 09:47:00 +00:00
# endif
2015-01-24 01:37:20 +00:00
// If the primitave is marked CullAll. All we need to do is update the vertex constants and calculate the zfreeze refrence slope
2015-01-24 20:12:52 +00:00
if ( ! s_cull_all )
2014-01-21 09:47:00 +00:00
{
2015-01-24 01:37:20 +00:00
BitSet32 usedtextures ;
for ( u32 i = 0 ; i < bpmem . genMode . numtevstages + 1u ; + + i )
if ( bpmem . tevorders [ i / 2 ] . getEnable ( i & 1 ) )
usedtextures [ bpmem . tevorders [ i / 2 ] . getTexMap ( i & 1 ) ] = true ;
if ( bpmem . genMode . numindstages > 0 )
for ( unsigned int i = 0 ; i < bpmem . genMode . numtevstages + 1u ; + + i )
if ( bpmem . tevind [ i ] . IsActive ( ) & & bpmem . tevind [ i ] . bt < bpmem . genMode . numindstages )
usedtextures [ bpmem . tevindref . getTexMap ( bpmem . tevind [ i ] . bt ) ] = true ;
2014-10-22 00:42:55 +00:00
2015-11-06 00:28:05 +00:00
TextureCacheBase : : UnbindTextures ( ) ;
2015-01-24 01:37:20 +00:00
for ( unsigned int i : usedtextures )
2014-01-21 09:47:00 +00:00
{
2015-11-06 00:28:05 +00:00
const TextureCacheBase : : TCacheEntryBase * tentry = TextureCacheBase : : Load ( i ) ;
2015-01-24 01:37:20 +00:00
if ( tentry )
{
2015-03-01 12:04:48 +00:00
g_renderer - > SetSamplerState ( i & 3 , i > > 2 , tentry - > is_custom_tex ) ;
PixelShaderManager : : SetTexDims ( i , tentry - > native_width , tentry - > native_height ) ;
2015-01-24 01:37:20 +00:00
}
else
2015-01-24 20:12:52 +00:00
{
2015-01-24 01:37:20 +00:00
ERROR_LOG ( VIDEO , " error loading texture " ) ;
2015-01-24 20:12:52 +00:00
}
2014-01-21 09:47:00 +00:00
}
2016-01-08 03:38:00 +00:00
g_texture_cache - > BindTextures ( ) ;
2014-01-21 09:47:00 +00:00
}
2015-01-24 01:37:20 +00:00
// set global vertex constants
2014-01-21 09:47:00 +00:00
VertexShaderManager : : SetConstants ( ) ;
2015-01-23 14:15:09 +00:00
// Calculate ZSlope for zfreeze
if ( ! bpmem . genMode . zfreeze )
{
// Must be done after VertexShaderManager::SetConstants()
CalculateZSlope ( VertexLoaderManager : : GetCurrentVertexFormat ( ) ) ;
}
2015-01-24 20:12:52 +00:00
else if ( s_zslope . dirty & & ! s_cull_all ) // or apply any dirty ZSlopes
2015-01-23 14:15:09 +00:00
{
2015-01-24 20:12:52 +00:00
PixelShaderManager : : SetZSlope ( s_zslope . dfdx , s_zslope . dfdy , s_zslope . f0 ) ;
s_zslope . dirty = false ;
2015-01-23 14:15:09 +00:00
}
2015-01-24 20:12:52 +00:00
if ( ! s_cull_all )
2015-01-23 14:15:09 +00:00
{
2015-01-24 01:37:20 +00:00
// set the rest of the global constants
GeometryShaderManager : : SetConstants ( ) ;
PixelShaderManager : : SetConstants ( ) ;
2015-09-09 19:20:46 +00:00
bool useDstAlpha = bpmem . dstalpha . enable & &
2015-11-02 10:42:22 +00:00
bpmem . blendmode . alphaupdate & &
bpmem . zcontrol . pixel_format = = PEControl : : RGBA6_Z24 ;
2015-01-24 01:37:20 +00:00
if ( PerfQueryBase : : ShouldEmulate ( ) )
g_perf_query - > EnableQuery ( bpmem . zcontrol . early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP ) ;
g_vertex_manager - > vFlush ( useDstAlpha ) ;
if ( PerfQueryBase : : ShouldEmulate ( ) )
g_perf_query - > DisableQuery ( bpmem . zcontrol . early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP ) ;
2015-01-23 14:15:09 +00:00
}
2014-02-03 15:56:17 +00:00
GFX_DEBUGGER_PAUSE_AT ( NEXT_FLUSH , true ) ;
2013-03-20 01:51:12 +00:00
2014-09-24 00:46:09 +00:00
if ( xfmem . numTexGen . numTexGens ! = bpmem . genMode . numtexgens )
ERROR_LOG ( VIDEO , " xf.numtexgens (%d) does not match bp.numtexgens (%d). Error in command stream. " , xfmem . numTexGen . numTexGens , bpmem . genMode . numtexgens . Value ( ) ) ;
2015-01-24 20:12:52 +00:00
s_is_flushed = true ;
s_cull_all = false ;
2010-10-03 08:20:24 +00:00
}
2010-10-19 22:24:27 +00:00
2015-11-01 21:54:41 +00:00
void VertexManagerBase : : DoState ( PointerWrap & p )
2012-01-04 08:42:22 +00:00
{
2015-01-24 20:12:52 +00:00
p . Do ( s_zslope ) ;
2012-01-04 08:42:22 +00:00
g_vertex_manager - > vDoState ( p ) ;
}
2014-12-26 08:25:24 +00:00
2015-11-01 21:54:41 +00:00
void VertexManagerBase : : CalculateZSlope ( NativeVertexFormat * format )
2014-12-26 08:25:24 +00:00
{
float out [ 12 ] ;
2015-01-13 09:55:25 +00:00
float viewOffset [ 2 ] = { xfmem . viewport . xOrig - bpmem . scissorOffset . x * 2 ,
2015-01-22 15:38:36 +00:00
xfmem . viewport . yOrig - bpmem . scissorOffset . y * 2 } ;
2014-12-26 08:25:24 +00:00
2015-05-29 11:43:12 +00:00
if ( current_primitive_type ! = PRIMITIVE_TRIANGLES )
return ;
2015-01-23 14:15:09 +00:00
// Global matrix ID.
u32 mtxIdx = g_main_cp_state . matrix_index_a . PosNormalMtxIdx ;
2015-01-24 01:37:20 +00:00
const PortableVertexDeclaration vert_decl = format - > GetVertexDeclaration ( ) ;
2015-01-23 14:15:09 +00:00
2015-05-29 11:43:12 +00:00
// Make sure the buffer contains at least 3 vertices.
2015-01-25 07:31:20 +00:00
if ( ( s_pCurBufferPointer - s_pBaseBufferPointer ) < ( vert_decl . stride * 3 ) )
return ;
2014-12-26 08:25:24 +00:00
// Lookup vertices of the last rendered triangle and software-transform them
2015-06-01 17:58:27 +00:00
// This allows us to determine the depth slope, which will be used if z-freeze
2014-12-26 08:25:24 +00:00
// is enabled in the following flush.
for ( unsigned int i = 0 ; i < 3 ; + + i )
{
2015-01-23 14:15:09 +00:00
// If this vertex format has per-vertex position matrix IDs, look it up.
2015-05-29 11:43:12 +00:00
if ( vert_decl . posmtx . enable )
2015-06-01 17:58:27 +00:00
mtxIdx = VertexLoaderManager : : position_matrix_index [ 2 - i ] ;
if ( vert_decl . position . components = = 2 )
VertexLoaderManager : : position_cache [ 2 - i ] [ 2 ] = 0 ;
2014-12-26 08:25:24 +00:00
2015-06-01 17:58:27 +00:00
VertexShaderManager : : TransformToClipSpace ( & VertexLoaderManager : : position_cache [ 2 - i ] [ 0 ] , & out [ i * 4 ] , mtxIdx ) ;
2014-12-26 08:25:24 +00:00
2015-01-02 10:55:41 +00:00
// Transform to Screenspace
2015-01-15 15:01:00 +00:00
float inv_w = 1.0f / out [ 3 + i * 4 ] ;
2015-01-13 09:55:25 +00:00
2015-01-15 15:01:00 +00:00
out [ 0 + i * 4 ] = out [ 0 + i * 4 ] * inv_w * xfmem . viewport . wd + viewOffset [ 0 ] ;
out [ 1 + i * 4 ] = out [ 1 + i * 4 ] * inv_w * xfmem . viewport . ht + viewOffset [ 1 ] ;
out [ 2 + i * 4 ] = out [ 2 + i * 4 ] * inv_w * xfmem . viewport . zRange + xfmem . viewport . farZ ;
2014-12-26 08:25:24 +00:00
}
2015-01-02 10:55:41 +00:00
2014-12-26 08:25:24 +00:00
float dx31 = out [ 8 ] - out [ 0 ] ;
float dx12 = out [ 0 ] - out [ 4 ] ;
float dy12 = out [ 1 ] - out [ 5 ] ;
float dy31 = out [ 9 ] - out [ 1 ] ;
float DF31 = out [ 10 ] - out [ 2 ] ;
float DF21 = out [ 6 ] - out [ 2 ] ;
float a = DF31 * - dy12 - DF21 * dy31 ;
float b = dx31 * DF21 + dx12 * DF31 ;
float c = - dx12 * dy31 - dx31 * - dy12 ;
2015-01-23 14:15:09 +00:00
// Sometimes we process de-generate triangles. Stop any divide by zeros
2015-01-13 09:55:25 +00:00
if ( c = = 0 )
return ;
2014-12-26 08:25:24 +00:00
2015-01-24 20:12:52 +00:00
s_zslope . dfdx = - a / c ;
s_zslope . dfdy = - b / c ;
s_zslope . f0 = out [ 2 ] - ( out [ 0 ] * s_zslope . dfdx + out [ 1 ] * s_zslope . dfdy ) ;
s_zslope . dirty = true ;
2014-12-26 08:25:24 +00:00
}