2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +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 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-01-11 18:03:16 +00:00
# include "base/timeutil.h"
2013-01-28 23:48:13 +00:00
# include "Common/MemoryUtil.h"
2012-11-01 15:19:01 +00:00
# include "../../Core/MemMap.h"
# include "../../Core/Host.h"
# include "../../Core/System.h"
2012-11-24 14:19:29 +00:00
# include "../../native/gfx_es2/gl_state.h"
2013-01-19 16:05:08 +00:00
# include "../../native/ext/cityhash/city.h"
2012-11-01 15:19:01 +00:00
# include "../Math3D.h"
# include "../GPUState.h"
# include "../ge_constants.h"
2013-01-10 11:51:18 +00:00
# include "../../Core/Config.h"
2012-11-01 15:19:01 +00:00
2012-11-24 14:19:29 +00:00
# include "StateMapping.h"
2012-11-01 15:19:01 +00:00
# include "TextureCache.h"
# include "TransformPipeline.h"
# include "VertexDecoder.h"
# include "ShaderManager.h"
2012-11-26 16:35:08 +00:00
# include "DisplayListInterpreter.h"
2012-11-01 15:19:01 +00:00
2012-12-21 15:49:42 +00:00
const GLuint glprim [ 8 ] = {
2012-11-01 15:19:01 +00:00
GL_POINTS ,
GL_LINES ,
GL_LINE_STRIP ,
GL_TRIANGLES ,
GL_TRIANGLE_STRIP ,
GL_TRIANGLE_FAN ,
2012-11-09 16:51:45 +00:00
GL_TRIANGLES , // 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?
2012-11-01 15:19:01 +00:00
} ;
2013-01-28 23:48:13 +00:00
enum {
DECODED_VERTEX_BUFFER_SIZE = 65536 * 48 ,
DECODED_INDEX_BUFFER_SIZE = 65536 * 2 ,
TRANSFORMED_VERTEX_BUFFER_SIZE = 65536 * sizeof ( TransformedVertex )
} ;
2012-12-25 14:28:34 +00:00
TransformDrawEngine : : TransformDrawEngine ( )
2013-01-19 16:05:08 +00:00
: numDrawCalls ( 0 ) ,
collectedVerts ( 0 ) ,
2013-01-19 18:22:15 +00:00
prevPrim_ ( - 1 ) ,
lastVType_ ( - 1 ) ,
2013-01-11 18:03:16 +00:00
curVbo_ ( 0 ) ,
2012-12-25 14:28:34 +00:00
shaderManager_ ( 0 ) {
2013-01-28 23:48:13 +00:00
// 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 ) ;
transformed = ( TransformedVertex * ) AllocateMemoryPages ( TRANSFORMED_VERTEX_BUFFER_SIZE ) ;
transformedExpanded = ( TransformedVertex * ) AllocateMemoryPages ( 3 * TRANSFORMED_VERTEX_BUFFER_SIZE ) ;
2013-01-11 18:03:16 +00:00
memset ( vbo_ , 0 , sizeof ( vbo_ ) ) ;
memset ( ebo_ , 0 , sizeof ( ebo_ ) ) ;
2012-12-25 12:47:59 +00:00
indexGen . Setup ( decIndex ) ;
2013-01-10 11:51:18 +00:00
InitDeviceObjects ( ) ;
register_gl_resource_holder ( this ) ;
2012-12-25 12:47:59 +00:00
}
2012-11-01 15:19:01 +00:00
2012-12-25 12:47:59 +00:00
TransformDrawEngine : : ~ TransformDrawEngine ( ) {
2013-01-10 11:51:18 +00:00
DestroyDeviceObjects ( ) ;
2013-01-28 23:48:13 +00:00
FreeMemoryPages ( decoded , DECODED_VERTEX_BUFFER_SIZE ) ;
FreeMemoryPages ( decIndex , DECODED_INDEX_BUFFER_SIZE ) ;
FreeMemoryPages ( transformed , TRANSFORMED_VERTEX_BUFFER_SIZE ) ;
FreeMemoryPages ( transformedExpanded , 3 * TRANSFORMED_VERTEX_BUFFER_SIZE ) ;
2013-01-10 11:51:18 +00:00
unregister_gl_resource_holder ( this ) ;
}
void TransformDrawEngine : : InitDeviceObjects ( ) {
2013-01-11 18:03:16 +00:00
if ( ! vbo_ [ 0 ] ) {
glGenBuffers ( NUM_VBOS , & vbo_ [ 0 ] ) ;
glGenBuffers ( NUM_VBOS , & ebo_ [ 0 ] ) ;
2013-01-10 11:51:18 +00:00
} else {
ERROR_LOG ( G3D , " Device objects already initialized! " ) ;
}
}
void TransformDrawEngine : : DestroyDeviceObjects ( ) {
2013-01-11 18:03:16 +00:00
glDeleteBuffers ( NUM_VBOS , & vbo_ [ 0 ] ) ;
glDeleteBuffers ( NUM_VBOS , & ebo_ [ 0 ] ) ;
memset ( vbo_ , 0 , sizeof ( vbo_ ) ) ;
memset ( ebo_ , 0 , sizeof ( ebo_ ) ) ;
2013-01-20 12:15:46 +00:00
ClearTrackedVertexArrays ( ) ;
2013-01-10 11:51:18 +00:00
}
void TransformDrawEngine : : GLLost ( ) {
// The objects have already been deleted.
2013-01-11 18:03:16 +00:00
memset ( vbo_ , 0 , sizeof ( vbo_ ) ) ;
memset ( ebo_ , 0 , sizeof ( ebo_ ) ) ;
2013-01-20 12:15:46 +00:00
ClearTrackedVertexArrays ( ) ;
2013-01-10 11:51:18 +00:00
InitDeviceObjects ( ) ;
2012-12-25 12:47:59 +00:00
}
// Just to get something on the screen, we'll just not subdivide correctly.
void TransformDrawEngine : : DrawBezier ( int ucount , int vcount ) {
u16 indices [ 3 * 3 * 6 ] ;
2012-12-28 18:33:26 +00:00
2013-01-04 08:54:19 +00:00
// Generate indices for a rectangular mesh.
2012-12-25 12:47:59 +00:00
int c = 0 ;
for ( int y = 0 ; y < 3 ; y + + ) {
for ( int x = 0 ; x < 3 ; x + + ) {
indices [ c + + ] = y * 4 + x ;
indices [ c + + ] = y * 4 + x + 1 ;
indices [ c + + ] = ( y + 1 ) * 4 + x + 1 ;
indices [ c + + ] = ( y + 1 ) * 4 + x + 1 ;
indices [ c + + ] = ( y + 1 ) * 4 + x ;
indices [ c + + ] = y * 4 + x ;
}
}
2013-01-04 08:54:19 +00:00
// We are free to use the "decoded" buffer here.
// Let's split it into two to get a second buffer, there's enough space.
u8 * decoded2 = decoded + 65536 * 24 ;
// Alright, now for the vertex data.
// For now, we will simply inject UVs.
float customUV [ 4 * 4 * 2 ] ;
2012-12-25 12:47:59 +00:00
for ( int y = 0 ; y < 4 ; y + + ) {
for ( int x = 0 ; x < 4 ; x + + ) {
customUV [ ( y * 4 + x ) * 2 + 0 ] = ( float ) x / 3.0f ;
customUV [ ( y * 4 + x ) * 2 + 1 ] = ( float ) y / 3.0f ;
}
}
2013-01-04 08:54:19 +00:00
if ( ! ( gstate . vertType & GE_VTYPE_TC_MASK ) ) {
dec . SetVertexType ( gstate . vertType ) ;
u32 newVertType = dec . InjectUVs ( decoded2 , Memory : : GetPointer ( gstate_c . vertexAddr ) , customUV , 16 ) ;
SubmitPrim ( decoded2 , & indices [ 0 ] , GE_PRIM_TRIANGLES , c , newVertType , GE_VTYPE_IDX_16BIT , 0 ) ;
} else {
SubmitPrim ( Memory : : GetPointer ( gstate_c . vertexAddr ) , & indices [ 0 ] , GE_PRIM_TRIANGLES , c , gstate . vertType , GE_VTYPE_IDX_16BIT , 0 ) ;
}
2013-01-19 16:05:08 +00:00
Flush ( ) ; // as our vertex storage here is temporary, it will only survive one draw.
2012-12-28 18:33:26 +00:00
}
void TransformDrawEngine : : DrawSpline ( int ucount , int vcount , int utype , int vtype ) {
// TODO
2012-12-25 12:47:59 +00:00
}
2012-11-09 16:51:45 +00:00
2012-11-16 14:16:14 +00:00
// Convenient way to do precomputation to save the parts of the lighting calculation
// that's common between the many vertices of a draw call.
class Lighter {
public :
Lighter ( ) ;
void Light ( float colorOut0 [ 4 ] , float colorOut1 [ 4 ] , const float colorIn [ 4 ] , Vec3 pos , Vec3 normal , float dots [ 4 ] ) ;
private :
bool disabled_ ;
Color4 globalAmbient ;
Color4 materialEmissive ;
Color4 materialAmbient ;
Color4 materialDiffuse ;
Color4 materialSpecular ;
float specCoef_ ;
2012-12-20 17:31:21 +00:00
// Vec3 viewer_;
2012-11-16 14:16:14 +00:00
bool doShadeMapping_ ;
int materialUpdate_ ;
} ;
2012-11-01 15:19:01 +00:00
2012-11-16 14:16:14 +00:00
Lighter : : Lighter ( ) {
disabled_ = false ;
doShadeMapping_ = ( gstate . texmapmode & 0x3 ) = = 2 ;
if ( ! doShadeMapping_ & & ! ( gstate . lightEnable [ 0 ] & 1 ) & & ! ( gstate . lightEnable [ 1 ] & 1 ) & & ! ( gstate . lightEnable [ 2 ] & 1 ) & & ! ( gstate . lightEnable [ 3 ] & 1 ) )
2012-11-01 15:19:01 +00:00
{
2012-11-16 14:16:14 +00:00
disabled_ = true ;
2012-11-01 15:19:01 +00:00
}
2012-11-16 14:16:14 +00:00
materialEmissive . GetFromRGB ( gstate . materialemissive ) ;
materialEmissive . a = 0.0f ;
2012-11-01 15:19:01 +00:00
globalAmbient . GetFromRGB ( gstate . ambientcolor ) ;
globalAmbient . GetFromA ( gstate . ambientalpha ) ;
2012-11-16 14:16:14 +00:00
materialAmbient . GetFromRGB ( gstate . materialambient ) ;
materialAmbient . a = 1.0f ;
materialDiffuse . GetFromRGB ( gstate . materialdiffuse ) ;
materialDiffuse . a = 1.0f ;
materialSpecular . GetFromRGB ( gstate . materialspecular ) ;
materialSpecular . a = 1.0f ;
specCoef_ = getFloat24 ( gstate . materialspecularcoef ) ;
2012-12-20 17:31:21 +00:00
// viewer_ = Vec3(-gstate.viewMatrix[9], -gstate.viewMatrix[10], -gstate.viewMatrix[11]);
2012-11-16 14:16:14 +00:00
materialUpdate_ = gstate . materialupdate & 7 ;
}
void Lighter : : Light ( float colorOut0 [ 4 ] , float colorOut1 [ 4 ] , const float colorIn [ 4 ] , Vec3 pos , Vec3 normal , float dots [ 4 ] )
{
if ( disabled_ ) {
memcpy ( colorOut0 , colorIn , sizeof ( float ) * 4 ) ;
memset ( colorOut1 , 0 , sizeof ( float ) * 4 ) ;
return ;
}
2012-11-01 15:19:01 +00:00
Vec3 norm = normal . Normalized ( ) ;
Color4 in ( colorIn ) ;
2012-11-16 14:16:14 +00:00
const Color4 * ambient ;
if ( materialUpdate_ & 1 )
ambient = & in ;
2012-11-01 15:19:01 +00:00
else
2012-11-16 14:16:14 +00:00
ambient = & materialAmbient ;
2012-11-01 15:19:01 +00:00
2012-11-16 14:16:14 +00:00
const Color4 * diffuse ;
if ( materialUpdate_ & 2 )
diffuse = & in ;
2012-11-01 15:19:01 +00:00
else
2012-11-16 14:16:14 +00:00
diffuse = & materialDiffuse ;
2012-11-01 15:19:01 +00:00
2012-11-16 14:16:14 +00:00
const Color4 * specular ;
if ( materialUpdate_ & 4 )
specular = & in ;
2012-11-01 15:19:01 +00:00
else
2012-11-16 14:16:14 +00:00
specular = & materialSpecular ;
2012-12-05 03:45:28 +00:00
2012-11-16 14:16:14 +00:00
Color4 lightSum0 = globalAmbient * * ambient + materialEmissive ;
2012-12-05 03:45:28 +00:00
Color4 lightSum1 ( 0 , 0 , 0 , 0 ) ;
2012-11-01 15:19:01 +00:00
for ( int l = 0 ; l < 4 ; l + + )
{
// can we skip this light?
2012-11-16 14:16:14 +00:00
if ( ( gstate . lightEnable [ l ] & 1 ) = = 0 & & ! doShadeMapping_ )
2012-11-11 18:00:44 +00:00
continue ;
2012-11-01 15:19:01 +00:00
2012-12-05 03:45:28 +00:00
GELightComputation comp = ( GELightComputation ) ( gstate . ltype [ l ] & 3 ) ;
GELightType type = ( GELightType ) ( ( gstate . ltype [ l ] > > 8 ) & 3 ) ;
2012-11-01 15:19:01 +00:00
Vec3 toLight ;
if ( type = = GE_LIGHTTYPE_DIRECTIONAL )
2012-11-18 12:04:49 +00:00
toLight = Vec3 ( gstate_c . lightpos [ l ] ) ; // lightdir is for spotlights
2012-11-01 15:19:01 +00:00
else
2012-11-18 12:04:49 +00:00
toLight = Vec3 ( gstate_c . lightpos [ l ] ) - pos ;
2012-11-01 15:19:01 +00:00
bool doSpecular = ( comp ! = GE_LIGHTCOMP_ONLYDIFFUSE ) ;
bool poweredDiffuse = comp = = GE_LIGHTCOMP_BOTHWITHPOWDIFFUSE ;
float dot = toLight * norm ;
// Clamp dot to zero.
if ( dot < 0.0f ) dot = 0.0f ;
if ( poweredDiffuse )
2012-11-16 14:16:14 +00:00
dot = powf ( dot , specCoef_ ) ;
2012-11-01 15:19:01 +00:00
2012-12-20 13:10:42 +00:00
float lightScale = 1.0f ;
2012-12-20 22:28:58 +00:00
float distance = toLight . Normalize ( ) ;
2012-12-20 13:10:42 +00:00
if ( type ! = GE_LIGHTTYPE_DIRECTIONAL )
{
lightScale = 1.0f / ( gstate_c . lightatt [ l ] [ 0 ] + gstate_c . lightatt [ l ] [ 1 ] * distance + gstate_c . lightatt [ l ] [ 2 ] * distance * distance ) ;
if ( lightScale > 1.0f ) lightScale = 1.0f ;
}
2012-12-21 15:49:42 +00:00
Color4 lightDiff ( gstate_c . lightColor [ 1 ] [ l ] , 0.0f ) ;
2013-02-10 13:01:18 +00:00
Color4 diff = ( lightDiff * * diffuse ) * ( std : : max ( dot , 0.0f ) * lightScale ) ;
2012-11-01 15:19:01 +00:00
2012-11-09 16:51:45 +00:00
// Real PSP specular
Vec3 toViewer ( 0 , 0 , 1 ) ;
// Better specular
2012-12-05 03:45:28 +00:00
// Vec3 toViewer = (viewer - pos).Normalized();
2012-11-09 16:51:45 +00:00
2012-11-01 15:19:01 +00:00
if ( doSpecular )
{
Vec3 halfVec = toLight ;
2012-11-09 16:51:45 +00:00
halfVec + = toViewer ;
2012-11-01 15:19:01 +00:00
halfVec . Normalize ( ) ;
dot = halfVec * norm ;
2013-02-06 18:01:10 +00:00
if ( dot > 0 )
2012-11-01 15:19:01 +00:00
{
2012-12-21 15:49:42 +00:00
Color4 lightSpec ( gstate_c . lightColor [ 2 ] [ l ] , 0.0f ) ;
2013-02-06 18:01:10 +00:00
lightSum1 + = ( lightSpec * * specular * ( powf ( dot , specCoef_ ) * lightScale ) ) ;
2012-12-05 03:45:28 +00:00
}
2012-11-01 15:19:01 +00:00
}
dots [ l ] = dot ;
if ( gstate . lightEnable [ l ] & 1 )
{
2013-01-01 05:44:40 +00:00
Color4 lightAmbient ( gstate_c . lightColor [ 0 ] [ l ] , 1.0f ) ;
2012-12-21 15:49:42 +00:00
lightSum0 + = lightAmbient * * ambient + diff ;
2012-11-01 15:19:01 +00:00
}
}
2012-11-16 14:16:14 +00:00
// 4?
for ( int i = 0 ; i < 4 ; i + + ) {
2012-11-17 14:06:10 +00:00
colorOut0 [ i ] = lightSum0 [ i ] > 1.0f ? 1.0f : lightSum0 [ i ] ;
colorOut1 [ i ] = lightSum1 [ i ] > 1.0f ? 1.0f : lightSum1 [ i ] ;
2012-11-16 14:16:14 +00:00
}
2012-11-01 15:19:01 +00:00
}
2012-12-20 13:10:42 +00:00
struct GlTypeInfo {
2013-02-04 22:09:01 +00:00
u16 type ;
u8 count ;
u8 normalized ;
2012-12-20 13:10:42 +00:00
} ;
2012-12-19 17:35:37 +00:00
2013-02-04 22:09:01 +00:00
static const GlTypeInfo GLComp [ ] = {
2012-12-20 13:10:42 +00:00
{ 0 } , // DEC_NONE,
{ GL_FLOAT , 1 , GL_FALSE } , // DEC_FLOAT_1,
{ GL_FLOAT , 2 , GL_FALSE } , // DEC_FLOAT_2,
{ GL_FLOAT , 3 , GL_FALSE } , // DEC_FLOAT_3,
{ GL_FLOAT , 4 , GL_FALSE } , // DEC_FLOAT_4,
2013-01-22 20:57:47 +00:00
{ GL_BYTE , 4 , GL_TRUE } , // DEC_S8_3,
{ GL_SHORT , 4 , GL_TRUE } , // DEC_S16_3,
2013-02-04 22:09:01 +00:00
{ GL_UNSIGNED_BYTE , 1 , GL_TRUE } , // DEC_U8_1,
{ GL_UNSIGNED_BYTE , 2 , GL_TRUE } , // DEC_U8_2,
2013-01-28 23:48:13 +00:00
{ GL_UNSIGNED_BYTE , 3 , GL_TRUE } , // DEC_U8_3,
2013-02-04 22:09:01 +00:00
{ GL_UNSIGNED_BYTE , 4 , GL_TRUE } , // DEC_U8_4,
2013-02-05 17:00:21 +00:00
{ GL_UNSIGNED_SHORT , 1 , GL_TRUE } , // DEC_U16_1,
2013-02-05 00:37:00 +00:00
{ GL_UNSIGNED_SHORT , 2 , GL_TRUE } , // DEC_U16_2,
2013-02-05 17:00:21 +00:00
{ GL_UNSIGNED_SHORT , 3 , GL_TRUE } , // DEC_U16_3,
{ GL_UNSIGNED_SHORT , 4 , GL_TRUE } , // DEC_U16_4,
2013-02-06 20:38:19 +00:00
{ GL_UNSIGNED_BYTE , 2 , GL_FALSE } , // DEC_U8A_2,
2013-02-05 00:37:00 +00:00
{ GL_UNSIGNED_SHORT , 2 , GL_FALSE } , // DEC_U16A_2,
2012-12-20 13:10:42 +00:00
} ;
2012-12-19 17:35:37 +00:00
2012-12-20 13:10:42 +00:00
static inline void VertexAttribSetup ( int attrib , int fmt , int stride , u8 * ptr ) {
if ( attrib ! = - 1 & & fmt ) {
const GlTypeInfo & type = GLComp [ fmt ] ;
glVertexAttribPointer ( attrib , type . count , type . type , type . normalized , stride , ptr ) ;
2012-12-05 03:45:28 +00:00
}
2012-12-20 13:10:42 +00:00
}
2012-12-05 03:45:28 +00:00
2012-12-20 13:10:42 +00:00
// TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets
static void SetupDecFmtForDraw ( LinkedShader * program , const DecVtxFormat & decFmt , u8 * vertexData ) {
VertexAttribSetup ( program - > a_weight0123 , decFmt . w0fmt , decFmt . stride , vertexData + decFmt . w0off ) ;
VertexAttribSetup ( program - > a_weight4567 , decFmt . w1fmt , decFmt . stride , vertexData + decFmt . w1off ) ;
VertexAttribSetup ( program - > a_texcoord , decFmt . uvfmt , decFmt . stride , vertexData + decFmt . uvoff ) ;
VertexAttribSetup ( program - > a_color0 , decFmt . c0fmt , decFmt . stride , vertexData + decFmt . c0off ) ;
VertexAttribSetup ( program - > a_color1 , decFmt . c1fmt , decFmt . stride , vertexData + decFmt . c1off ) ;
VertexAttribSetup ( program - > a_normal , decFmt . nrmfmt , decFmt . stride , vertexData + decFmt . nrmoff ) ;
VertexAttribSetup ( program - > a_position , decFmt . posfmt , decFmt . stride , vertexData + decFmt . posoff ) ;
}
2012-12-26 07:54:33 +00:00
// The verts are in the order: BR BL TL TR
static void SwapUVs ( TransformedVertex & a , TransformedVertex & b ) {
float tempu = a . u ;
float tempv = a . v ;
a . u = b . u ;
a . v = b . v ;
b . u = tempu ;
b . v = tempv ;
}
2013-01-07 08:52:47 +00:00
2012-12-26 07:54:33 +00:00
// 2 3 3 2 0 3 2 1
// to to or
// 1 0 0 1 1 2 3 0
2013-02-03 20:46:31 +00:00
// See comment below where this was called before.
/*
2013-02-03 11:27:52 +00:00
static void RotateUV ( TransformedVertex v [ 4 ] ) {
float x1 = v [ 2 ] . x ;
float x2 = v [ 0 ] . x ;
float y1 = v [ 2 ] . y ;
float y2 = v [ 0 ] . y ;
if ( ( x1 < x2 & & y1 < y2 ) | | ( x1 > x2 & & y1 > y2 ) )
SwapUVs ( v [ 1 ] , v [ 3 ] ) ;
2013-02-03 20:46:31 +00:00
} */
2013-02-03 11:27:52 +00:00
static void RotateUVThrough ( TransformedVertex v [ 4 ] ) {
float x1 = v [ 2 ] . x ;
float x2 = v [ 0 ] . x ;
float y1 = v [ 2 ] . y ;
float y2 = v [ 0 ] . y ;
if ( ( x1 < x2 & & y1 > y2 ) | | ( x1 > x2 & & y1 < y2 ) )
2012-12-26 07:54:33 +00:00
SwapUVs ( v [ 1 ] , v [ 3 ] ) ;
}
2012-12-21 15:49:42 +00:00
// This is the software transform pipeline, which is necessary for supporting RECT
// primitives correctly, and may be easier to use for debugging than the hardware
// transform pipeline.
// There's code here that simply expands transformed RECTANGLES into plain triangles.
// We're gonna have to keep software transforming RECTANGLES, unless we use a geom shader which we can't on OpenGL ES 2.0.
// Usually, though, these primitives don't use lighting etc so it's no biggie performance wise, but it would be nice to get rid of
// this code.
// Actually, if we find the camera-relative right and down vectors, it might even be possible to add the extra points in pre-transformed
// space and thus make decent use of hardware transform.
// Actually again, single quads could be drawn more efficiently using GL_TRIANGLE_STRIP, no need to duplicate verts as for
// GL_TRIANGLES. Still need to sw transform to compute the extra two corners though.
2012-12-25 12:56:30 +00:00
void TransformDrawEngine : : SoftwareTransformAndDraw (
2012-12-28 18:33:26 +00:00
int prim , u8 * decoded , LinkedShader * program , int vertexCount , u32 vertType , void * inds , int indexType , const DecVtxFormat & decVtxFormat , int maxIndex ) {
2012-11-01 15:19:01 +00:00
2012-12-28 18:33:26 +00:00
bool throughmode = ( vertType & GE_VTYPE_THROUGH_MASK ) ! = 0 ;
2012-12-20 13:10:42 +00:00
2012-11-26 03:25:14 +00:00
// TODO: Split up into multiple draw calls for GLES 2.0 where you can't guarantee support for more than 0x10000 verts.
2012-11-01 15:19:01 +00:00
2012-11-26 03:25:14 +00:00
# if defined(USING_GLES2)
2012-11-01 15:19:01 +00:00
if ( vertexCount > 0x10000 / 3 )
vertexCount = 0x10000 / 3 ;
# endif
2013-02-05 00:37:00 +00:00
float uscale = 1.0f ;
float vscale = 1.0f ;
if ( throughmode ) {
uscale / = gstate_c . curTextureWidth ;
vscale / = gstate_c . curTextureHeight ;
}
2012-11-16 14:16:14 +00:00
Lighter lighter ;
2013-01-14 19:43:18 +00:00
float fog_end = getFloat24 ( gstate . fog1 ) ;
float fog_slope = getFloat24 ( gstate . fog2 ) ;
2012-11-16 14:16:14 +00:00
2012-12-20 13:10:42 +00:00
VertexReader reader ( decoded , decVtxFormat ) ;
2012-12-25 12:56:30 +00:00
for ( int index = 0 ; index < maxIndex ; index + + ) {
2012-12-19 19:21:59 +00:00
reader . Goto ( index ) ;
2012-11-16 14:16:14 +00:00
float v [ 3 ] = { 0 , 0 , 0 } ;
float c0 [ 4 ] = { 1 , 1 , 1 , 1 } ;
float c1 [ 4 ] = { 0 , 0 , 0 , 0 } ;
float uv [ 2 ] = { 0 , 0 } ;
2013-01-14 19:43:18 +00:00
float fogCoef = 1.0f ;
2012-11-01 15:19:01 +00:00
2012-12-26 07:54:33 +00:00
if ( throughmode ) {
2012-11-01 15:19:01 +00:00
// Do not touch the coordinates or the colors. No lighting.
2012-12-19 19:21:59 +00:00
reader . ReadPos ( v ) ;
if ( reader . hasColor0 ( ) ) {
reader . ReadColor0 ( c0 ) ;
for ( int j = 0 ; j < 4 ; j + + ) {
2012-11-25 16:45:50 +00:00
c1 [ j ] = 0.0f ;
}
2012-12-26 07:54:33 +00:00
} else {
2013-01-10 14:21:28 +00:00
c0 [ 0 ] = ( gstate . materialambient & 0xFF ) / 255.f ;
2013-01-01 15:12:45 +00:00
c0 [ 1 ] = ( ( gstate . materialambient > > 8 ) & 0xFF ) / 255.f ;
2013-01-10 14:21:28 +00:00
c0 [ 2 ] = ( ( gstate . materialambient > > 16 ) & 0xFF ) / 255.f ;
2012-11-25 16:45:50 +00:00
c0 [ 3 ] = ( gstate . materialalpha & 0xFF ) / 255.f ;
2012-11-16 14:16:14 +00:00
}
2012-12-19 19:21:59 +00:00
if ( reader . hasUV ( ) ) {
reader . ReadUV ( uv ) ;
2013-02-05 00:37:00 +00:00
uv [ 0 ] * = uscale ;
uv [ 1 ] * = vscale ;
2012-12-19 19:21:59 +00:00
}
2013-01-14 19:43:18 +00:00
fogCoef = 1.0f ;
2012-12-19 19:21:59 +00:00
// Scale UV?
2012-12-26 07:54:33 +00:00
} else {
2012-11-09 16:51:45 +00:00
// We do software T&L for now
2012-11-01 15:19:01 +00:00
float out [ 3 ] , norm [ 3 ] ;
2012-12-20 17:31:21 +00:00
float pos [ 3 ] , nrm [ 3 ] = { 0 } ;
2012-12-19 19:21:59 +00:00
reader . ReadPos ( pos ) ;
if ( reader . hasNormal ( ) )
reader . ReadNrm ( nrm ) ;
2012-12-28 18:33:26 +00:00
if ( ( vertType & GE_VTYPE_WEIGHT_MASK ) = = GE_VTYPE_WEIGHT_NONE ) {
2012-12-19 19:21:59 +00:00
Vec3ByMatrix43 ( out , pos , gstate . worldMatrix ) ;
if ( reader . hasNormal ( ) ) {
Norm3ByMatrix43 ( norm , nrm , gstate . worldMatrix ) ;
} else {
memset ( norm , 0 , 12 ) ;
}
2012-12-26 07:54:33 +00:00
} else {
2012-12-19 19:21:59 +00:00
float weights [ 8 ] ;
reader . ReadWeights ( weights ) ;
2012-11-10 09:15:11 +00:00
// Skinning
2012-11-01 15:19:01 +00:00
Vec3 psum ( 0 , 0 , 0 ) ;
Vec3 nsum ( 0 , 0 , 0 ) ;
2012-12-28 18:33:26 +00:00
int nweights = ( ( vertType & GE_VTYPE_WEIGHTCOUNT_MASK ) > > GE_VTYPE_WEIGHTCOUNT_SHIFT ) + 1 ;
2012-11-11 18:03:48 +00:00
for ( int i = 0 ; i < nweights ; i + + )
2012-11-01 15:19:01 +00:00
{
2012-12-19 19:21:59 +00:00
if ( weights [ i ] ! = 0.0f ) {
Vec3ByMatrix43 ( out , pos , gstate . boneMatrix + i * 12 ) ;
Vec3 tpos ( out ) ;
psum + = tpos * weights [ i ] ;
if ( reader . hasNormal ( ) ) {
Norm3ByMatrix43 ( norm , nrm , gstate . boneMatrix + i * 12 ) ;
Vec3 tnorm ( norm ) ;
nsum + = tnorm * weights [ i ] ;
}
2012-11-11 18:00:44 +00:00
}
2012-11-01 15:19:01 +00:00
}
2012-12-25 12:47:59 +00:00
2012-12-20 13:10:42 +00:00
// Yes, we really must multiply by the world matrix too.
2012-11-11 16:54:13 +00:00
Vec3ByMatrix43 ( out , psum . v , gstate . worldMatrix ) ;
2012-12-19 19:21:59 +00:00
if ( reader . hasNormal ( ) ) {
Norm3ByMatrix43 ( norm , nsum . v , gstate . worldMatrix ) ;
}
2012-11-01 15:19:01 +00:00
}
// Perform lighting here if enabled. don't need to check through, it's checked above.
float dots [ 4 ] = { 0 , 0 , 0 , 0 } ;
2012-12-19 19:21:59 +00:00
float unlitColor [ 4 ] = { 1 , 1 , 1 , 1 } ;
if ( reader . hasColor0 ( ) ) {
reader . ReadColor0 ( unlitColor ) ;
2012-12-20 22:28:58 +00:00
} else {
2013-01-10 14:21:28 +00:00
unlitColor [ 0 ] = ( gstate . materialambient & 0xFF ) / 255.f ;
2013-01-01 15:12:45 +00:00
unlitColor [ 1 ] = ( ( gstate . materialambient > > 8 ) & 0xFF ) / 255.f ;
2013-01-10 14:21:28 +00:00
unlitColor [ 2 ] = ( ( gstate . materialambient > > 16 ) & 0xFF ) / 255.f ;
2012-12-20 22:28:58 +00:00
unlitColor [ 3 ] = ( gstate . materialalpha & 0xFF ) / 255.f ;
2012-11-26 19:38:26 +00:00
}
float litColor0 [ 4 ] ;
float litColor1 [ 4 ] ;
lighter . Light ( litColor0 , litColor1 , unlitColor , out , norm , dots ) ;
2012-12-05 03:45:28 +00:00
2012-12-26 07:54:33 +00:00
if ( gstate . lightingEnable & 1 ) {
2012-12-20 13:10:42 +00:00
// Don't ignore gstate.lmode - we should send two colors in that case
2012-11-26 19:38:26 +00:00
if ( gstate . lmode & 1 ) {
// Separate colors
for ( int j = 0 ; j < 4 ; j + + ) {
c0 [ j ] = litColor0 [ j ] ;
c1 [ j ] = litColor1 [ j ] ;
2012-11-16 14:16:14 +00:00
}
2012-11-26 19:38:26 +00:00
} else {
// Summed color into c0
for ( int j = 0 ; j < 4 ; j + + ) {
c0 [ j ] = litColor0 [ j ] + litColor1 [ j ] ;
c1 [ j ] = 0.0f ;
2012-11-16 14:16:14 +00:00
}
2012-11-01 15:19:01 +00:00
}
2012-12-26 07:54:33 +00:00
} else {
2012-12-20 13:10:42 +00:00
if ( reader . hasColor0 ( ) ) {
2012-11-26 19:38:26 +00:00
for ( int j = 0 ; j < 4 ; j + + ) {
c0 [ j ] = unlitColor [ j ] ;
c1 [ j ] = 0.0f ;
}
} else {
2013-01-20 04:41:47 +00:00
c0 [ 0 ] = ( gstate . materialambient & 0xFF ) / 255.f ;
c0 [ 1 ] = ( ( gstate . materialambient > > 8 ) & 0xFF ) / 255.f ;
c0 [ 2 ] = ( ( gstate . materialambient > > 16 ) & 0xFF ) / 255.f ;
2012-11-26 19:38:26 +00:00
c0 [ 3 ] = ( gstate . materialalpha & 0xFF ) / 255.f ;
2013-01-22 20:57:47 +00:00
memset ( c1 , 0 , sizeof ( c1 ) ) ;
2012-11-16 14:16:14 +00:00
}
2012-11-01 15:19:01 +00:00
}
2012-12-21 18:16:17 +00:00
if ( reader . hasUV ( ) ) {
2012-12-19 19:21:59 +00:00
float ruv [ 2 ] ;
reader . ReadUV ( ruv ) ;
2012-11-01 15:19:01 +00:00
// Perform texture coordinate generation after the transform and lighting - one style of UV depends on lights.
2012-12-20 13:10:42 +00:00
switch ( gstate . getUVGenMode ( ) )
2012-11-01 15:19:01 +00:00
{
case 0 : // UV mapping
// Texture scale/offset is only performed in this mode.
2013-02-05 00:37:00 +00:00
uv [ 0 ] = uscale * ( ruv [ 0 ] * gstate_c . uScale + gstate_c . uOff ) ;
uv [ 1 ] = vscale * ( ruv [ 1 ] * gstate_c . vScale + gstate_c . vOff ) ;
2012-11-01 15:19:01 +00:00
break ;
case 1 :
{
// Projection mapping
Vec3 source ;
2012-12-20 13:10:42 +00:00
switch ( gstate . getUVProjMode ( ) )
2012-11-01 15:19:01 +00:00
{
case 0 : // Use model space XYZ as source
2012-12-19 19:21:59 +00:00
source = pos ;
2012-11-01 15:19:01 +00:00
break ;
case 1 : // Use unscaled UV as source
2012-12-19 19:21:59 +00:00
source = Vec3 ( ruv [ 0 ] , ruv [ 1 ] , 0.0f ) ;
2012-11-01 15:19:01 +00:00
break ;
case 2 : // Use normalized normal as source
source = Vec3 ( norm ) . Normalized ( ) ;
break ;
case 3 : // Use non-normalized normal as source!
source = Vec3 ( norm ) ;
break ;
}
2012-12-19 19:21:59 +00:00
2012-11-01 15:19:01 +00:00
float uvw [ 3 ] ;
Vec3ByMatrix43 ( uvw , & source . x , gstate . tgenMatrix ) ;
uv [ 0 ] = uvw [ 0 ] ;
uv [ 1 ] = uvw [ 1 ] ;
}
break ;
case 2 :
2012-12-21 15:49:42 +00:00
// Shade mapping - use dot products from light sources to generate U and V.
2012-11-01 15:19:01 +00:00
{
2012-12-20 13:10:42 +00:00
uv [ 0 ] = dots [ gstate . getUVLS0 ( ) ] ;
uv [ 1 ] = dots [ gstate . getUVLS1 ( ) ] ;
2012-11-01 15:19:01 +00:00
}
break ;
case 3 :
// Illegal
break ;
}
}
2012-11-10 09:15:11 +00:00
2012-11-16 14:16:14 +00:00
// Transform the coord by the view matrix.
2012-11-01 15:19:01 +00:00
Vec3ByMatrix43 ( v , out , gstate . viewMatrix ) ;
2013-01-14 19:43:18 +00:00
fogCoef = ( v [ 2 ] + fog_end ) * fog_slope ;
2012-11-01 15:19:01 +00:00
}
2012-12-19 19:21:59 +00:00
2012-12-21 15:49:42 +00:00
// TODO: Write to a flexible buffer, we don't always need all four components.
2012-11-16 15:19:37 +00:00
memcpy ( & transformed [ index ] . x , v , 3 * sizeof ( float ) ) ;
2013-01-14 19:43:18 +00:00
transformed [ index ] . fog = fogCoef ;
2012-12-26 07:54:33 +00:00
memcpy ( & transformed [ index ] . u , uv , 2 * sizeof ( float ) ) ;
2013-02-05 00:37:00 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
transformed [ index ] . color0 [ i ] = c0 [ i ] * 255.0f ;
}
for ( int i = 0 ; i < 4 ; i + + ) {
transformed [ index ] . color1 [ i ] = c1 [ i ] * 255.0f ;
}
2012-11-16 15:19:37 +00:00
}
2012-11-01 15:19:01 +00:00
2012-12-21 15:49:42 +00:00
// Step 2: expand rectangles.
2012-11-16 15:19:37 +00:00
const TransformedVertex * drawBuffer = transformed ;
int numTrans = 0 ;
2012-11-16 15:43:16 +00:00
bool drawIndexed = false ;
if ( prim ! = GE_PRIM_RECTANGLES ) {
2012-11-16 15:19:37 +00:00
// We can simply draw the unexpanded buffer.
numTrans = vertexCount ;
2012-12-21 17:46:15 +00:00
drawIndexed = true ;
2012-11-16 15:19:37 +00:00
} else {
2013-01-04 08:54:19 +00:00
// Temporary storage for RECTANGLES emulation
float v2 [ 3 ] = { 0 } ;
float uv2 [ 2 ] = { 0 } ;
2012-11-16 15:19:37 +00:00
numTrans = 0 ;
drawBuffer = transformedExpanded ;
TransformedVertex * trans = & transformedExpanded [ 0 ] ;
TransformedVertex saved ;
for ( int i = 0 ; i < vertexCount ; i + + ) {
2012-12-21 17:46:15 +00:00
int index = ( ( u16 * ) inds ) [ i ] ;
2012-11-01 15:19:01 +00:00
2012-11-16 15:19:37 +00:00
TransformedVertex & transVtx = transformed [ index ] ;
2012-11-16 15:43:16 +00:00
if ( ( i & 1 ) = = 0 )
2012-11-16 15:19:37 +00:00
{
2012-11-16 15:43:16 +00:00
// Save this vertex so we can generate when we get the next one. Color is taken from the last vertex.
saved = transVtx ;
2012-11-16 15:19:37 +00:00
}
2012-11-16 15:43:16 +00:00
else
2012-11-16 15:19:37 +00:00
{
2012-11-16 15:43:16 +00:00
// We have to turn the rectangle into two triangles, so 6 points. Sigh.
2012-11-22 22:07:15 +00:00
// bottom right
2012-12-26 07:54:33 +00:00
trans [ 0 ] = transVtx ;
// bottom left
trans [ 1 ] = transVtx ;
trans [ 1 ] . y = saved . y ;
trans [ 1 ] . v = saved . v ;
2012-11-16 15:43:16 +00:00
2012-11-22 22:07:15 +00:00
// top left
2012-12-26 07:54:33 +00:00
trans [ 2 ] = transVtx ;
trans [ 2 ] . x = saved . x ;
trans [ 2 ] . y = saved . y ;
trans [ 2 ] . u = saved . u ;
trans [ 2 ] . v = saved . v ;
2012-11-16 15:43:16 +00:00
// top right
2012-12-26 07:54:33 +00:00
trans [ 3 ] = transVtx ;
trans [ 3 ] . x = saved . x ;
trans [ 3 ] . u = saved . u ;
2012-11-16 16:19:28 +00:00
2012-12-26 07:54:33 +00:00
// That's the four corners. Now process UV rotation.
2013-02-03 11:27:52 +00:00
if ( throughmode )
RotateUVThrough ( trans ) ;
2013-02-03 20:46:31 +00:00
// Apparently, non-through RotateUV just breaks things.
// If we find a game where it helps, we'll just have to figure out how they differ.
// Possibly, it has something to do with flipped viewport Y axis, which a few games use.
// else
// RotateUV(trans);
2012-11-16 15:43:16 +00:00
// bottom right
2012-12-26 07:54:33 +00:00
trans [ 4 ] = trans [ 0 ] ;
2012-11-16 15:43:16 +00:00
// top left
2012-12-26 07:54:33 +00:00
trans [ 5 ] = trans [ 2 ] ;
trans + = 6 ;
2012-11-16 15:43:16 +00:00
numTrans + = 6 ;
2012-11-01 15:19:01 +00:00
}
}
}
2013-01-20 12:15:46 +00:00
// TODO: Add a post-transform cache here for multi-RECTANGLES only.
// Might help for text drawing.
2012-12-21 15:49:42 +00:00
// these spam the gDebugger log.
2012-12-19 14:14:41 +00:00
const int vertexSize = sizeof ( transformed [ 0 ] ) ;
2013-01-10 11:51:18 +00:00
2013-01-10 12:08:04 +00:00
bool useVBO = g_Config . bUseVBO ;
if ( useVBO ) {
2013-01-12 10:33:04 +00:00
//char title[64];
//sprintf(title, "upload %i verts for sw", indexGen.VertexCount());
//LoggingDeadline deadline(title, 5);
2013-01-11 18:03:16 +00:00
glBindBuffer ( GL_ARRAY_BUFFER , vbo_ [ curVbo_ ] ) ;
2013-01-26 20:38:27 +00:00
glBufferData ( GL_ARRAY_BUFFER , vertexSize * numTrans , drawBuffer , GL_STREAM_DRAW ) ;
2013-01-10 12:08:04 +00:00
drawBuffer = 0 ; // so that the calls use offsets instead.
}
2013-01-14 19:43:18 +00:00
glVertexAttribPointer ( program - > a_position , 4 , GL_FLOAT , GL_FALSE , vertexSize , drawBuffer ) ;
if ( program - > a_texcoord ! = - 1 ) glVertexAttribPointer ( program - > a_texcoord , 2 , GL_FLOAT , GL_FALSE , vertexSize , ( ( uint8_t * ) drawBuffer ) + 4 * 4 ) ;
2013-02-05 00:37:00 +00:00
if ( program - > a_color0 ! = - 1 ) glVertexAttribPointer ( program - > a_color0 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , vertexSize , ( ( uint8_t * ) drawBuffer ) + 6 * 4 ) ;
if ( program - > a_color1 ! = - 1 ) glVertexAttribPointer ( program - > a_color1 , 3 , GL_UNSIGNED_BYTE , GL_TRUE , vertexSize , ( ( uint8_t * ) drawBuffer ) + 7 * 4 ) ;
2012-12-19 14:14:41 +00:00
if ( drawIndexed ) {
2013-01-10 12:08:04 +00:00
if ( useVBO ) {
2013-01-11 18:03:16 +00:00
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , ebo_ [ curVbo_ ] ) ;
2013-01-26 20:38:27 +00:00
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( short ) * numTrans , inds , GL_STREAM_DRAW ) ;
2013-01-10 12:08:04 +00:00
inds = 0 ;
}
glDrawElements ( glprim [ prim ] , numTrans , GL_UNSIGNED_SHORT , inds ) ;
if ( useVBO ) {
2013-01-11 18:03:16 +00:00
// Attempt to orphan the buffer we used so the GPU can alloc a new one.
// glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short) * numTrans, 0, GL_DYNAMIC_DRAW);
2013-01-20 21:42:11 +00:00
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
2013-01-10 12:08:04 +00:00
}
2012-12-19 14:14:41 +00:00
} else {
glDrawArrays ( glprim [ prim ] , 0 , numTrans ) ;
}
2013-01-10 12:08:04 +00:00
if ( useVBO ) {
2013-01-11 18:03:16 +00:00
// Attempt to orphan the buffer we used so the GPU can alloc a new one.
// glBufferData(GL_ARRAY_BUFFER, vertexSize * numTrans, 0, GL_DYNAMIC_DRAW);
2013-01-20 21:42:11 +00:00
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
2013-01-11 18:03:16 +00:00
curVbo_ + + ;
if ( curVbo_ = = NUM_VBOS )
curVbo_ = 0 ;
2013-01-10 12:08:04 +00:00
}
2012-12-19 14:14:41 +00:00
}
2013-01-04 08:54:19 +00:00
void TransformDrawEngine : : SubmitPrim ( void * verts , void * inds , int prim , int vertexCount , u32 vertType , int forceIndexType , int * bytesRead ) {
2013-01-19 16:05:08 +00:00
if ( vertexCount = = 0 )
{
return ; // we ignore zero-sized draw calls.
}
2013-01-19 18:22:15 +00:00
if ( ! indexGen . PrimCompatible ( prevPrim_ , prim ) | | numDrawCalls > = MAX_DEFERRED_DRAW_CALLS )
2012-12-21 18:16:17 +00:00
Flush ( ) ;
2013-01-19 18:22:15 +00:00
prevPrim_ = prim ;
2012-12-25 13:09:22 +00:00
// If vtype has changed, setup the vertex decoder.
// TODO: Simply cache the setup decoders instead.
2013-01-19 18:22:15 +00:00
if ( vertType ! = lastVType_ ) {
2012-12-28 18:33:26 +00:00
dec . SetVertexType ( vertType ) ;
2013-01-19 18:22:15 +00:00
lastVType_ = vertType ;
2012-12-25 13:09:22 +00:00
}
2012-12-28 18:33:26 +00:00
2012-12-20 13:10:42 +00:00
if ( bytesRead )
* bytesRead = vertexCount * dec . VertexSize ( ) ;
2012-12-19 17:35:37 +00:00
2013-01-19 16:05:08 +00:00
if ( ! indexGen . Empty ( ) ) {
gpuStats . numJoins + + ;
}
gpuStats . numDrawCalls + + ;
2013-01-24 23:36:59 +00:00
gpuStats . numVertsSubmitted + = vertexCount ;
2013-01-19 16:05:08 +00:00
DeferredDrawCall & dc = drawCalls [ numDrawCalls + + ] ;
dc . verts = verts ;
dc . inds = inds ;
dc . vertType = vertType ;
2013-01-20 21:42:11 +00:00
dc . indexType = ( ( forceIndexType = = - 1 ) ? ( vertType & GE_VTYPE_IDX_MASK ) : forceIndexType ) > > GE_VTYPE_IDX_SHIFT ;
2013-01-19 16:05:08 +00:00
dc . prim = prim ;
dc . vertexCount = vertexCount ;
if ( inds ) {
GetIndexBounds ( inds , vertexCount , vertType , & dc . indexLowerBound , & dc . indexUpperBound ) ;
} else {
dc . indexLowerBound = 0 ;
dc . indexUpperBound = vertexCount - 1 ;
}
}
void TransformDrawEngine : : DecodeVerts ( ) {
for ( int i = 0 ; i < numDrawCalls ; i + + ) {
const DeferredDrawCall & dc = drawCalls [ i ] ;
indexGen . SetIndex ( collectedVerts ) ;
int indexLowerBound = dc . indexLowerBound , indexUpperBound = dc . indexUpperBound ;
// Decode the verts and apply morphing
dec . DecodeVerts ( decoded + collectedVerts * ( int ) dec . GetDecVtxFmt ( ) . stride ,
dc . verts , dc . inds , dc . prim , dc . vertexCount , indexLowerBound , indexUpperBound ) ;
collectedVerts + = indexUpperBound - indexLowerBound + 1 ;
u32 indexType = dc . indexType ;
int vertexCount = dc . vertexCount ;
void * inds = dc . inds ;
switch ( indexType ) {
case GE_VTYPE_IDX_NONE > > GE_VTYPE_IDX_SHIFT :
switch ( dc . prim ) {
case GE_PRIM_POINTS : indexGen . AddPoints ( vertexCount ) ; break ;
case GE_PRIM_LINES : indexGen . AddLineList ( vertexCount ) ; break ;
case GE_PRIM_LINE_STRIP : indexGen . AddLineStrip ( vertexCount ) ; break ;
case GE_PRIM_TRIANGLES : indexGen . AddList ( vertexCount ) ; break ;
case GE_PRIM_TRIANGLE_STRIP : indexGen . AddStrip ( vertexCount ) ; break ;
case GE_PRIM_TRIANGLE_FAN : indexGen . AddFan ( vertexCount ) ; break ;
case GE_PRIM_RECTANGLES : indexGen . AddRectangles ( vertexCount ) ; break ; // Same
}
break ;
case GE_VTYPE_IDX_8BIT > > GE_VTYPE_IDX_SHIFT :
switch ( dc . prim ) {
2013-01-28 18:04:12 +00:00
case GE_PRIM_POINTS : indexGen . TranslatePoints ( vertexCount , ( const u8 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_LINES : indexGen . TranslateLineList ( vertexCount , ( const u8 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_LINE_STRIP : indexGen . TranslateLineStrip ( vertexCount , ( const u8 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_TRIANGLES : indexGen . TranslateList ( vertexCount , ( const u8 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_TRIANGLE_STRIP : indexGen . TranslateStrip ( vertexCount , ( const u8 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_TRIANGLE_FAN : indexGen . TranslateFan ( vertexCount , ( const u8 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_RECTANGLES : indexGen . TranslateRectangles ( vertexCount , ( const u8 * ) inds , indexLowerBound , indexUpperBound ) ; break ; // Same
2013-01-19 16:05:08 +00:00
}
break ;
case GE_VTYPE_IDX_16BIT > > GE_VTYPE_IDX_SHIFT :
switch ( dc . prim ) {
2013-01-28 18:04:12 +00:00
case GE_PRIM_POINTS : indexGen . TranslatePoints ( vertexCount , ( const u16 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_LINES : indexGen . TranslateLineList ( vertexCount , ( const u16 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_LINE_STRIP : indexGen . TranslateLineStrip ( vertexCount , ( const u16 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_TRIANGLES : indexGen . TranslateList ( vertexCount , ( const u16 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_TRIANGLE_STRIP : indexGen . TranslateStrip ( vertexCount , ( const u16 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_TRIANGLE_FAN : indexGen . TranslateFan ( vertexCount , ( const u16 * ) inds , indexLowerBound , indexUpperBound ) ; break ;
case GE_PRIM_RECTANGLES : indexGen . TranslateRectangles ( vertexCount , ( const u16 * ) inds , indexLowerBound , indexUpperBound ) ; break ; // Same
2013-01-19 16:05:08 +00:00
}
break ;
2012-12-21 17:46:15 +00:00
}
2013-01-19 16:05:08 +00:00
}
}
u32 TransformDrawEngine : : ComputeHash ( ) {
u32 fullhash = 0 ;
int vertexSize = dec . GetDecVtxFmt ( ) . stride ;
2013-02-05 23:42:06 +00:00
// TODO: Add some caps both for numDrawCalls and num verts to check?
2013-01-19 16:05:08 +00:00
for ( int i = 0 ; i < numDrawCalls ; i + + ) {
if ( ! drawCalls [ i ] . inds ) {
fullhash + = CityHash32 ( ( const char * ) drawCalls [ i ] . verts , vertexSize * drawCalls [ i ] . vertexCount ) ;
} else {
fullhash + = CityHash32 ( ( const char * ) drawCalls [ i ] . verts + vertexSize * drawCalls [ i ] . indexLowerBound ,
vertexSize * ( drawCalls [ i ] . indexUpperBound - drawCalls [ i ] . indexLowerBound ) ) ;
int indexSize = ( dec . VertexType ( ) & GE_VTYPE_IDX_MASK ) = = GE_VTYPE_IDX_16BIT ? 2 : 1 ;
fullhash + = CityHash32 ( ( const char * ) drawCalls [ i ] . inds , indexSize * drawCalls [ i ] . vertexCount ) ;
2012-12-21 17:46:15 +00:00
}
2013-01-19 16:05:08 +00:00
}
return fullhash ;
}
u32 TransformDrawEngine : : ComputeFastDCID ( ) {
u32 hash = 0 ;
for ( int i = 0 ; i < numDrawCalls ; i + + ) {
2013-01-20 23:42:08 +00:00
hash ^ = * ( u32 * ) & drawCalls [ i ] . verts ;
2013-01-19 16:05:08 +00:00
hash = _rotl ( hash , 13 ) ;
2013-01-20 23:42:08 +00:00
hash ^ = * ( u32 * ) & drawCalls [ i ] . inds ;
2013-01-19 16:05:08 +00:00
hash = _rotl ( hash , 13 ) ;
hash ^ = ( u32 ) drawCalls [ i ] . vertType ;
hash = _rotl ( hash , 13 ) ;
hash ^ = ( u32 ) drawCalls [ i ] . vertexCount ;
hash = _rotl ( hash , 13 ) ;
hash ^ = ( u32 ) drawCalls [ i ] . prim ;
}
return hash ;
}
2013-01-19 18:22:15 +00:00
enum { VAI_KILL_AGE = 120 } ;
2013-01-19 16:05:08 +00:00
void TransformDrawEngine : : ClearTrackedVertexArrays ( ) {
for ( auto vai = vai_ . begin ( ) ; vai ! = vai_ . end ( ) ; vai + + ) {
delete vai - > second ;
}
vai_ . clear ( ) ;
}
void TransformDrawEngine : : DecimateTrackedVertexArrays ( ) {
2013-02-04 22:09:01 +00:00
int threshold = gpuStats . numFrames - VAI_KILL_AGE ;
2013-01-19 16:05:08 +00:00
for ( auto iter = vai_ . begin ( ) ; iter ! = vai_ . end ( ) ; ) {
2013-02-06 19:45:25 +00:00
if ( iter - > second - > lastFrame < threshold ) {
2013-01-19 16:05:08 +00:00
delete iter - > second ;
vai_ . erase ( iter + + ) ;
2012-12-21 17:46:15 +00:00
}
2013-01-19 16:05:08 +00:00
else
+ + iter ;
2012-12-21 17:46:15 +00:00
}
2012-12-21 18:16:17 +00:00
}
2013-01-19 18:22:15 +00:00
VertexArrayInfo : : ~ VertexArrayInfo ( ) {
if ( vbo )
glDeleteBuffers ( 1 , & vbo ) ;
if ( ebo )
glDeleteBuffers ( 1 , & ebo ) ;
}
2012-12-25 12:47:59 +00:00
void TransformDrawEngine : : Flush ( ) {
2013-01-19 16:05:08 +00:00
if ( ! numDrawCalls )
2012-12-21 18:16:17 +00:00
return ;
2012-12-20 13:10:42 +00:00
2013-01-19 16:05:08 +00:00
gpuStats . numFlushes + + ;
2013-01-19 18:22:15 +00:00
2013-01-19 16:05:08 +00:00
gpuStats . numTrackedVertexArrays = vai_ . size ( ) ;
2012-12-19 17:35:37 +00:00
2012-12-20 13:10:42 +00:00
// TODO: This should not be done on every drawcall, we should collect vertex data
// until critical state changes. That's when we draw (flush).
2012-12-19 17:35:37 +00:00
2013-01-19 18:22:15 +00:00
int prim = prevPrim_ ;
2013-01-12 16:20:00 +00:00
ApplyDrawState ( prim ) ;
2012-12-20 13:10:42 +00:00
UpdateViewportAndProjection ( ) ;
LinkedShader * program = shaderManager_ - > ApplyShader ( prim ) ;
2013-01-19 18:22:15 +00:00
if ( CanUseHardwareTransform ( prevPrim_ ) ) {
GLuint vbo = 0 , ebo = 0 ;
int vertexCount = 0 ;
2013-01-20 20:52:54 +00:00
bool useElements = true ;
2013-01-20 21:48:29 +00:00
// Cannot cache vertex data with morph enabled.
if ( g_Config . bVertexCache & & ! ( lastVType_ & GE_VTYPE_MORPHCOUNT_MASK ) ) {
2013-01-20 12:15:46 +00:00
u32 id = ComputeFastDCID ( ) ;
auto iter = vai_ . find ( id ) ;
VertexArrayInfo * vai ;
2013-01-26 19:36:45 +00:00
if ( iter ! = vai_ . end ( ) ) {
2013-01-20 12:15:46 +00:00
// We've seen this before. Could have been a cached draw.
vai = iter - > second ;
} else {
vai = new VertexArrayInfo ( ) ;
vai - > decFmt = dec . GetDecVtxFmt ( ) ;
vai_ [ id ] = vai ;
}
2013-01-19 18:22:15 +00:00
2013-01-20 20:52:54 +00:00
switch ( vai - > status ) {
2013-01-20 12:15:46 +00:00
case VertexArrayInfo : : VAI_NEW :
{
// Haven't seen this one before.
u32 dataHash = ComputeHash ( ) ;
vai - > hash = dataHash ;
vai - > status = VertexArrayInfo : : VAI_HASHING ;
2013-02-12 07:41:31 +00:00
vai - > drawsUntilNextFullHash = 0 ;
2013-01-20 12:15:46 +00:00
DecodeVerts ( ) ; // writes to indexGen
goto rotateVBO ;
}
2013-01-19 18:22:15 +00:00
2013-01-20 12:15:46 +00: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 VertexArrayInfo : : VAI_HASHING :
{
vai - > numDraws + + ;
2013-02-10 20:27:43 +00:00
if ( vai - > lastFrame ! = gpuStats . numFrames ) {
vai - > numFrames + + ;
}
2013-02-12 07:41:31 +00:00
if ( vai - > drawsUntilNextFullHash = = 0 ) {
2013-02-05 23:42:06 +00:00
u32 newHash = ComputeHash ( ) ;
if ( newHash ! = vai - > hash ) {
vai - > status = VertexArrayInfo : : VAI_UNRELIABLE ;
if ( vai - > vbo ) {
glDeleteBuffers ( 1 , & vai - > vbo ) ;
vai - > vbo = 0 ;
}
if ( vai - > ebo ) {
glDeleteBuffers ( 1 , & vai - > ebo ) ;
vai - > ebo = 0 ;
}
DecodeVerts ( ) ;
goto rotateVBO ;
2013-01-20 20:52:54 +00:00
}
2013-02-12 08:02:53 +00:00
if ( vai - > numVerts > 100 ) {
// exponential backoff up to 16 draws, then every 24
vai - > drawsUntilNextFullHash = std : : min ( 24 , vai - > numFrames ) ;
} else {
// Lower numbers seem much more likely to change.
vai - > status = VertexArrayInfo : : VAI_UNRELIABLE ;
}
// TODO: tweak
//if (vai->numFrames > 1000) {
// vai->status = VertexArrayInfo::VAI_RELIABLE;
//}
2013-02-05 23:42:06 +00:00
} else {
2013-02-12 07:41:31 +00:00
vai - > drawsUntilNextFullHash - - ;
2013-02-05 23:42:06 +00:00
// TODO: "mini-hashing" the first 32 bytes of the vertex/index data or something.
2013-01-19 18:22:15 +00:00
}
2013-02-05 23:42:06 +00:00
2013-01-20 12:15:46 +00:00
if ( vai - > vbo = = 0 ) {
2013-01-20 20:52:54 +00:00
DecodeVerts ( ) ;
2013-01-20 12:15:46 +00:00
vai - > numVerts = indexGen . VertexCount ( ) ;
vai - > prim = indexGen . Prim ( ) ;
2013-01-20 21:42:11 +00:00
useElements = ! indexGen . SeenOnlyPurePrims ( ) ;
2013-01-20 12:15:46 +00:00
glGenBuffers ( 1 , & vai - > vbo ) ;
2013-01-20 21:42:11 +00:00
glBindBuffer ( GL_ARRAY_BUFFER , vai - > vbo ) ;
glBufferData ( GL_ARRAY_BUFFER , dec . GetDecVtxFmt ( ) . stride * indexGen . MaxIndex ( ) , decoded , GL_STATIC_DRAW ) ;
2013-01-20 12:15:46 +00:00
// If there's only been one primitive type, and it's either TRIANGLES, LINES or POINTS,
// there is no need for the index buffer we built. We can then use glDrawArrays instead
// for a very minor speed boost.
2013-01-20 21:42:11 +00:00
if ( useElements ) {
2013-01-20 12:15:46 +00:00
glGenBuffers ( 1 , & vai - > ebo ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , vai - > ebo ) ;
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( short ) * indexGen . VertexCount ( ) , ( GLvoid * ) decIndex , GL_STATIC_DRAW ) ;
2013-01-20 20:52:54 +00:00
} else {
vai - > ebo = 0 ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
2013-01-20 12:15:46 +00:00
}
} else {
2013-02-05 23:42:06 +00:00
gpuStats . numCachedDrawCalls + + ;
2013-01-20 12:15:46 +00:00
glBindBuffer ( GL_ARRAY_BUFFER , vai - > vbo ) ;
if ( vai - > ebo )
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , vai - > ebo ) ;
2013-01-20 21:42:11 +00:00
useElements = vai - > ebo ? true : false ;
2013-01-24 23:36:59 +00:00
gpuStats . numCachedVertsDrawn + = vai - > numVerts ;
2013-01-19 18:22:15 +00:00
}
2013-01-20 12:15:46 +00:00
vbo = vai - > vbo ;
ebo = vai - > ebo ;
vertexCount = vai - > numVerts ;
prim = vai - > prim ;
break ;
2013-01-19 18:22:15 +00:00
}
2013-01-20 12:15:46 +00:00
// Reliable - we don't even bother hashing anymore. Right now we don't go here until after a very long time.
case VertexArrayInfo : : VAI_RELIABLE :
{
vai - > numDraws + + ;
2013-02-10 20:27:43 +00:00
if ( vai - > lastFrame ! = gpuStats . numFrames ) {
vai - > numFrames + + ;
}
2013-01-20 12:15:46 +00:00
gpuStats . numCachedDrawCalls + + ;
2013-01-24 23:36:59 +00:00
gpuStats . numCachedVertsDrawn + = vai - > numVerts ;
2013-01-20 12:15:46 +00:00
vbo = vai - > vbo ;
ebo = vai - > ebo ;
glBindBuffer ( GL_ARRAY_BUFFER , vbo ) ;
if ( ebo )
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , ebo ) ;
vertexCount = vai - > numVerts ;
prim = vai - > prim ;
break ;
}
case VertexArrayInfo : : VAI_UNRELIABLE :
{
vai - > numDraws + + ;
2013-02-10 20:27:43 +00:00
if ( vai - > lastFrame ! = gpuStats . numFrames ) {
vai - > numFrames + + ;
}
2013-01-20 12:15:46 +00:00
DecodeVerts ( ) ;
goto rotateVBO ;
}
}
2013-02-12 07:29:49 +00:00
vai - > lastFrame = gpuStats . numFrames ;
2013-01-20 12:15:46 +00:00
} else {
DecodeVerts ( ) ;
2013-01-19 18:22:15 +00:00
rotateVBO :
2013-01-24 23:36:59 +00:00
gpuStats . numUncachedVertsDrawn + = indexGen . VertexCount ( ) ;
2013-01-20 20:52:54 +00:00
useElements = ! indexGen . SeenOnlyPurePrims ( ) ;
2013-01-28 18:04:12 +00:00
vertexCount = indexGen . VertexCount ( ) ;
2013-01-20 12:15:46 +00:00
if ( g_Config . bUseVBO ) {
2013-01-19 18:22:15 +00:00
// Just rotate VBO.
vbo = vbo_ [ curVbo_ ] ;
ebo = ebo_ [ curVbo_ ] ;
curVbo_ + + ;
if ( curVbo_ = = NUM_VBOS )
curVbo_ = 0 ;
glBindBuffer ( GL_ARRAY_BUFFER , vbo ) ;
glBufferData ( GL_ARRAY_BUFFER , dec . GetDecVtxFmt ( ) . stride * indexGen . MaxIndex ( ) , decoded , GL_STREAM_DRAW ) ;
2013-01-20 21:42:11 +00:00
if ( useElements ) {
2013-01-20 20:52:54 +00:00
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , ebo ) ;
2013-01-28 18:04:12 +00:00
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( short ) * vertexCount , ( GLvoid * ) decIndex , GL_STREAM_DRAW ) ;
2013-01-20 20:52:54 +00:00
}
} else {
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
2013-01-10 11:51:18 +00:00
}
2013-01-20 20:52:54 +00:00
prim = indexGen . Prim ( ) ;
2013-01-19 18:22:15 +00:00
}
DEBUG_LOG ( G3D , " Flush prim %i! %i verts in one go " , prim , vertexCount ) ;
SetupDecFmtForDraw ( program , dec . GetDecVtxFmt ( ) , vbo ? 0 : decoded ) ;
2013-01-20 20:52:54 +00:00
if ( useElements ) {
glDrawElements ( glprim [ prim ] , vertexCount , GL_UNSIGNED_SHORT , ebo ? 0 : ( GLvoid * ) decIndex ) ;
2013-01-20 12:15:46 +00:00
if ( ebo )
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
2013-01-20 20:52:54 +00:00
} else {
glDrawArrays ( glprim [ prim ] , 0 , vertexCount ) ;
2012-12-26 07:54:33 +00:00
}
2013-01-20 12:15:46 +00:00
if ( vbo )
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
2012-12-20 13:10:42 +00:00
} else {
2013-01-19 18:22:15 +00:00
DecodeVerts ( ) ;
2013-01-24 23:36:59 +00:00
gpuStats . numUncachedVertsDrawn + = indexGen . VertexCount ( ) ;
2013-01-19 18:22:15 +00:00
prim = indexGen . Prim ( ) ;
DEBUG_LOG ( G3D , " Flush prim %i SW! %i verts in one go " , prim , indexGen . VertexCount ( ) ) ;
2012-12-28 18:33:26 +00:00
SoftwareTransformAndDraw ( prim , decoded , program , indexGen . VertexCount ( ) , dec . VertexType ( ) , ( void * ) decIndex , GE_VTYPE_IDX_16BIT , dec . GetDecVtxFmt ( ) ,
2012-12-21 18:16:17 +00:00
indexGen . MaxIndex ( ) ) ;
2012-12-20 13:10:42 +00:00
}
2012-12-19 17:35:37 +00:00
2012-12-21 18:16:17 +00:00
indexGen . Reset ( ) ;
2013-01-12 10:33:04 +00:00
collectedVerts = 0 ;
2013-01-19 16:05:08 +00:00
numDrawCalls = 0 ;
2013-01-19 18:22:15 +00:00
prevPrim_ = - 1 ;
2012-12-21 20:49:09 +00:00
}