2013-08-17 11:23:51 +02:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2013-11-15 14:24:25 +01:00
# include "base/logging.h"
2013-08-17 11:23:51 +02:00
# include "base/timeutil.h"
# include "Common/MemoryUtil.h"
# include "Core/MemMap.h"
# include "Core/Host.h"
# include "Core/System.h"
# include "Core/Reporting.h"
# include "Core/Config.h"
# include "Core/CoreTiming.h"
# include "helper/dx_state.h"
# include "GPU/Math3D.h"
# include "GPU/GPUState.h"
# include "GPU/ge_constants.h"
2014-03-25 00:21:04 -07:00
# include "GPU/Common/TextureDecoder.h"
2014-08-24 22:16:32 -07:00
# include "GPU/Common/SplineCommon.h"
2014-04-18 14:30:18 +02:00
# include "GPU/Common/TransformCommon.h"
2014-09-10 10:44:22 +02:00
# include "GPU/Common/VertexDecoderCommon.h"
2014-09-13 13:53:04 +02:00
# include "GPU/Common/SoftwareTransformCommon.h"
2013-09-15 12:46:14 +02:00
# include "GPU/Directx9/StateMappingDX9.h"
# include "GPU/Directx9/TextureCacheDX9.h"
2016-04-10 10:27:28 +02:00
# include "GPU/Directx9/DrawEngineDX9.h"
2013-09-15 12:46:14 +02:00
# include "GPU/Directx9/ShaderManagerDX9.h"
# include "GPU/Directx9/GPU_DX9.h"
2013-08-17 11:23:51 +02:00
2013-09-15 08:53:21 -07:00
namespace DX9 {
2013-08-17 11:23:51 +02:00
const D3DPRIMITIVETYPE glprim [ 8 ] = {
D3DPT_POINTLIST ,
D3DPT_LINELIST ,
D3DPT_LINESTRIP ,
D3DPT_TRIANGLELIST ,
D3DPT_TRIANGLESTRIP ,
D3DPT_TRIANGLEFAN ,
D3DPT_TRIANGLELIST , // With OpenGL ES we have to expand sprites into triangles, tripling the data instead of doubling. sigh. OpenGL ES, Y U NO SUPPORT GL_QUADS?
} ;
2013-09-15 12:46:14 +02:00
// hrydgard's quick guesses - TODO verify
static const int D3DPRIMITIVEVERTEXCOUNT [ 8 ] [ 2 ] = {
{ 0 , 0 } , // invalid
{ 1 , 0 } , // 1 = D3DPT_POINTLIST,
{ 2 , 0 } , // 2 = D3DPT_LINELIST,
{ 2 , 1 } , // 3 = D3DPT_LINESTRIP,
{ 3 , 0 } , // 4 = D3DPT_TRIANGLELIST,
{ 1 , 2 } , // 5 = D3DPT_TRIANGLESTRIP,
{ 1 , 2 } , // 6 = D3DPT_TRIANGLEFAN,
} ;
2013-08-17 11:23:51 +02:00
int D3DPrimCount ( D3DPRIMITIVETYPE prim , int size ) {
return ( size / D3DPRIMITIVEVERTEXCOUNT [ prim ] [ 0 ] ) - D3DPRIMITIVEVERTEXCOUNT [ prim ] [ 1 ] ;
}
enum {
2013-10-27 14:43:58 -07:00
TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof ( TransformedVertex )
2013-08-17 11:23:51 +02:00
} ;
2013-08-20 18:47:11 +02:00
# define VERTEXCACHE_DECIMATION_INTERVAL 17
2014-09-14 13:50:57 -07:00
enum { VAI_KILL_AGE = 120 , VAI_UNRELIABLE_KILL_AGE = 240 , VAI_UNRELIABLE_KILL_MAX = 4 } ;
2016-04-10 10:21:48 +02:00
DrawEngineDX9 : : DrawEngineDX9 ( )
2014-09-18 00:40:25 +02:00
: decodedVerts_ ( 0 ) ,
prevPrim_ ( GE_PRIM_INVALID ) ,
lastVType_ ( - 1 ) ,
shaderManager_ ( 0 ) ,
textureCache_ ( 0 ) ,
framebufferManager_ ( 0 ) ,
numDrawCalls ( 0 ) ,
vertexCountInDrawCalls ( 0 ) ,
decodeCounter_ ( 0 ) ,
dcid_ ( 0 ) ,
2014-09-21 12:01:49 -07:00
uvScale ( 0 ) ,
2015-03-17 22:07:09 -07:00
fboTexNeedBind_ ( false ) ,
2014-09-21 12:01:49 -07:00
fboTexBound_ ( false ) {
2014-09-10 10:28:44 +02:00
memset ( & decOptions_ , 0 , sizeof ( decOptions_ ) ) ;
decOptions_ . expandAllUVtoFloat = true ;
2014-09-12 01:38:45 +02:00
decOptions_ . expandAllWeightsToFloat = true ;
decOptions_ . expand8BitNormalsToFloat = true ;
2014-09-10 10:28:44 +02:00
2013-11-15 14:24:25 +01:00
decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL ;
// Allocate nicely aligned memory. Maybe graphics drivers will
// appreciate it.
// All this is a LOT of memory, need to see if we can cut down somehow.
decoded = ( u8 * ) AllocateMemoryPages ( DECODED_VERTEX_BUFFER_SIZE ) ;
decIndex = ( u16 * ) AllocateMemoryPages ( DECODED_INDEX_BUFFER_SIZE ) ;
2015-01-29 14:12:24 +01:00
splineBuffer = ( u8 * ) AllocateMemoryPages ( SPLINE_BUFFER_SIZE ) ;
2013-11-15 14:24:25 +01:00
transformed = ( TransformedVertex * ) AllocateMemoryPages ( TRANSFORMED_VERTEX_BUFFER_SIZE ) ;
transformedExpanded = ( TransformedVertex * ) AllocateMemoryPages ( 3 * TRANSFORMED_VERTEX_BUFFER_SIZE ) ;
2013-12-03 16:53:30 +01:00
if ( g_Config . bPrescaleUV ) {
uvScale = new UVScale [ MAX_DEFERRED_DRAW_CALLS ] ;
}
indexGen . Setup ( decIndex ) ;
2014-09-12 01:48:43 +02:00
2013-12-03 16:53:30 +01:00
InitDeviceObjects ( ) ;
2013-08-17 11:23:51 +02:00
}
2016-04-10 10:21:48 +02:00
DrawEngineDX9 : : ~ DrawEngineDX9 ( ) {
2013-08-17 11:23:51 +02:00
DestroyDeviceObjects ( ) ;
FreeMemoryPages ( decoded , DECODED_VERTEX_BUFFER_SIZE ) ;
FreeMemoryPages ( decIndex , DECODED_INDEX_BUFFER_SIZE ) ;
2015-01-29 14:12:24 +01:00
FreeMemoryPages ( splineBuffer , SPLINE_BUFFER_SIZE ) ;
2013-08-17 11:23:51 +02:00
FreeMemoryPages ( transformed , TRANSFORMED_VERTEX_BUFFER_SIZE ) ;
FreeMemoryPages ( transformedExpanded , 3 * TRANSFORMED_VERTEX_BUFFER_SIZE ) ;
2014-09-07 13:07:12 -07:00
for ( auto decl = vertexDeclMap_ . begin ( ) ; decl ! = vertexDeclMap_ . end ( ) ; + + decl ) {
2014-09-09 00:29:01 +02:00
if ( decl - > second ) {
decl - > second - > Release ( ) ;
}
2014-09-07 13:07:12 -07:00
}
2013-08-17 11:23:51 +02:00
delete [ ] uvScale ;
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : InitDeviceObjects ( ) {
2013-08-23 17:24:51 +02:00
2013-08-17 11:23:51 +02:00
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : DestroyDeviceObjects ( ) {
2013-08-17 11:23:51 +02:00
ClearTrackedVertexArrays ( ) ;
}
2013-08-23 17:24:51 +02:00
2013-08-21 11:10:56 +02:00
struct DeclTypeInfo {
u32 type ;
2013-09-01 08:38:40 +02:00
const char * name ;
2013-08-21 11:10:56 +02:00
} ;
2013-08-23 17:24:51 +02:00
2013-08-21 11:10:56 +02:00
static const DeclTypeInfo VComp [ ] = {
2014-09-17 22:03:44 +02:00
{ 0 , " NULL " } , // DEC_NONE,
{ D3DDECLTYPE_FLOAT1 , " D3DDECLTYPE_FLOAT1 " } , // DEC_FLOAT_1,
{ D3DDECLTYPE_FLOAT2 , " D3DDECLTYPE_FLOAT2 " } , // DEC_FLOAT_2,
{ D3DDECLTYPE_FLOAT3 , " D3DDECLTYPE_FLOAT3 " } , // DEC_FLOAT_3,
{ D3DDECLTYPE_FLOAT4 , " D3DDECLTYPE_FLOAT4 " } , // DEC_FLOAT_4,
{ 0 , " UNUSED " } , // DEC_S8_3,
{ D3DDECLTYPE_SHORT4N , " D3DDECLTYPE_SHORT4N " } , // DEC_S16_3,
{ D3DDECLTYPE_UBYTE4N , " D3DDECLTYPE_UBYTE4N " } , // DEC_U8_1,
{ D3DDECLTYPE_UBYTE4N , " D3DDECLTYPE_UBYTE4N " } , // DEC_U8_2,
{ D3DDECLTYPE_UBYTE4N , " D3DDECLTYPE_UBYTE4N " } , // DEC_U8_3,
{ D3DDECLTYPE_UBYTE4N , " D3DDECLTYPE_UBYTE4N " } , // DEC_U8_4,
{ 0 , " UNUSED_DEC_U16_1 " } , // DEC_U16_1,
{ 0 , " UNUSED_DEC_U16_2 " } , // DEC_U16_2,
{ D3DDECLTYPE_USHORT4N , " D3DDECLTYPE_USHORT4N " } , // DEC_U16_3,
{ D3DDECLTYPE_USHORT4N , " D3DDECLTYPE_USHORT4N " } , // DEC_U16_4,
2013-09-15 12:46:14 +02:00
// Not supported in regular DX9 so faking, will cause graphics bugs until worked around
2014-09-17 22:03:44 +02:00
{ 0 , " UNUSED_DEC_U8A_2 " } , // DEC_U8A_2,
{ 0 , " UNUSED_DEC_U16A_2 " } , // DEC_U16A_2,
2013-08-21 11:10:56 +02:00
} ;
static void VertexAttribSetup ( D3DVERTEXELEMENT9 * VertexElement , u8 fmt , u8 offset , u8 usage , u8 usage_index = 0 ) {
memset ( VertexElement , 0 , sizeof ( D3DVERTEXELEMENT9 ) ) ;
VertexElement - > Offset = offset ;
2014-08-26 07:37:19 -07:00
VertexElement - > Type = VComp [ fmt ] . type ;
2013-08-21 11:10:56 +02:00
VertexElement - > Usage = usage ;
VertexElement - > UsageIndex = usage_index ;
}
2016-04-10 10:21:48 +02:00
IDirect3DVertexDeclaration9 * DrawEngineDX9 : : SetupDecFmtForDraw ( VSShader * vshader , const DecVtxFormat & decFmt , u32 pspFmt ) {
2014-09-07 13:07:12 -07:00
auto vertexDeclCached = vertexDeclMap_ . find ( pspFmt ) ;
2013-09-01 08:38:40 +02:00
2014-09-07 13:07:12 -07:00
if ( vertexDeclCached = = vertexDeclMap_ . end ( ) ) {
D3DVERTEXELEMENT9 VertexElements [ 8 ] ;
D3DVERTEXELEMENT9 * VertexElement = & VertexElements [ 0 ] ;
2013-09-01 08:38:40 +02:00
// Vertices Elements orders
// WEIGHT
if ( decFmt . w0fmt ! = 0 ) {
2014-09-10 15:20:57 +02:00
VertexAttribSetup ( VertexElement , decFmt . w0fmt , decFmt . w0off , D3DDECLUSAGE_TEXCOORD , 1 ) ;
2013-09-01 08:38:40 +02:00
VertexElement + + ;
}
if ( decFmt . w1fmt ! = 0 ) {
2014-09-10 15:20:57 +02:00
VertexAttribSetup ( VertexElement , decFmt . w1fmt , decFmt . w1off , D3DDECLUSAGE_TEXCOORD , 2 ) ;
2013-09-01 08:38:40 +02:00
VertexElement + + ;
}
2013-08-21 11:10:56 +02:00
2013-09-01 08:38:40 +02:00
// TC
if ( decFmt . uvfmt ! = 0 ) {
2014-09-09 00:42:12 +02:00
VertexAttribSetup ( VertexElement , decFmt . uvfmt , decFmt . uvoff , D3DDECLUSAGE_TEXCOORD , 0 ) ;
2013-09-01 08:38:40 +02:00
VertexElement + + ;
}
// COLOR
if ( decFmt . c0fmt ! = 0 ) {
2014-08-25 01:16:49 -07:00
VertexAttribSetup ( VertexElement , decFmt . c0fmt , decFmt . c0off , D3DDECLUSAGE_COLOR , 0 ) ;
2013-09-01 08:38:40 +02:00
VertexElement + + ;
}
// Never used ?
if ( decFmt . c1fmt ! = 0 ) {
2014-08-25 01:16:49 -07:00
VertexAttribSetup ( VertexElement , decFmt . c1fmt , decFmt . c1off , D3DDECLUSAGE_COLOR , 1 ) ;
2013-09-01 08:38:40 +02:00
VertexElement + + ;
}
// NORMAL
if ( decFmt . nrmfmt ! = 0 ) {
VertexAttribSetup ( VertexElement , decFmt . nrmfmt , decFmt . nrmoff , D3DDECLUSAGE_NORMAL , 0 ) ;
VertexElement + + ;
}
// POSITION
// Always
VertexAttribSetup ( VertexElement , decFmt . posfmt , decFmt . posoff , D3DDECLUSAGE_POSITION , 0 ) ;
VertexElement + + ;
// End
D3DVERTEXELEMENT9 end = D3DDECL_END ( ) ;
memcpy ( VertexElement , & end , sizeof ( D3DVERTEXELEMENT9 ) ) ;
2014-09-18 00:40:25 +02:00
2014-09-07 13:07:12 -07:00
// Create declaration
2014-09-09 00:29:01 +02:00
IDirect3DVertexDeclaration9 * pHardwareVertexDecl = nullptr ;
2014-08-22 21:27:13 +02:00
HRESULT hr = pD3Ddevice - > CreateVertexDeclaration ( VertexElements , & pHardwareVertexDecl ) ;
if ( FAILED ( hr ) ) {
2014-09-13 12:27:20 +02:00
ERROR_LOG ( G3D , " Failed to create vertex declaration! " ) ;
pHardwareVertexDecl = nullptr ;
2014-08-22 21:27:13 +02:00
}
2013-09-01 08:38:40 +02:00
// Add it to map
2014-09-07 13:07:12 -07:00
vertexDeclMap_ [ pspFmt ] = pHardwareVertexDecl ;
return pHardwareVertexDecl ;
2013-09-01 08:38:40 +02:00
} else {
// Set it from map
2014-09-07 13:07:12 -07:00
return vertexDeclCached - > second ;
2013-09-01 08:38:40 +02:00
}
2013-08-21 11:10:56 +02:00
}
2016-04-10 10:21:48 +02:00
VertexDecoder * DrawEngineDX9 : : GetVertexDecoder ( u32 vtype ) {
2013-08-17 11:23:51 +02:00
auto iter = decoderMap_ . find ( vtype ) ;
if ( iter ! = decoderMap_ . end ( ) )
return iter - > second ;
2014-09-14 13:50:57 -07:00
VertexDecoder * dec = new VertexDecoder ( ) ;
2014-09-12 01:48:43 +02:00
dec - > SetVertexType ( vtype , decOptions_ , decJitCache_ ) ;
2013-08-17 11:23:51 +02:00
decoderMap_ [ vtype ] = dec ;
return dec ;
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : SetupVertexDecoder ( u32 vertType ) {
2014-09-13 12:27:20 +02:00
SetupVertexDecoderInternal ( vertType ) ;
}
2016-04-10 10:21:48 +02:00
inline void DrawEngineDX9 : : SetupVertexDecoderInternal ( u32 vertType ) {
2014-09-13 12:27:20 +02:00
// As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it
// into the top of the verttype where there are unused bits.
const u32 vertTypeID = ( vertType & 0xFFFFFF ) | ( gstate . getUVGenMode ( ) < < 24 ) ;
2013-08-17 11:23:51 +02:00
// If vtype has changed, setup the vertex decoder.
2014-09-13 12:27:20 +02:00
if ( vertTypeID ! = lastVType_ ) {
dec_ = GetVertexDecoder ( vertTypeID ) ;
lastVType_ = vertTypeID ;
2013-08-17 11:23:51 +02:00
}
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : SubmitPrim ( void * verts , void * inds , GEPrimitiveType prim , int vertexCount , u32 vertType , int * bytesRead ) {
2013-10-27 14:43:58 -07:00
if ( ! indexGen . PrimCompatible ( prevPrim_ , prim ) | | numDrawCalls > = MAX_DEFERRED_DRAW_CALLS | | vertexCountInDrawCalls + vertexCount > VERTEX_BUFFER_MAX )
2013-08-17 11:23:51 +02:00
Flush ( ) ;
2014-09-13 12:27:20 +02:00
2013-09-10 22:35:38 +02:00
// TODO: Is this the right thing to do?
if ( prim = = GE_PRIM_KEEP_PREVIOUS ) {
2014-10-29 23:50:21 +01:00
prim = prevPrim_ ! = GE_PRIM_INVALID ? prevPrim_ : GE_PRIM_POINTS ;
2014-10-30 01:03:34 +01:00
} else {
2014-10-19 20:25:04 +02:00
prevPrim_ = prim ;
2013-09-10 22:35:38 +02:00
}
2014-10-18 11:26:51 +02:00
2014-09-13 12:27:20 +02:00
SetupVertexDecoderInternal ( vertType ) ;
2013-08-17 11:23:51 +02:00
2014-10-19 20:25:04 +02:00
* bytesRead = vertexCount * dec_ - > VertexSize ( ) ;
2013-08-17 11:23:51 +02:00
2015-02-28 01:28:30 -08:00
if ( ( vertexCount < 2 & & prim > 0 ) | | ( vertexCount < 3 & & prim > 2 & & prim ! = GE_PRIM_RECTANGLES ) )
return ;
2013-08-17 11:23:51 +02:00
DeferredDrawCall & dc = drawCalls [ numDrawCalls ] ;
dc . verts = verts ;
dc . inds = inds ;
dc . vertType = vertType ;
2014-09-13 12:27:20 +02:00
dc . indexType = ( vertType & GE_VTYPE_IDX_MASK ) > > GE_VTYPE_IDX_SHIFT ;
2013-08-17 11:23:51 +02:00
dc . prim = prim ;
dc . vertexCount = vertexCount ;
2014-09-13 12:27:20 +02:00
u32 dhash = dcid_ ;
dhash ^ = ( u32 ) ( uintptr_t ) verts ;
dhash = __rotl ( dhash , 13 ) ;
dhash ^ = ( u32 ) ( uintptr_t ) inds ;
dhash = __rotl ( dhash , 13 ) ;
dhash ^ = ( u32 ) vertType ;
dhash = __rotl ( dhash , 13 ) ;
dhash ^ = ( u32 ) vertexCount ;
dhash = __rotl ( dhash , 13 ) ;
dhash ^ = ( u32 ) prim ;
dcid_ = dhash ;
2013-08-17 11:23:51 +02:00
if ( inds ) {
GetIndexBounds ( inds , vertexCount , vertType , & dc . indexLowerBound , & dc . indexUpperBound ) ;
} else {
dc . indexLowerBound = 0 ;
dc . indexUpperBound = vertexCount - 1 ;
}
if ( uvScale ) {
uvScale [ numDrawCalls ] = gstate_c . uv ;
}
2014-09-13 12:27:20 +02:00
2013-08-17 11:23:51 +02:00
numDrawCalls + + ;
2013-10-27 14:43:58 -07:00
vertexCountInDrawCalls + = vertexCount ;
2014-09-13 12:11:34 +02:00
if ( g_Config . bSoftwareSkinning & & ( vertType & GE_VTYPE_WEIGHT_MASK ) ) {
2014-09-13 12:27:20 +02:00
DecodeVertsStep ( ) ;
decodeCounter_ + + ;
2014-09-13 12:11:34 +02:00
}
if ( prim = = GE_PRIM_RECTANGLES & & ( gstate . getTextureAddress ( 0 ) & 0x3FFFFFFF ) = = ( gstate . getFrameBufAddress ( ) & 0x3FFFFFFF ) ) {
2014-10-19 20:25:04 +02:00
// Rendertarget == texture?
2014-09-13 12:11:34 +02:00
if ( ! g_Config . bDisableSlowFramebufEffects ) {
gstate_c . textureChanged | = TEXCHANGE_PARAMSONLY ;
Flush ( ) ;
}
}
2013-08-17 11:23:51 +02:00
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : DecodeVerts ( ) {
2014-09-14 13:50:57 -07:00
if ( uvScale ) {
const UVScale origUV = gstate_c . uv ;
for ( ; decodeCounter_ < numDrawCalls ; decodeCounter_ + + ) {
gstate_c . uv = uvScale [ decodeCounter_ ] ;
DecodeVertsStep ( ) ;
}
gstate_c . uv = origUV ;
} else {
for ( ; decodeCounter_ < numDrawCalls ; decodeCounter_ + + ) {
DecodeVertsStep ( ) ;
}
}
// Sanity check
if ( indexGen . Prim ( ) < 0 ) {
ERROR_LOG_REPORT ( G3D , " DecodeVerts: Failed to deduce prim: %i " , indexGen . Prim ( ) ) ;
// Force to points (0)
indexGen . AddPrim ( GE_PRIM_POINTS , 0 ) ;
}
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : DecodeVertsStep ( ) {
2014-09-13 12:27:20 +02:00
const int i = decodeCounter_ ;
2013-08-17 11:23:51 +02:00
2014-09-13 12:27:20 +02:00
const DeferredDrawCall & dc = drawCalls [ i ] ;
2013-08-17 11:23:51 +02:00
2014-09-13 12:27:20 +02:00
indexGen . SetIndex ( decodedVerts_ ) ;
int indexLowerBound = dc . indexLowerBound , indexUpperBound = dc . indexUpperBound ;
u32 indexType = dc . indexType ;
void * inds = dc . inds ;
if ( indexType = = GE_VTYPE_IDX_NONE > > GE_VTYPE_IDX_SHIFT ) {
// Decode the verts and apply morphing. Simple.
dec_ - > DecodeVerts ( decoded + decodedVerts_ * ( int ) dec_ - > GetDecVtxFmt ( ) . stride ,
dc . verts , indexLowerBound , indexUpperBound ) ;
decodedVerts_ + = indexUpperBound - indexLowerBound + 1 ;
indexGen . AddPrim ( dc . prim , dc . vertexCount ) ;
} else {
// It's fairly common that games issue long sequences of PRIM calls, with differing
// inds pointer but the same base vertex pointer. We'd like to reuse vertices between
// these as much as possible, so we make sure here to combine as many as possible
// into one nice big drawcall, sharing data.
// 1. Look ahead to find the max index, only looking as "matching" drawcalls.
// Expand the lower and upper bounds as we go.
int lastMatch = i ;
const int total = numDrawCalls ;
if ( uvScale ) {
for ( int j = i + 1 ; j < total ; + + j ) {
2013-08-17 11:23:51 +02:00
if ( drawCalls [ j ] . verts ! = dc . verts )
break ;
2014-09-13 12:27:20 +02:00
if ( memcmp ( & uvScale [ j ] , & uvScale [ i ] , sizeof ( uvScale [ 0 ] ) ) ! = 0 )
2013-08-17 11:23:51 +02:00
break ;
indexLowerBound = std : : min ( indexLowerBound , ( int ) drawCalls [ j ] . indexLowerBound ) ;
indexUpperBound = std : : max ( indexUpperBound , ( int ) drawCalls [ j ] . indexUpperBound ) ;
lastMatch = j ;
}
2014-09-13 12:27:20 +02:00
} else {
for ( int j = i + 1 ; j < total ; + + j ) {
if ( drawCalls [ j ] . verts ! = dc . verts )
2013-08-17 11:23:51 +02:00
break ;
2014-09-13 12:27:20 +02:00
indexLowerBound = std : : min ( indexLowerBound , ( int ) drawCalls [ j ] . indexLowerBound ) ;
indexUpperBound = std : : max ( indexUpperBound , ( int ) drawCalls [ j ] . indexUpperBound ) ;
lastMatch = j ;
}
}
2013-08-17 11:23:51 +02:00
2014-09-13 12:27:20 +02:00
// 2. Loop through the drawcalls, translating indices as we go.
switch ( indexType ) {
case GE_VTYPE_IDX_8BIT > > GE_VTYPE_IDX_SHIFT :
for ( int j = i ; j < = lastMatch ; j + + ) {
indexGen . TranslatePrim ( drawCalls [ j ] . prim , drawCalls [ j ] . vertexCount , ( const u8 * ) drawCalls [ j ] . inds , indexLowerBound ) ;
}
break ;
case GE_VTYPE_IDX_16BIT > > GE_VTYPE_IDX_SHIFT :
for ( int j = i ; j < = lastMatch ; j + + ) {
2016-04-10 01:52:51 -07:00
indexGen . TranslatePrim ( drawCalls [ j ] . prim , drawCalls [ j ] . vertexCount , ( const u16_le * ) drawCalls [ j ] . inds , indexLowerBound ) ;
2014-09-13 12:27:20 +02:00
}
break ;
2016-04-10 10:59:23 +02:00
case GE_VTYPE_IDX_32BIT > > GE_VTYPE_IDX_SHIFT :
for ( int j = i ; j < = lastMatch ; j + + ) {
2016-04-10 01:52:51 -07:00
indexGen . TranslatePrim ( drawCalls [ j ] . prim , drawCalls [ j ] . vertexCount , ( const u32_le * ) drawCalls [ j ] . inds , indexLowerBound ) ;
2016-04-10 10:59:23 +02:00
}
break ;
2013-08-17 11:23:51 +02:00
}
2014-09-13 12:27:20 +02:00
const int vertexCount = indexUpperBound - indexLowerBound + 1 ;
2015-03-01 16:15:13 +01:00
// This check is a workaround for Pangya Fantasy Golf, which sends bogus index data when switching items in "My Room" sometimes.
if ( decodedVerts_ + vertexCount > VERTEX_BUFFER_MAX ) {
return ;
}
2014-09-13 12:27:20 +02:00
// 3. Decode that range of vertex data.
dec_ - > DecodeVerts ( decoded + decodedVerts_ * ( int ) dec_ - > GetDecVtxFmt ( ) . stride ,
dc . verts , indexLowerBound , indexUpperBound ) ;
decodedVerts_ + = vertexCount ;
// 4. Advance indexgen vertex counter.
indexGen . Advance ( vertexCount ) ;
decodeCounter_ = lastMatch ;
2013-08-17 11:23:51 +02:00
}
2014-09-13 12:27:20 +02:00
}
2014-09-14 13:50:57 -07:00
inline u32 ComputeMiniHashRange ( const void * ptr , size_t sz ) {
// Switch to u32 units.
const u32 * p = ( const u32 * ) ptr ;
sz > > = 2 ;
2013-08-17 11:23:51 +02:00
2014-09-14 13:50:57 -07:00
if ( sz > 100 ) {
size_t step = sz / 4 ;
u32 hash = 0 ;
for ( size_t i = 0 ; i < sz ; i + = step ) {
2014-10-26 17:49:24 -07:00
hash + = DoReliableHash32 ( p + i , 100 , 0x3A44B9C4 ) ;
2014-09-13 12:27:20 +02:00
}
2014-09-14 13:50:57 -07:00
return hash ;
2014-09-13 12:27:20 +02:00
} else {
2014-09-14 13:50:57 -07:00
return p [ 0 ] + p [ sz - 1 ] ;
}
}
2016-04-10 10:21:48 +02:00
u32 DrawEngineDX9 : : ComputeMiniHash ( ) {
2014-09-14 13:50:57 -07:00
u32 fullhash = 0 ;
const int vertexSize = dec_ - > GetDecVtxFmt ( ) . stride ;
2016-04-10 01:52:51 -07:00
const int indexSize = IndexSize ( dec_ - > VertexType ( ) ) ;
2014-09-14 13:50:57 -07:00
int step ;
if ( numDrawCalls < 3 ) {
step = 1 ;
} else if ( numDrawCalls < 8 ) {
step = 4 ;
} else {
step = numDrawCalls / 8 ;
}
for ( int i = 0 ; i < numDrawCalls ; i + = step ) {
const DeferredDrawCall & dc = drawCalls [ i ] ;
if ( ! dc . inds ) {
fullhash + = ComputeMiniHashRange ( dc . verts , vertexSize * dc . vertexCount ) ;
} else {
int indexLowerBound = dc . indexLowerBound , indexUpperBound = dc . indexUpperBound ;
fullhash + = ComputeMiniHashRange ( ( const u8 * ) dc . verts + vertexSize * indexLowerBound , vertexSize * ( indexUpperBound - indexLowerBound ) ) ;
fullhash + = ComputeMiniHashRange ( dc . inds , indexSize * dc . vertexCount ) ;
2014-09-13 12:27:20 +02:00
}
}
2014-09-14 13:50:57 -07:00
return fullhash ;
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : MarkUnreliable ( VertexArrayInfoDX9 * vai ) {
2014-09-14 13:50:57 -07:00
vai - > status = VertexArrayInfoDX9 : : VAI_UNRELIABLE ;
if ( vai - > vbo ) {
vai - > vbo - > Release ( ) ;
vai - > vbo = nullptr ;
}
if ( vai - > ebo ) {
vai - > ebo - > Release ( ) ;
vai - > ebo = nullptr ;
2013-08-17 11:23:51 +02:00
}
}
2016-04-10 10:21:48 +02:00
ReliableHashType DrawEngineDX9 : : ComputeHash ( ) {
2014-10-26 17:49:24 -07:00
ReliableHashType fullhash = 0 ;
2014-09-14 13:50:57 -07:00
const int vertexSize = dec_ - > GetDecVtxFmt ( ) . stride ;
2016-04-10 01:52:51 -07:00
const int indexSize = IndexSize ( dec_ - > VertexType ( ) ) ;
2013-08-17 11:23:51 +02:00
// TODO: Add some caps both for numDrawCalls and num verts to check?
2013-08-20 18:47:11 +02:00
// It is really very expensive to check all the vertex data so often.
2013-08-17 11:23:51 +02:00
for ( int i = 0 ; i < numDrawCalls ; i + + ) {
2013-11-09 23:29:44 +01:00
const DeferredDrawCall & dc = drawCalls [ i ] ;
if ( ! dc . inds ) {
fullhash + = DoReliableHash ( ( const char * ) dc . verts , vertexSize * dc . vertexCount , 0x1DE8CAC4 ) ;
2013-08-17 11:23:51 +02:00
} else {
2013-11-09 23:29:44 +01:00
int indexLowerBound = dc . indexLowerBound , indexUpperBound = dc . indexUpperBound ;
int j = i + 1 ;
int lastMatch = i ;
while ( j < numDrawCalls ) {
if ( drawCalls [ j ] . verts ! = dc . verts )
break ;
indexLowerBound = std : : min ( indexLowerBound , ( int ) dc . indexLowerBound ) ;
indexUpperBound = std : : max ( indexUpperBound , ( int ) dc . indexUpperBound ) ;
lastMatch = j ;
j + + ;
}
2013-08-17 11:23:51 +02:00
// This could get seriously expensive with sparse indices. Need to combine hashing ranges the same way
// we do when drawing.
2013-11-09 23:29:44 +01:00
fullhash + = DoReliableHash ( ( const char * ) dc . verts + vertexSize * indexLowerBound ,
vertexSize * ( indexUpperBound - indexLowerBound ) , 0x029F3EE1 ) ;
// Hm, we will miss some indices when combining above, but meh, it should be fine.
fullhash + = DoReliableHash ( ( const char * ) dc . inds , indexSize * dc . vertexCount , 0x955FD1CA ) ;
i = lastMatch ;
2013-08-17 11:23:51 +02:00
}
}
2013-11-09 23:29:44 +01:00
if ( uvScale ) {
fullhash + = DoReliableHash ( & uvScale [ 0 ] , sizeof ( uvScale [ 0 ] ) * numDrawCalls , 0x0123e658 ) ;
}
2013-08-17 11:23:51 +02:00
return fullhash ;
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : ClearTrackedVertexArrays ( ) {
2013-08-17 11:23:51 +02:00
for ( auto vai = vai_ . begin ( ) ; vai ! = vai_ . end ( ) ; vai + + ) {
delete vai - > second ;
}
vai_ . clear ( ) ;
}
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : DecimateTrackedVertexArrays ( ) {
2013-08-20 18:47:11 +02:00
if ( - - decimationCounter_ < = 0 ) {
decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL ;
} else {
return ;
}
2014-09-14 13:50:57 -07:00
const int threshold = gpuStats . numFlips - VAI_KILL_AGE ;
const int unreliableThreshold = gpuStats . numFlips - VAI_UNRELIABLE_KILL_AGE ;
int unreliableLeft = VAI_UNRELIABLE_KILL_MAX ;
2013-08-17 11:23:51 +02:00
for ( auto iter = vai_ . begin ( ) ; iter ! = vai_ . end ( ) ; ) {
2014-09-14 13:50:57 -07:00
bool kill ;
if ( iter - > second - > status = = VertexArrayInfoDX9 : : VAI_UNRELIABLE ) {
// We limit killing unreliable so we don't rehash too often.
kill = iter - > second - > lastFrame < unreliableThreshold & & - - unreliableLeft > = 0 ;
} else {
kill = iter - > second - > lastFrame < threshold ;
}
if ( kill ) {
2013-08-17 11:23:51 +02:00
delete iter - > second ;
vai_ . erase ( iter + + ) ;
2014-09-14 13:50:57 -07:00
} else {
2013-08-17 11:23:51 +02:00
+ + iter ;
2014-09-14 13:50:57 -07:00
}
2013-08-17 11:23:51 +02:00
}
// Enable if you want to see vertex decoders in the log output. Need a better way.
#if 0
char buffer [ 16384 ] ;
for ( std : : map < u32 , VertexDecoder * > : : iterator dec = decoderMap_ . begin ( ) ; dec ! = decoderMap_ . end ( ) ; + + dec ) {
char * ptr = buffer ;
ptr + = dec - > second - > ToString ( ptr ) ;
2013-08-23 17:24:51 +02:00
// *ptr++ = '\n';
2013-09-10 22:35:38 +02:00
NOTICE_LOG ( G3D , buffer ) ;
2013-08-17 11:23:51 +02:00
}
# endif
}
2013-09-15 12:46:14 +02:00
VertexArrayInfoDX9 : : ~ VertexArrayInfoDX9 ( ) {
2013-08-17 11:23:51 +02:00
if ( vbo ) {
vbo - > Release ( ) ;
}
if ( ebo ) {
ebo - > Release ( ) ;
}
}
2014-10-18 11:26:51 +02:00
// The inline wrapper in the header checks for numDrawCalls == 0
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : DoFlush ( ) {
2013-08-17 11:23:51 +02:00
gpuStats . numFlushes + + ;
gpuStats . numTrackedVertexArrays = ( int ) vai_ . size ( ) ;
2013-08-20 18:47:11 +02:00
// This is not done on every drawcall, we should collect vertex data
2013-08-17 11:23:51 +02:00
// until critical state changes. That's when we draw (flush).
2013-09-04 11:19:36 +02:00
GEPrimitiveType prim = prevPrim_ ;
2013-08-17 11:23:51 +02:00
ApplyDrawState ( prim ) ;
2014-09-10 14:07:30 +02:00
VSShader * vshader = shaderManager_ - > ApplyShader ( prim , lastVType_ ) ;
2013-08-21 11:10:56 +02:00
2014-09-10 14:07:30 +02:00
if ( vshader - > UseHWTransform ( ) ) {
2014-09-17 22:03:44 +02:00
LPDIRECT3DVERTEXBUFFER9 vb_ = NULL ;
LPDIRECT3DINDEXBUFFER9 ib_ = NULL ;
int vertexCount = 0 ;
int maxIndex = 0 ;
bool useElements = true ;
// Cannot cache vertex data with morph enabled.
bool useCache = g_Config . bVertexCache & & ! ( lastVType_ & GE_VTYPE_MORPHCOUNT_MASK ) ;
// Also avoid caching when software skinning.
if ( g_Config . bSoftwareSkinning & & ( lastVType_ & GE_VTYPE_WEIGHT_MASK ) )
useCache = false ;
if ( useCache ) {
u32 id = dcid_ ;
auto iter = vai_ . find ( id ) ;
VertexArrayInfoDX9 * vai ;
if ( iter ! = vai_ . end ( ) ) {
// We've seen this before. Could have been a cached draw.
vai = iter - > second ;
} else {
vai = new VertexArrayInfoDX9 ( ) ;
vai_ [ id ] = vai ;
}
2013-08-23 17:24:51 +02:00
2014-09-17 22:03:44 +02:00
switch ( vai - > status ) {
case VertexArrayInfoDX9 : : VAI_NEW :
{
// Haven't seen this one before.
2014-10-26 17:49:24 -07:00
ReliableHashType dataHash = ComputeHash ( ) ;
2014-09-17 22:03:44 +02:00
vai - > hash = dataHash ;
vai - > minihash = ComputeMiniHash ( ) ;
vai - > status = VertexArrayInfoDX9 : : VAI_HASHING ;
vai - > drawsUntilNextFullHash = 0 ;
DecodeVerts ( ) ; // writes to indexGen
vai - > numVerts = indexGen . VertexCount ( ) ;
vai - > prim = indexGen . Prim ( ) ;
vai - > maxIndex = indexGen . MaxIndex ( ) ;
vai - > flags = gstate_c . vertexFullAlpha ? VAI_FLAG_VERTEXFULLALPHA : 0 ;
goto rotateVBO ;
}
2014-09-09 01:03:08 -07:00
2014-09-17 22:03:44 +02:00
// Hashing - still gaining confidence about the buffer.
// But if we get this far it's likely to be worth creating a vertex buffer.
case VertexArrayInfoDX9 : : VAI_HASHING :
{
vai - > numDraws + + ;
if ( vai - > lastFrame ! = gpuStats . numFlips ) {
vai - > numFrames + + ;
2013-08-23 17:24:51 +02:00
}
2014-09-17 22:03:44 +02:00
if ( vai - > drawsUntilNextFullHash = = 0 ) {
// Let's try to skip a full hash if mini would fail.
const u32 newMiniHash = ComputeMiniHash ( ) ;
2014-10-26 17:49:24 -07:00
ReliableHashType newHash = vai - > hash ;
2014-09-17 22:03:44 +02:00
if ( newMiniHash = = vai - > minihash ) {
newHash = ComputeHash ( ) ;
}
if ( newMiniHash ! = vai - > minihash | | newHash ! = vai - > hash ) {
MarkUnreliable ( vai ) ;
DecodeVerts ( ) ;
goto rotateVBO ;
2013-08-23 17:24:51 +02:00
}
2014-09-17 22:03:44 +02:00
if ( vai - > numVerts > 64 ) {
// exponential backoff up to 16 draws, then every 24
vai - > drawsUntilNextFullHash = std : : min ( 24 , vai - > numFrames ) ;
2013-08-23 17:24:51 +02:00
} else {
2014-09-17 22:03:44 +02:00
// Lower numbers seem much more likely to change.
vai - > drawsUntilNextFullHash = 0 ;
2013-08-23 17:24:51 +02:00
}
2014-09-17 22:03:44 +02:00
// TODO: tweak
//if (vai->numFrames > 1000) {
// vai->status = VertexArrayInfo::VAI_RELIABLE;
//}
} else {
vai - > drawsUntilNextFullHash - - ;
u32 newMiniHash = ComputeMiniHash ( ) ;
if ( newMiniHash ! = vai - > minihash ) {
MarkUnreliable ( vai ) ;
2013-08-23 17:24:51 +02:00
DecodeVerts ( ) ;
2014-09-17 22:03:44 +02:00
goto rotateVBO ;
2013-08-23 17:24:51 +02:00
}
}
2014-09-17 22:03:44 +02:00
if ( vai - > vbo = = 0 ) {
DecodeVerts ( ) ;
vai - > numVerts = indexGen . VertexCount ( ) ;
vai - > prim = indexGen . Prim ( ) ;
vai - > maxIndex = indexGen . MaxIndex ( ) ;
vai - > flags = gstate_c . vertexFullAlpha ? VAI_FLAG_VERTEXFULLALPHA : 0 ;
useElements = ! indexGen . SeenOnlyPurePrims ( ) ;
if ( ! useElements & & indexGen . PureCount ( ) ) {
vai - > numVerts = indexGen . PureCount ( ) ;
}
2015-09-13 11:09:21 -07:00
2015-09-13 14:52:10 -07:00
_dbg_assert_msg_ ( G3D , gstate_c . vertBounds . minV > = gstate_c . vertBounds . maxV , " Should not have checked UVs when caching. " ) ;
2015-09-13 11:09:21 -07:00
2014-09-18 00:40:25 +02:00
void * pVb ;
u32 size = dec_ - > GetDecVtxFmt ( ) . stride * indexGen . MaxIndex ( ) ;
pD3Ddevice - > CreateVertexBuffer ( size , D3DUSAGE_WRITEONLY , 0 , D3DPOOL_DEFAULT , & vai - > vbo , NULL ) ;
vai - > vbo - > Lock ( 0 , size , & pVb , 0 ) ;
memcpy ( pVb , decoded , size ) ;
vai - > vbo - > Unlock ( ) ;
2014-09-17 22:03:44 +02:00
if ( useElements ) {
void * pIb ;
2014-10-18 11:26:51 +02:00
u32 size = sizeof ( short ) * indexGen . VertexCount ( ) ;
2014-09-17 22:03:44 +02:00
pD3Ddevice - > CreateIndexBuffer ( size , D3DUSAGE_WRITEONLY , D3DFMT_INDEX16 , D3DPOOL_DEFAULT , & vai - > ebo , NULL ) ;
vai - > ebo - > Lock ( 0 , size , & pIb , 0 ) ;
memcpy ( pIb , decIndex , size ) ;
vai - > ebo - > Unlock ( ) ;
} else {
vai - > ebo = 0 ;
2013-08-23 17:24:51 +02:00
}
2014-09-17 22:03:44 +02:00
} else {
2013-08-23 17:24:51 +02:00
gpuStats . numCachedDrawCalls + + ;
2014-09-17 22:03:44 +02:00
useElements = vai - > ebo ? true : false ;
2013-08-23 17:24:51 +02:00
gpuStats . numCachedVertsDrawn + = vai - > numVerts ;
2014-09-09 01:03:08 -07:00
gstate_c . vertexFullAlpha = vai - > flags & VAI_FLAG_VERTEXFULLALPHA ;
2013-08-23 17:24:51 +02:00
}
2014-09-17 22:03:44 +02:00
vb_ = vai - > vbo ;
ib_ = vai - > ebo ;
vertexCount = vai - > numVerts ;
maxIndex = vai - > maxIndex ;
prim = static_cast < GEPrimitiveType > ( vai - > prim ) ;
break ;
2013-08-23 17:24:51 +02:00
}
2014-09-17 22:03:44 +02:00
// Reliable - we don't even bother hashing anymore. Right now we don't go here until after a very long time.
case VertexArrayInfoDX9 : : VAI_RELIABLE :
{
vai - > numDraws + + ;
if ( vai - > lastFrame ! = gpuStats . numFlips ) {
vai - > numFrames + + ;
}
gpuStats . numCachedDrawCalls + + ;
gpuStats . numCachedVertsDrawn + = vai - > numVerts ;
vb_ = vai - > vbo ;
ib_ = vai - > ebo ;
2013-08-23 17:24:51 +02:00
2014-09-17 22:03:44 +02:00
vertexCount = vai - > numVerts ;
2013-08-23 17:24:51 +02:00
2014-09-17 22:03:44 +02:00
maxIndex = vai - > maxIndex ;
prim = static_cast < GEPrimitiveType > ( vai - > prim ) ;
2013-08-23 17:24:51 +02:00
2014-09-17 22:03:44 +02:00
gstate_c . vertexFullAlpha = vai - > flags & VAI_FLAG_VERTEXFULLALPHA ;
break ;
}
2013-08-23 17:24:51 +02:00
2014-09-17 22:03:44 +02:00
case VertexArrayInfoDX9 : : VAI_UNRELIABLE :
{
vai - > numDraws + + ;
if ( vai - > lastFrame ! = gpuStats . numFlips ) {
vai - > numFrames + + ;
2014-08-22 21:27:13 +02:00
}
2014-09-17 22:03:44 +02:00
DecodeVerts ( ) ;
goto rotateVBO ;
2013-08-23 17:24:51 +02:00
}
}
2014-09-17 22:03:44 +02:00
vai - > lastFrame = gpuStats . numFlips ;
2013-08-21 11:10:56 +02:00
} else {
2013-08-23 17:24:51 +02:00
DecodeVerts ( ) ;
2014-09-17 22:03:44 +02:00
rotateVBO :
2013-08-23 17:24:51 +02:00
gpuStats . numUncachedVertsDrawn + = indexGen . VertexCount ( ) ;
2014-09-17 22:03:44 +02:00
useElements = ! indexGen . SeenOnlyPurePrims ( ) ;
vertexCount = indexGen . VertexCount ( ) ;
maxIndex = indexGen . MaxIndex ( ) ;
if ( ! useElements & & indexGen . PureCount ( ) ) {
vertexCount = indexGen . PureCount ( ) ;
}
2013-08-23 17:24:51 +02:00
prim = indexGen . Prim ( ) ;
2014-09-17 22:03:44 +02:00
}
2014-09-13 13:53:04 +02:00
2015-01-04 18:00:59 +01:00
VERBOSE_LOG ( G3D , " Flush prim %i! %i verts in one go " , prim , vertexCount ) ;
2014-09-17 22:03:44 +02:00
bool hasColor = ( lastVType_ & GE_VTYPE_COL_MASK ) ! = GE_VTYPE_COL_NONE ;
if ( gstate . isModeThrough ( ) ) {
gstate_c . vertexFullAlpha = gstate_c . vertexFullAlpha & & ( hasColor | | gstate . getMaterialAmbientA ( ) = = 255 ) ;
} else {
gstate_c . vertexFullAlpha = gstate_c . vertexFullAlpha & & ( ( hasColor & & ( gstate . materialupdate & 1 ) ) | | gstate . getMaterialAmbientA ( ) = = 255 ) & & ( ! gstate . isLightingEnabled ( ) | | gstate . getAmbientA ( ) = = 255 ) ;
}
2014-09-13 13:53:04 +02:00
2015-03-17 22:07:09 -07:00
ApplyDrawStateLate ( ) ;
vshader = shaderManager_ - > ApplyShader ( prim , lastVType_ ) ;
2014-09-17 22:03:44 +02:00
IDirect3DVertexDeclaration9 * pHardwareVertexDecl = SetupDecFmtForDraw ( vshader , dec_ - > GetDecVtxFmt ( ) , dec_ - > VertexType ( ) ) ;
2014-09-13 13:53:04 +02:00
2014-09-17 22:03:44 +02:00
if ( pHardwareVertexDecl ) {
pD3Ddevice - > SetVertexDeclaration ( pHardwareVertexDecl ) ;
if ( vb_ = = NULL ) {
if ( useElements ) {
2014-09-17 22:06:34 +02:00
pD3Ddevice - > DrawIndexedPrimitiveUP ( glprim [ prim ] , 0 , maxIndex + 1 , D3DPrimCount ( glprim [ prim ] , vertexCount ) , decIndex , D3DFMT_INDEX16 , decoded , dec_ - > GetDecVtxFmt ( ) . stride ) ;
2014-09-13 13:53:04 +02:00
} else {
2014-09-17 22:03:44 +02:00
pD3Ddevice - > DrawPrimitiveUP ( glprim [ prim ] , D3DPrimCount ( glprim [ prim ] , vertexCount ) , decoded , dec_ - > GetDecVtxFmt ( ) . stride ) ;
2014-09-13 13:53:04 +02:00
}
2014-09-17 22:03:44 +02:00
} else {
pD3Ddevice - > SetStreamSource ( 0 , vb_ , 0 , dec_ - > GetDecVtxFmt ( ) . stride ) ;
2014-09-13 13:53:04 +02:00
2014-09-17 22:03:44 +02:00
if ( useElements ) {
pD3Ddevice - > SetIndices ( ib_ ) ;
2014-09-13 13:53:04 +02:00
2014-09-17 23:28:46 +02:00
pD3Ddevice - > DrawIndexedPrimitive ( glprim [ prim ] , 0 , 0 , maxIndex + 1 , 0 , D3DPrimCount ( glprim [ prim ] , vertexCount ) ) ;
2014-09-17 22:03:44 +02:00
} else {
pD3Ddevice - > DrawPrimitive ( glprim [ prim ] , 0 , D3DPrimCount ( glprim [ prim ] , vertexCount ) ) ;
2014-09-13 13:53:04 +02:00
}
2014-09-17 22:03:44 +02:00
}
}
} else {
DecodeVerts ( ) ;
bool hasColor = ( lastVType_ & GE_VTYPE_COL_MASK ) ! = GE_VTYPE_COL_NONE ;
if ( gstate . isModeThrough ( ) ) {
gstate_c . vertexFullAlpha = gstate_c . vertexFullAlpha & & ( hasColor | | gstate . getMaterialAmbientA ( ) = = 255 ) ;
} else {
gstate_c . vertexFullAlpha = gstate_c . vertexFullAlpha & & ( ( hasColor & & ( gstate . materialupdate & 1 ) ) | | gstate . getMaterialAmbientA ( ) = = 255 ) & & ( ! gstate . isLightingEnabled ( ) | | gstate . getAmbientA ( ) = = 255 ) ;
}
2014-09-13 13:53:04 +02:00
2014-09-17 22:03:44 +02:00
gpuStats . numUncachedVertsDrawn + = indexGen . VertexCount ( ) ;
prim = indexGen . Prim ( ) ;
// Undo the strip optimization, not supported by the SW code yet.
if ( prim = = GE_PRIM_TRIANGLE_STRIP )
prim = GE_PRIM_TRIANGLES ;
2015-01-04 18:00:59 +01:00
VERBOSE_LOG ( G3D , " Flush prim %i SW! %i verts in one go " , prim , indexGen . VertexCount ( ) ) ;
2014-09-17 22:03:44 +02:00
int numTrans = 0 ;
bool drawIndexed = false ;
u16 * inds = decIndex ;
TransformedVertex * drawBuffer = NULL ;
SoftwareTransformResult result ;
memset ( & result , 0 , sizeof ( result ) ) ;
2016-03-12 13:37:08 -08:00
SoftwareTransformParams params ;
memset ( & params , 0 , sizeof ( params ) ) ;
params . decoded = decoded ;
params . transformed = transformed ;
params . transformedExpanded = transformedExpanded ;
params . fbman = framebufferManager_ ;
params . texCache = textureCache_ ;
params . allowSeparateAlphaClear = true ;
2015-01-15 23:58:07 +01:00
int maxIndex = indexGen . MaxIndex ( ) ;
2014-09-17 22:03:44 +02:00
SoftwareTransform (
2016-03-12 13:37:08 -08:00
prim , indexGen . VertexCount ( ) ,
2015-01-15 12:26:35 -08:00
dec_ - > VertexType ( ) , inds , GE_VTYPE_IDX_16BIT , dec_ - > GetDecVtxFmt ( ) ,
2016-03-12 13:37:08 -08:00
maxIndex , drawBuffer , numTrans , drawIndexed , & params , & result ) ;
2014-09-17 22:03:44 +02:00
2015-09-13 06:53:25 -07:00
ApplyDrawStateLate ( ) ;
vshader = shaderManager_ - > ApplyShader ( prim , lastVType_ ) ;
2014-09-17 22:03:44 +02:00
if ( result . action = = SW_DRAW_PRIMITIVES ) {
if ( result . setStencil ) {
dxstate . stencilFunc . set ( D3DCMP_ALWAYS , result . stencilValue , 255 ) ;
2014-09-13 13:53:04 +02:00
}
2014-09-17 22:03:44 +02:00
// TODO: Add a post-transform cache here for multi-RECTANGLES only.
// Might help for text drawing.
// these spam the gDebugger log.
const int vertexSize = sizeof ( transformed [ 0 ] ) ;
pD3Ddevice - > SetVertexDeclaration ( pSoftVertexDecl ) ;
if ( drawIndexed ) {
2015-01-15 23:58:07 +01:00
pD3Ddevice - > DrawIndexedPrimitiveUP ( glprim [ prim ] , 0 , maxIndex , D3DPrimCount ( glprim [ prim ] , numTrans ) , inds , D3DFMT_INDEX16 , drawBuffer , sizeof ( TransformedVertex ) ) ;
2014-09-17 22:03:44 +02:00
} else {
pD3Ddevice - > DrawPrimitiveUP ( glprim [ prim ] , D3DPrimCount ( glprim [ prim ] , numTrans ) , drawBuffer , sizeof ( TransformedVertex ) ) ;
}
} else if ( result . action = = SW_CLEAR ) {
u32 clearColor = result . color ;
float clearDepth = result . depth ;
int mask = gstate . isClearModeColorMask ( ) ? D3DCLEAR_TARGET : 0 ;
if ( gstate . isClearModeAlphaMask ( ) ) mask | = D3DCLEAR_STENCIL ;
if ( gstate . isClearModeDepthMask ( ) ) mask | = D3DCLEAR_ZBUFFER ;
if ( mask & D3DCLEAR_ZBUFFER ) {
framebufferManager_ - > SetDepthUpdated ( ) ;
}
if ( mask & D3DCLEAR_TARGET ) {
2015-07-26 22:38:40 +02:00
framebufferManager_ - > SetColorUpdated ( gstate_c . skipDrawReason ) ;
2014-09-17 22:03:44 +02:00
}
dxstate . colorMask . set ( ( mask & D3DCLEAR_TARGET ) ! = 0 , ( mask & D3DCLEAR_TARGET ) ! = 0 , ( mask & D3DCLEAR_TARGET ) ! = 0 , ( mask & D3DCLEAR_STENCIL ) ! = 0 ) ;
pD3Ddevice - > Clear ( 0 , NULL , mask , clearColor , clearDepth , clearColor > > 24 ) ;
2013-08-21 11:10:56 +02:00
}
2014-09-17 22:03:44 +02:00
}
2014-10-19 20:25:04 +02:00
gpuStats . numDrawCalls + = numDrawCalls ;
gpuStats . numVertsSubmitted + = vertexCountInDrawCalls ;
2014-09-17 22:03:44 +02:00
indexGen . Reset ( ) ;
decodedVerts_ = 0 ;
numDrawCalls = 0 ;
vertexCountInDrawCalls = 0 ;
decodeCounter_ = 0 ;
dcid_ = 0 ;
prevPrim_ = GE_PRIM_INVALID ;
gstate_c . vertexFullAlpha = true ;
2015-07-26 22:38:40 +02:00
framebufferManager_ - > SetColorUpdated ( gstate_c . skipDrawReason ) ;
2013-08-17 11:23:51 +02:00
2015-03-15 21:38:01 -07:00
// Now seems as good a time as any to reset the min/max coords, which we may examine later.
2015-09-13 14:52:10 -07:00
gstate_c . vertBounds . minU = 512 ;
gstate_c . vertBounds . minV = 512 ;
gstate_c . vertBounds . maxU = 0 ;
gstate_c . vertBounds . maxV = 0 ;
2015-03-15 21:38:01 -07:00
2014-09-17 22:03:44 +02:00
host - > GPUNotifyDraw ( ) ;
2013-08-17 11:23:51 +02:00
}
2014-08-24 14:21:35 +02:00
2016-04-10 10:21:48 +02:00
void DrawEngineDX9 : : Resized ( ) {
2014-09-12 01:48:43 +02:00
decJitCache_ - > Clear ( ) ;
2014-09-11 23:30:42 -07:00
lastVType_ = - 1 ;
dec_ = NULL ;
for ( auto iter = decoderMap_ . begin ( ) ; iter ! = decoderMap_ . end ( ) ; iter + + ) {
delete iter - > second ;
}
decoderMap_ . clear ( ) ;
2014-09-12 01:48:43 +02:00
2014-09-14 14:04:09 -07:00
if ( g_Config . bPrescaleUV & & ! uvScale ) {
uvScale = new UVScale [ MAX_DEFERRED_DRAW_CALLS ] ;
} else if ( ! g_Config . bPrescaleUV & & uvScale ) {
delete uvScale ;
uvScale = 0 ;
2013-11-15 14:24:25 +01:00
}
2014-09-14 14:04:09 -07:00
}
2013-11-15 14:24:25 +01:00
2016-04-10 10:21:48 +02:00
bool DrawEngineDX9 : : IsCodePtrVertexDecoder ( const u8 * ptr ) const {
2014-09-14 14:04:09 -07:00
return decJitCache_ - > IsInSpace ( ptr ) ;
2013-11-15 14:24:25 +01:00
}
2013-09-15 08:53:21 -07:00
2013-11-15 14:24:25 +01:00
} // namespace