2012-11-01 16:19:01 +01:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2013-10-09 11:01:52 +02:00
// Ideas for speeding things up on mobile OpenGL ES implementations
//
// Use superbuffers! Yes I just invented that name.
//
// The idea is to avoid respecifying the vertex format between every draw call (multiple glVertexAttribPointer ...)
// by combining the contents of multiple draw calls into one buffer, as long as
// they have exactly the same output vertex format. (different input formats is fine! This way
// we can combine the data for multiple draws with different numbers of bones, as we consider numbones < 4 to be = 4)
// into one VBO.
//
// This will likely be a win because I believe that between every change of VBO + glVertexAttribPointer*N, the driver will
// perform a lot of validation, probably at draw call time, while all the validation can be skipped if the only thing
// that changes between two draw calls is simple state or texture or a matrix etc, not anything vertex related.
// Also the driver will have to manage hundreds instead of thousands of VBOs in games like GTA.
//
// * Every 10 frames or something, do the following:
// - Frame 1:
// + Mark all drawn buffers with in-frame sequence numbers (alternatively,
// just log them in an array)
// - Frame 2 (beginning?):
// + Take adjacent buffers that have the same output vertex format, and add them
// to a list of buffers to combine. Create said buffers with appropriate sizes
// and precompute the offsets that the draws should be written into.
// - Frame 2 (end):
// + Actually do the work of combining the buffers. This probably means re-decoding
// the vertices into a new one. Will also have to apply index offsets.
//
// Also need to change the drawing code so that we don't glBindBuffer and respecify glVAP if
// two subsequent drawcalls come from the same superbuffer.
//
// Or we ignore all of this including vertex caching and simply find a way to do highly optimized vertex streaming,
// like Dolphin is trying to. That will likely never be able to reach the same speed as perfectly optimized
// superbuffers though. For this we will have to JIT the vertex decoder but that's not too hard.
//
// Now, when do we delete superbuffers? Maybe when half the buffers within have been killed?
//
// Another idea for GTA which switches textures a lot while not changing much other state is to use ES 3 Array
// textures, if they are the same size (even if they aren't, might be okay to simply resize the textures to match
// if they're just a multiple of 2 away) or something. Then we'd have to add a W texture coordinate to choose the
// texture within the bound texture array to the vertex data when merging into superbuffers.
//
// There are even more things to try. For games that do matrix palette skinning by quickly switching bones and
// just drawing a few triangles per call (NBA, FF:CC, Tekken 6 etc) we could even collect matrices, upload them
// all at once, writing matrix indices into the vertices in addition to the weights, and then doing a single
// draw call with specially generated shader to draw the whole mesh. This code will be seriously complex though.
2013-10-09 16:08:36 +02:00
# include "base/logging.h"
2013-01-11 19:03:16 +01:00
# include "base/timeutil.h"
2013-01-29 00:48:13 +01:00
# include "Common/MemoryUtil.h"
2013-04-20 20:20:03 +02:00
# include "Core/MemMap.h"
# include "Core/Host.h"
# include "Core/System.h"
# include "Core/Reporting.h"
# include "Core/Config.h"
# include "Core/CoreTiming.h"
2017-03-03 14:15:27 +01:00
# include "gfx/gl_debug_log.h"
2015-05-13 22:28:02 +02:00
# include "profiler/profiler.h"
2013-04-20 20:20:03 +02:00
# include "GPU/Math3D.h"
# include "GPU/GPUState.h"
# include "GPU/ge_constants.h"
2012-11-01 16:19:01 +01:00
2014-03-25 00:21:04 -07:00
# include "GPU/Common/TextureDecoder.h"
2013-10-13 20:47:52 -07:00
# include "GPU/Common/SplineCommon.h"
2014-09-10 10:44:22 +02:00
# include "GPU/Common/VertexDecoderCommon.h"
2014-09-13 13:27:42 +02:00
# include "GPU/Common/SoftwareTransformCommon.h"
2017-02-04 18:46:12 +01:00
# include "ext/native/gfx/GLStateCache.h"
2017-01-21 22:16:30 +01:00
# include "GPU/GLES/FragmentTestCacheGLES.h"
# include "GPU/GLES/StateMappingGLES.h"
# include "GPU/GLES/TextureCacheGLES.h"
2016-04-10 10:27:28 +02:00
# include "GPU/GLES/DrawEngineGLES.h"
2017-01-21 22:16:30 +01:00
# include "GPU/GLES/ShaderManagerGLES.h"
2016-04-10 10:27:28 +02:00
# include "GPU/GLES/GPU_GLES.h"
2012-11-01 16:19:01 +01:00
2013-11-13 14:56:34 +01:00
extern const GLuint glprim [ 8 ] = {
2012-11-01 16:19:01 +01:00
GL_POINTS ,
GL_LINES ,
GL_LINE_STRIP ,
GL_TRIANGLES ,
GL_TRIANGLE_STRIP ,
GL_TRIANGLE_FAN ,
2013-12-09 13:45:17 +01:00
GL_TRIANGLES ,
2016-04-10 10:59:23 +02:00
// Rectangles need to be expanded into triangles.
2012-11-01 16:19:01 +01:00
} ;
2013-01-29 00:48:13 +01:00
enum {
2013-10-27 14:43:58 -07:00
TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof ( TransformedVertex )
2013-01-29 00:48:13 +01:00
} ;
2013-08-01 00:13:58 +02:00
# define VERTEXCACHE_DECIMATION_INTERVAL 17
2015-12-23 01:29:42 -08:00
# define VERTEXCACHE_NAME_DECIMATION_INTERVAL 41
2015-12-23 10:02:01 -08:00
# define VERTEXCACHE_NAME_DECIMATION_MAX 100
2014-06-07 16:15:37 -07:00
# define VERTEXCACHE_NAME_CACHE_SIZE 64
2015-12-23 10:02:01 -08:00
# define VERTEXCACHE_NAME_CACHE_FULL_BYTES (1024 * 1024)
2015-12-23 01:29:42 -08:00
# define VERTEXCACHE_NAME_CACHE_MAX_AGE 120
2013-08-01 00:13:58 +02:00
2014-09-07 17:08:05 -07:00
enum { VAI_KILL_AGE = 120 , VAI_UNRELIABLE_KILL_AGE = 240 , VAI_UNRELIABLE_KILL_MAX = 4 } ;
2014-03-23 15:34:04 +01:00
2017-08-20 11:30:19 +02:00
DrawEngineGLES : : DrawEngineGLES ( ) : vai_ ( 256 ) {
2017-01-25 19:44:17 +01:00
decOptions_ . expandAllWeightsToFloat = false ;
decOptions_ . expand8BitNormalsToFloat = false ;
2013-08-01 00:13:58 +02:00
decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL ;
2015-12-23 01:29:42 -08:00
bufferDecimationCounter_ = VERTEXCACHE_NAME_DECIMATION_INTERVAL ;
2013-01-29 00:48:13 +01: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.
2016-08-28 12:28:17 +02:00
decoded = ( u8 * ) AllocateMemoryPages ( DECODED_VERTEX_BUFFER_SIZE , MEM_PROT_READ | MEM_PROT_WRITE ) ;
decIndex = ( u16 * ) AllocateMemoryPages ( DECODED_INDEX_BUFFER_SIZE , MEM_PROT_READ | MEM_PROT_WRITE ) ;
splineBuffer = ( u8 * ) AllocateMemoryPages ( SPLINE_BUFFER_SIZE , MEM_PROT_READ | MEM_PROT_WRITE ) ;
2013-09-24 11:14:04 +02:00
2012-12-25 13:47:59 +01:00
indexGen . Setup ( decIndex ) ;
2013-11-03 15:27:12 +01:00
2013-01-10 12:51:18 +01:00
InitDeviceObjects ( ) ;
2017-03-18 15:20:36 +01:00
register_gl_resource_holder ( this , " drawengine_gles " , 1 ) ;
2017-01-09 03:37:21 +09:00
2017-01-27 12:49:27 +09:00
tessDataTransfer = new TessellationDataTransferGLES ( gl_extensions . VersionGEThan ( 3 , 0 , 0 ) ) ;
2012-12-25 13:47:59 +01:00
}
2012-11-01 16:19:01 +01:00
2016-04-10 10:21:48 +02:00
DrawEngineGLES : : ~ DrawEngineGLES ( ) {
2013-01-10 12:51:18 +01:00
DestroyDeviceObjects ( ) ;
2013-01-29 00:48:13 +01:00
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-09-24 11:14:04 +02:00
2013-01-10 12:51:18 +01:00
unregister_gl_resource_holder ( this ) ;
2017-01-09 03:37:21 +09:00
delete tessDataTransfer ;
2013-01-10 12:51:18 +01:00
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : RestoreVAO ( ) {
2015-12-13 22:48:21 -08:00
if ( sharedVao_ ! = 0 ) {
glBindVertexArray ( sharedVao_ ) ;
} else if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) ) {
// Note: this is here because, InitDeviceObjects() is called before GPU_SUPPORTS_VAO is setup.
// So, this establishes it if Supports() returns true and there isn't one yet.
glGenVertexArrays ( 1 , & sharedVao_ ) ;
glBindVertexArray ( sharedVao_ ) ;
}
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : InitDeviceObjects ( ) {
2014-06-07 16:15:37 -07:00
if ( bufferNameCache_ . empty ( ) ) {
bufferNameCache_ . resize ( VERTEXCACHE_NAME_CACHE_SIZE ) ;
glGenBuffers ( VERTEXCACHE_NAME_CACHE_SIZE , & bufferNameCache_ [ 0 ] ) ;
2015-12-23 10:02:01 -08:00
bufferNameCacheSize_ = 0 ;
2015-12-13 22:48:21 -08:00
if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) ) {
glGenVertexArrays ( 1 , & sharedVao_ ) ;
} else {
sharedVao_ = 0 ;
}
2013-01-10 12:51:18 +01:00
} else {
ERROR_LOG ( G3D , " Device objects already initialized! " ) ;
}
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : DestroyDeviceObjects ( ) {
2015-12-23 18:17:22 -08:00
ClearTrackedVertexArrays ( ) ;
2014-06-07 16:15:37 -07:00
if ( ! bufferNameCache_ . empty ( ) ) {
2015-01-11 17:03:45 -08:00
glstate . arrayBuffer . unbind ( ) ;
glstate . elementArrayBuffer . unbind ( ) ;
2014-06-07 16:15:37 -07:00
glDeleteBuffers ( ( GLsizei ) bufferNameCache_ . size ( ) , & bufferNameCache_ [ 0 ] ) ;
bufferNameCache_ . clear ( ) ;
2015-12-22 23:06:56 -08:00
bufferNameInfo_ . clear ( ) ;
2016-02-28 15:10:41 -08:00
freeSizedBuffers_ . clear ( ) ;
2015-12-23 10:02:01 -08:00
bufferNameCacheSize_ = 0 ;
2015-12-13 22:48:21 -08:00
if ( sharedVao_ ! = 0 ) {
glDeleteVertexArrays ( 1 , & sharedVao_ ) ;
}
2014-06-07 16:15:37 -07:00
}
2013-01-10 12:51:18 +01:00
}
2016-09-10 20:29:58 -07:00
void DrawEngineGLES : : GLLost ( ) {
ILOG ( " TransformDrawEngine::GLLost() " ) ;
2017-03-18 13:01:08 +01:00
// The objects have already been deleted by losing the context, so we don't call DestroyDeviceObjects.
2014-06-07 16:15:37 -07:00
bufferNameCache_ . clear ( ) ;
2015-12-22 23:06:56 -08:00
bufferNameInfo_ . clear ( ) ;
2016-02-28 15:10:41 -08:00
freeSizedBuffers_ . clear ( ) ;
2015-12-23 10:02:01 -08:00
bufferNameCacheSize_ = 0 ;
2013-01-20 13:15:46 +01:00
ClearTrackedVertexArrays ( ) ;
2016-09-10 20:29:58 -07:00
}
void DrawEngineGLES : : GLRestore ( ) {
ILOG ( " TransformDrawEngine::GLRestore() " ) ;
2013-01-10 12:51:18 +01:00
InitDeviceObjects ( ) ;
2012-12-25 13:47:59 +01:00
}
2012-12-20 14:10:42 +01:00
struct GlTypeInfo {
2013-02-04 23:09:01 +01:00
u16 type ;
u8 count ;
u8 normalized ;
2012-12-20 14:10:42 +01:00
} ;
2012-12-19 18:35:37 +01:00
2013-02-04 23:09:01 +01:00
static const GlTypeInfo GLComp [ ] = {
2012-12-20 14:10:42 +01: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 21:57:47 +01:00
{ GL_BYTE , 4 , GL_TRUE } , // DEC_S8_3,
{ GL_SHORT , 4 , GL_TRUE } , // DEC_S16_3,
2013-02-04 23:09:01 +01:00
{ GL_UNSIGNED_BYTE , 1 , GL_TRUE } , // DEC_U8_1,
{ GL_UNSIGNED_BYTE , 2 , GL_TRUE } , // DEC_U8_2,
2013-01-29 00:48:13 +01:00
{ GL_UNSIGNED_BYTE , 3 , GL_TRUE } , // DEC_U8_3,
2013-02-04 23:09:01 +01:00
{ GL_UNSIGNED_BYTE , 4 , GL_TRUE } , // DEC_U8_4,
2013-02-05 18:00:21 +01:00
{ GL_UNSIGNED_SHORT , 1 , GL_TRUE } , // DEC_U16_1,
2013-02-05 01:37:00 +01:00
{ GL_UNSIGNED_SHORT , 2 , GL_TRUE } , // DEC_U16_2,
2013-02-05 18:00:21 +01:00
{ GL_UNSIGNED_SHORT , 3 , GL_TRUE } , // DEC_U16_3,
{ GL_UNSIGNED_SHORT , 4 , GL_TRUE } , // DEC_U16_4,
2012-12-20 14:10:42 +01:00
} ;
2012-12-19 18:35:37 +01:00
2012-12-20 14:10:42 +01:00
static inline void VertexAttribSetup ( int attrib , int fmt , int stride , u8 * ptr ) {
2016-03-05 13:12:24 -08:00
if ( fmt ) {
2012-12-20 14:10:42 +01:00
const GlTypeInfo & type = GLComp [ fmt ] ;
glVertexAttribPointer ( attrib , type . count , type . type , type . normalized , stride , ptr ) ;
2012-12-05 10:45:28 +07:00
}
2012-12-20 14:10:42 +01:00
}
2012-12-05 10:45:28 +07:00
2012-12-20 14:10:42 +01: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 ) {
2017-03-03 14:15:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-10-08 17:18:59 +02:00
VertexAttribSetup ( ATTR_W1 , decFmt . w0fmt , decFmt . stride , vertexData + decFmt . w0off ) ;
VertexAttribSetup ( ATTR_W2 , decFmt . w1fmt , decFmt . stride , vertexData + decFmt . w1off ) ;
VertexAttribSetup ( ATTR_TEXCOORD , decFmt . uvfmt , decFmt . stride , vertexData + decFmt . uvoff ) ;
VertexAttribSetup ( ATTR_COLOR0 , decFmt . c0fmt , decFmt . stride , vertexData + decFmt . c0off ) ;
VertexAttribSetup ( ATTR_COLOR1 , decFmt . c1fmt , decFmt . stride , vertexData + decFmt . c1off ) ;
VertexAttribSetup ( ATTR_NORMAL , decFmt . nrmfmt , decFmt . stride , vertexData + decFmt . nrmoff ) ;
VertexAttribSetup ( ATTR_POSITION , decFmt . posfmt , decFmt . stride , vertexData + decFmt . posoff ) ;
2017-03-03 14:15:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2012-12-20 14:10:42 +01:00
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : SubmitPrim ( void * verts , void * inds , GEPrimitiveType prim , int vertexCount , u32 vertType , int * bytesRead ) {
2017-06-02 12:03:46 +02:00
if ( ! indexGen . PrimCompatible ( prevPrim_ , prim ) | | numDrawCalls > = MAX_DEFERRED_DRAW_CALLS | | vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX )
2012-12-21 19:16:17 +01:00
Flush ( ) ;
2013-10-09 11:01:52 +02:00
2013-09-07 22:31:22 -07:00
// TODO: Is this the right thing to do?
2013-09-04 17:12:53 +08:00
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-19 20:25:04 +02:00
} else {
prevPrim_ = prim ;
2013-09-04 17:12:53 +08:00
}
2013-10-09 11:01:52 +02:00
2017-06-02 13:06:14 +02:00
SetupVertexDecoder ( vertType ) ;
2012-12-28 19:33:26 +01:00
2014-10-19 20:25:04 +02:00
* bytesRead = vertexCount * dec_ - > VertexSize ( ) ;
2013-01-19 17:05:08 +01:00
2015-02-28 01:28:30 -08:00
if ( ( vertexCount < 2 & & prim > 0 ) | | ( vertexCount < 3 & & prim > 2 & & prim ! = GE_PRIM_RECTANGLES ) )
return ;
2013-07-28 00:18:41 +02:00
DeferredDrawCall & dc = drawCalls [ numDrawCalls ] ;
2013-01-19 17:05:08 +01:00
dc . verts = verts ;
dc . inds = inds ;
dc . vertType = vertType ;
2013-11-14 14:03:03 +01:00
dc . indexType = ( vertType & GE_VTYPE_IDX_MASK ) > > GE_VTYPE_IDX_SHIFT ;
2013-01-19 17:05:08 +01:00
dc . prim = prim ;
dc . vertexCount = vertexCount ;
2014-03-23 01:51:51 +01:00
u32 dhash = dcid_ ;
2014-03-23 15:34:04 +01:00
dhash ^ = ( u32 ) ( uintptr_t ) verts ;
2014-03-23 01:51:51 +01:00
dhash = __rotl ( dhash , 13 ) ;
2014-03-23 15:34:04 +01:00
dhash ^ = ( u32 ) ( uintptr_t ) inds ;
2014-03-23 01:51:51 +01:00
dhash = __rotl ( dhash , 13 ) ;
2014-03-23 15:34:04 +01:00
dhash ^ = ( u32 ) vertType ;
2014-03-23 01:51:51 +01:00
dhash = __rotl ( dhash , 13 ) ;
2014-03-23 15:34:04 +01:00
dhash ^ = ( u32 ) vertexCount ;
2014-03-23 01:51:51 +01:00
dhash = __rotl ( dhash , 13 ) ;
2014-03-23 15:34:04 +01:00
dhash ^ = ( u32 ) prim ;
2014-03-23 01:51:51 +01:00
dcid_ = dhash ;
2013-01-19 17:05:08 +01:00
if ( inds ) {
GetIndexBounds ( inds , vertexCount , vertType , & dc . indexLowerBound , & dc . indexUpperBound ) ;
} else {
dc . indexLowerBound = 0 ;
dc . indexUpperBound = vertexCount - 1 ;
}
2013-07-28 00:18:41 +02:00
2016-12-20 13:27:44 +01:00
uvScale [ numDrawCalls ] = gstate_c . uv ;
2013-11-10 15:31:56 +01:00
2013-07-28 00:18:41 +02:00
numDrawCalls + + ;
2017-06-02 12:03:46 +02:00
vertexCountInDrawCalls_ + = vertexCount ;
2013-11-10 15:31:56 +01:00
if ( g_Config . bSoftwareSkinning & & ( vertType & GE_VTYPE_WEIGHT_MASK ) ) {
2017-06-02 12:09:57 +02:00
DecodeVertsStep ( decoded , decodeCounter_ , decodedVerts_ ) ;
2013-11-10 15:31:56 +01:00
decodeCounter_ + + ;
}
2014-06-22 20:37:50 +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-07-08 23:32:41 -07:00
if ( ! g_Config . bDisableSlowFramebufEffects ) {
2017-01-24 09:41:38 +01:00
gstate_c . Dirty ( DIRTY_TEXTURE_PARAMS ) ;
2014-07-08 23:32:41 -07:00
Flush ( ) ;
}
2014-06-22 20:37:50 +02:00
}
2013-01-19 17:05:08 +01:00
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : MarkUnreliable ( VertexArrayInfo * vai ) {
2014-09-07 17:28:12 -07:00
vai - > status = VertexArrayInfo : : VAI_UNRELIABLE ;
if ( vai - > vbo ) {
FreeBuffer ( vai - > vbo ) ;
vai - > vbo = 0 ;
}
if ( vai - > ebo ) {
FreeBuffer ( vai - > ebo ) ;
vai - > ebo = 0 ;
}
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : ClearTrackedVertexArrays ( ) {
2017-08-20 11:30:19 +02:00
vai_ . Iterate ( [ & ] ( uint32_t hash , VertexArrayInfo * vai ) {
FreeVertexArray ( vai ) ;
delete vai ;
} ) ;
vai_ . Clear ( ) ;
2013-01-19 17:05:08 +01:00
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : DecimateTrackedVertexArrays ( ) {
2013-08-01 00:13:58 +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 ;
2014-09-07 17:08:05 -07:00
int unreliableLeft = VAI_UNRELIABLE_KILL_MAX ;
2017-08-20 11:30:19 +02:00
vai_ . Iterate ( [ & ] ( uint32_t hash , VertexArrayInfo * vai ) {
2014-09-07 17:08:05 -07:00
bool kill ;
2017-08-20 11:30:19 +02:00
if ( vai - > status = = VertexArrayInfo : : VAI_UNRELIABLE ) {
2014-09-07 17:08:05 -07:00
// We limit killing unreliable so we don't rehash too often.
2017-08-20 11:30:19 +02:00
kill = vai - > lastFrame < unreliableThreshold & & - - unreliableLeft > = 0 ;
2014-09-07 17:08:05 -07:00
} else {
2017-08-20 11:30:19 +02:00
kill = vai - > lastFrame < threshold ;
2014-09-07 17:08:05 -07:00
}
if ( kill ) {
2017-08-20 11:30:19 +02:00
FreeVertexArray ( vai ) ;
delete vai ;
vai_ . Remove ( hash ) ;
2013-12-09 13:45:17 +01:00
}
2017-08-20 11:30:19 +02:00
} ) ;
2017-08-20 13:20:16 +02:00
vai_ . Maintain ( ) ;
2012-12-21 19:16:17 +01:00
}
2016-04-10 10:21:48 +02:00
GLuint DrawEngineGLES : : AllocateBuffer ( size_t sz ) {
2015-12-23 01:29:42 -08:00
GLuint unused = 0 ;
2016-02-28 15:10:41 -08:00
auto freeMatch = freeSizedBuffers_ . find ( sz ) ;
if ( freeMatch ! = freeSizedBuffers_ . end ( ) ) {
unused = freeMatch - > second ;
_assert_ ( ! bufferNameInfo_ [ unused ] . used ) ;
freeSizedBuffers_ . erase ( freeMatch ) ;
} else {
for ( GLuint buf : bufferNameCache_ ) {
const BufferNameInfo & info = bufferNameInfo_ [ buf ] ;
if ( info . used ) {
continue ;
}
// Just pick the first unused one, we'll have to resize it.
unused = buf ;
// Let's also remove from the free list, if it's there.
if ( info . sz ! = 0 ) {
auto range = freeSizedBuffers_ . equal_range ( info . sz ) ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
if ( it - > second = = buf ) {
// It will only be once, so remove and bail.
freeSizedBuffers_ . erase ( it ) ;
break ;
}
}
}
2015-12-22 23:06:56 -08:00
break ;
}
2014-06-07 16:15:37 -07:00
}
2015-12-22 23:06:56 -08:00
2015-12-23 01:29:42 -08:00
if ( unused = = 0 ) {
2015-12-22 23:06:56 -08:00
size_t oldSize = bufferNameCache_ . size ( ) ;
bufferNameCache_ . resize ( oldSize + VERTEXCACHE_NAME_CACHE_SIZE ) ;
glGenBuffers ( VERTEXCACHE_NAME_CACHE_SIZE , & bufferNameCache_ [ oldSize ] ) ;
2015-12-23 01:29:42 -08:00
unused = bufferNameCache_ [ oldSize ] ;
2015-12-22 23:06:56 -08:00
}
2015-12-23 10:02:01 -08:00
BufferNameInfo & info = bufferNameInfo_ [ unused ] ;
// Record the change in size.
bufferNameCacheSize_ + = sz - info . sz ;
info . sz = sz ;
info . used = true ;
2015-12-23 01:29:42 -08:00
return unused ;
2014-06-07 16:15:37 -07:00
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : FreeBuffer ( GLuint buf ) {
2015-12-23 01:29:42 -08:00
// We can reuse buffers by setting new data on them, so let's actually keep it.
auto it = bufferNameInfo_ . find ( buf ) ;
if ( it ! = bufferNameInfo_ . end ( ) ) {
it - > second . used = false ;
it - > second . lastFrame = gpuStats . numFlips ;
2016-02-28 15:10:41 -08:00
if ( it - > second . sz ! = 0 ) {
freeSizedBuffers_ . insert ( std : : make_pair ( it - > second . sz , buf ) ) ;
}
2015-12-23 01:29:42 -08:00
} else {
ERROR_LOG ( G3D , " Unexpected buffer freed (%d) but not tracked " , buf ) ;
2014-06-07 16:28:24 -07:00
}
2014-06-07 16:20:12 -07:00
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : FreeVertexArray ( VertexArrayInfo * vai ) {
2015-12-23 16:57:24 -08:00
if ( vai - > vbo ) {
FreeBuffer ( vai - > vbo ) ;
vai - > vbo = 0 ;
}
if ( vai - > ebo ) {
FreeBuffer ( vai - > ebo ) ;
vai - > ebo = 0 ;
}
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : DoFlush ( ) {
2015-07-03 12:05:08 -07:00
PROFILE_THIS_SCOPE ( " flush " ) ;
2017-03-03 14:15:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-01-19 17:05:08 +01:00
gpuStats . numFlushes + + ;
2013-02-24 10:38:30 -08:00
gpuStats . numTrackedVertexArrays = ( int ) vai_ . size ( ) ;
2012-12-19 18:35:37 +01:00
2013-08-25 19:51:06 +02:00
GEPrimitiveType prim = prevPrim_ ;
2013-01-12 17:20:00 +01:00
ApplyDrawState ( prim ) ;
2017-03-03 14:15:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2012-12-20 14:10:42 +01:00
2016-01-17 20:29:34 +01:00
ShaderID vsid ;
Shader * vshader = shaderManager_ - > ApplyVertexShader ( prim , lastVType_ , & vsid ) ;
2012-12-20 14:10:42 +01:00
2014-03-24 10:55:07 +01:00
if ( vshader - > UseHWTransform ( ) ) {
2013-01-19 19:22:15 +01:00
GLuint vbo = 0 , ebo = 0 ;
int vertexCount = 0 ;
2013-01-20 21:52:54 +01:00
bool useElements = true ;
2013-11-10 13:18:52 +01:00
2013-01-20 22:48:29 +01:00
// Cannot cache vertex data with morph enabled.
2013-11-10 13:18:52 +01:00
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 ) {
2017-02-04 11:15:47 +01:00
u32 id = dcid_ ^ gstate . getUVGenMode ( ) ; // This can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263
2017-08-20 11:30:19 +02:00
VertexArrayInfo * vai = vai_ . Get ( id ) ;
if ( ! vai ) {
2013-01-20 13:15:46 +01:00
vai = new VertexArrayInfo ( ) ;
2017-08-20 11:30:19 +02:00
vai_ . Insert ( id , vai ) ;
2013-01-20 13:15:46 +01:00
}
2013-01-19 19:22:15 +01:00
2013-01-20 21:52:54 +01:00
switch ( vai - > status ) {
2013-01-20 13:15:46 +01:00
case VertexArrayInfo : : VAI_NEW :
{
// Haven't seen this one before.
2014-10-26 17:49:24 -07:00
ReliableHashType dataHash = ComputeHash ( ) ;
2013-01-20 13:15:46 +01:00
vai - > hash = dataHash ;
2014-09-07 17:29:28 -07:00
vai - > minihash = ComputeMiniHash ( ) ;
2013-01-20 13:15:46 +01:00
vai - > status = VertexArrayInfo : : VAI_HASHING ;
2013-02-11 23:41:31 -08:00
vai - > drawsUntilNextFullHash = 0 ;
2017-11-19 12:38:52 +01:00
DecodeVerts ( decoded ) ; // writes to indexGen
2013-07-22 19:10:19 +02:00
vai - > numVerts = indexGen . VertexCount ( ) ;
vai - > prim = indexGen . Prim ( ) ;
2013-10-08 22:59:40 +02:00
vai - > maxIndex = indexGen . MaxIndex ( ) ;
2014-03-24 11:19:11 +01:00
vai - > flags = gstate_c . vertexFullAlpha ? VAI_FLAG_VERTEXFULLALPHA : 0 ;
2013-01-20 13:15:46 +01:00
goto rotateVBO ;
}
2013-01-19 19:22:15 +01:00
2013-01-20 13:15:46 +01: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-08-07 22:32:04 +02:00
if ( vai - > lastFrame ! = gpuStats . numFlips ) {
2013-02-10 12:27:43 -08:00
vai - > numFrames + + ;
}
2013-02-11 23:41:31 -08:00
if ( vai - > drawsUntilNextFullHash = = 0 ) {
2014-09-07 17:34:29 -07:00
// 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-07 17:34:29 -07:00
if ( newMiniHash = = vai - > minihash ) {
newHash = ComputeHash ( ) ;
}
if ( newMiniHash ! = vai - > minihash | | newHash ! = vai - > hash ) {
2014-09-07 17:28:12 -07:00
MarkUnreliable ( vai ) ;
2017-11-19 12:38:52 +01:00
DecodeVerts ( decoded ) ;
2013-02-06 00:42:06 +01:00
goto rotateVBO ;
2013-01-20 21:52:54 +01:00
}
2014-09-07 17:36:19 -07:00
if ( vai - > numVerts > 64 ) {
// exponential backoff up to 16 draws, then every 32
vai - > drawsUntilNextFullHash = std : : min ( 32 , vai - > numFrames ) ;
2013-02-12 00:02:53 -08:00
} else {
// Lower numbers seem much more likely to change.
2013-02-12 00:17:12 -08:00
vai - > drawsUntilNextFullHash = 0 ;
2013-02-12 00:02:53 -08:00
}
// TODO: tweak
//if (vai->numFrames > 1000) {
// vai->status = VertexArrayInfo::VAI_RELIABLE;
//}
2013-02-06 00:42:06 +01:00
} else {
2013-02-11 23:41:31 -08:00
vai - > drawsUntilNextFullHash - - ;
2014-09-07 17:29:28 -07:00
u32 newMiniHash = ComputeMiniHash ( ) ;
if ( newMiniHash ! = vai - > minihash ) {
MarkUnreliable ( vai ) ;
2017-11-19 12:38:52 +01:00
DecodeVerts ( decoded ) ;
2014-09-07 17:29:28 -07:00
goto rotateVBO ;
}
2013-01-19 19:22:15 +01:00
}
2013-02-06 00:42:06 +01:00
2013-01-20 13:15:46 +01:00
if ( vai - > vbo = = 0 ) {
2017-11-19 12:38:52 +01:00
DecodeVerts ( decoded ) ;
2013-01-20 13:15:46 +01:00
vai - > numVerts = indexGen . VertexCount ( ) ;
vai - > prim = indexGen . Prim ( ) ;
2013-10-08 22:59:40 +02:00
vai - > maxIndex = indexGen . MaxIndex ( ) ;
2014-03-25 08:19:14 -07:00
vai - > flags = gstate_c . vertexFullAlpha ? VAI_FLAG_VERTEXFULLALPHA : 0 ;
2013-01-20 22:42:11 +01:00
useElements = ! indexGen . SeenOnlyPurePrims ( ) ;
2013-04-20 23:35:53 +02:00
if ( ! useElements & & indexGen . PureCount ( ) ) {
vai - > numVerts = indexGen . PureCount ( ) ;
}
2013-12-09 13:45:17 +01: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
2015-12-22 23:06:56 -08:00
size_t vsz = dec_ - > GetDecVtxFmt ( ) . stride * indexGen . MaxIndex ( ) ;
vai - > vbo = AllocateBuffer ( vsz ) ;
2015-01-11 16:56:29 -08:00
glstate . arrayBuffer . bind ( vai - > vbo ) ;
2015-12-22 23:06:56 -08:00
glBufferData ( GL_ARRAY_BUFFER , vsz , decoded , GL_STATIC_DRAW ) ;
2013-01-20 13:15:46 +01: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 22:42:11 +01:00
if ( useElements ) {
2015-12-22 23:06:56 -08:00
size_t esz = sizeof ( short ) * indexGen . VertexCount ( ) ;
vai - > ebo = AllocateBuffer ( esz ) ;
2015-01-11 16:56:29 -08:00
glstate . elementArrayBuffer . bind ( vai - > ebo ) ;
2015-12-22 23:06:56 -08:00
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , esz , ( GLvoid * ) decIndex , GL_STATIC_DRAW ) ;
2013-01-20 21:52:54 +01:00
} else {
vai - > ebo = 0 ;
2015-01-11 16:56:29 -08:00
glstate . elementArrayBuffer . bind ( vai - > ebo ) ;
2013-01-20 13:15:46 +01:00
}
} else {
2013-02-06 00:42:06 +01:00
gpuStats . numCachedDrawCalls + + ;
2015-01-11 16:56:29 -08:00
glstate . arrayBuffer . bind ( vai - > vbo ) ;
2015-01-11 17:03:45 -08:00
glstate . elementArrayBuffer . bind ( vai - > ebo ) ;
2013-01-20 22:42:11 +01:00
useElements = vai - > ebo ? true : false ;
2013-01-25 00:36:59 +01:00
gpuStats . numCachedVertsDrawn + = vai - > numVerts ;
2014-03-25 08:19:14 -07:00
gstate_c . vertexFullAlpha = vai - > flags & VAI_FLAG_VERTEXFULLALPHA ;
2013-01-19 19:22:15 +01:00
}
2013-01-20 13:15:46 +01:00
vbo = vai - > vbo ;
ebo = vai - > ebo ;
vertexCount = vai - > numVerts ;
2013-08-25 19:51:06 +02:00
prim = static_cast < GEPrimitiveType > ( vai - > prim ) ;
2013-01-20 13:15:46 +01:00
break ;
2013-01-19 19:22:15 +01:00
}
2013-01-20 13:15:46 +01: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-08-07 22:32:04 +02:00
if ( vai - > lastFrame ! = gpuStats . numFlips ) {
2013-02-10 12:27:43 -08:00
vai - > numFrames + + ;
}
2013-01-20 13:15:46 +01:00
gpuStats . numCachedDrawCalls + + ;
2013-01-25 00:36:59 +01:00
gpuStats . numCachedVertsDrawn + = vai - > numVerts ;
2013-01-20 13:15:46 +01:00
vbo = vai - > vbo ;
ebo = vai - > ebo ;
2015-01-11 16:56:29 -08:00
glstate . arrayBuffer . bind ( vbo ) ;
2015-01-11 17:03:45 -08:00
glstate . elementArrayBuffer . bind ( ebo ) ;
2013-01-20 13:15:46 +01:00
vertexCount = vai - > numVerts ;
2013-08-25 19:51:06 +02:00
prim = static_cast < GEPrimitiveType > ( vai - > prim ) ;
2014-03-24 11:19:11 +01:00
gstate_c . vertexFullAlpha = vai - > flags & VAI_FLAG_VERTEXFULLALPHA ;
2013-01-20 13:15:46 +01:00
break ;
}
case VertexArrayInfo : : VAI_UNRELIABLE :
{
vai - > numDraws + + ;
2013-08-07 22:32:04 +02:00
if ( vai - > lastFrame ! = gpuStats . numFlips ) {
2013-02-10 12:27:43 -08:00
vai - > numFrames + + ;
}
2017-11-19 12:38:52 +01:00
DecodeVerts ( decoded ) ;
2013-01-20 13:15:46 +01:00
goto rotateVBO ;
}
}
2013-02-11 23:29:49 -08:00
2013-08-07 22:32:04 +02:00
vai - > lastFrame = gpuStats . numFlips ;
2013-01-20 13:15:46 +01:00
} else {
2017-11-19 12:38:52 +01:00
DecodeVerts ( decoded ) ;
2013-12-09 13:45:17 +01:00
2013-01-19 19:22:15 +01:00
rotateVBO :
2013-01-25 00:36:59 +01:00
gpuStats . numUncachedVertsDrawn + = indexGen . VertexCount ( ) ;
2013-01-20 21:52:54 +01:00
useElements = ! indexGen . SeenOnlyPurePrims ( ) ;
2013-01-28 19:04:12 +01:00
vertexCount = indexGen . VertexCount ( ) ;
2013-04-20 23:35:53 +02:00
if ( ! useElements & & indexGen . PureCount ( ) ) {
vertexCount = indexGen . PureCount ( ) ;
}
2015-01-11 16:56:29 -08:00
glstate . arrayBuffer . unbind ( ) ;
glstate . elementArrayBuffer . unbind ( ) ;
2013-08-09 05:12:26 -04:00
2013-01-20 21:52:54 +01:00
prim = indexGen . Prim ( ) ;
2013-01-19 19:22:15 +01:00
}
2013-12-09 13:45:17 +01:00
2013-11-14 11:01:31 +01:00
VERBOSE_LOG ( G3D , " Flush prim %i! %i verts in one go " , prim , vertexCount ) ;
2014-03-24 17:33:20 +01: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 ) ;
}
2013-01-19 19:22:15 +01:00
2014-09-10 23:43:48 -07:00
ApplyDrawStateLate ( ) ;
2016-05-21 15:30:54 -07:00
if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) & & vbo = = 0 ) {
vbo = BindBuffer ( decoded , dec_ - > GetDecVtxFmt ( ) . stride * indexGen . MaxIndex ( ) ) ;
if ( useElements ) {
ebo = BindElementBuffer ( decIndex , sizeof ( short ) * indexGen . VertexCount ( ) ) ;
}
}
2016-01-17 20:29:34 +01:00
LinkedShader * program = shaderManager_ - > ApplyFragmentShader ( vsid , vshader , lastVType_ , prim ) ;
2013-03-24 12:28:42 +01:00
SetupDecFmtForDraw ( program , dec_ - > GetDecVtxFmt ( ) , vbo ? 0 : decoded ) ;
2014-03-24 10:55:07 +01:00
2013-01-20 21:52:54 +01:00
if ( useElements ) {
2017-01-09 03:37:21 +09:00
if ( gstate_c . bezier | | gstate_c . spline )
// Instanced rendering for instanced tessellation
glDrawElementsInstanced ( glprim [ prim ] , vertexCount , GL_UNSIGNED_SHORT , ebo ? 0 : ( GLvoid * ) decIndex , numPatches ) ;
else
glDrawElements ( glprim [ prim ] , vertexCount , GL_UNSIGNED_SHORT , ebo ? 0 : ( GLvoid * ) decIndex ) ;
2013-01-20 21:52:54 +01:00
} else {
glDrawArrays ( glprim [ prim ] , 0 , vertexCount ) ;
2012-12-26 08:54:33 +01:00
}
2012-12-20 14:10:42 +01:00
} else {
2017-11-19 12:38:52 +01:00
DecodeVerts ( decoded ) ;
2014-03-24 17:33:20 +01: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-03-24 11:19:11 +01:00
2013-01-25 00:36:59 +01:00
gpuStats . numUncachedVertsDrawn + = indexGen . VertexCount ( ) ;
2013-01-19 19:22:15 +01:00
prim = indexGen . Prim ( ) ;
2013-04-21 19:28:24 +02:00
// Undo the strip optimization, not supported by the SW code yet.
if ( prim = = GE_PRIM_TRIANGLE_STRIP )
prim = GE_PRIM_TRIANGLES ;
2013-01-19 19:22:15 +01:00
2014-09-13 13:03:37 +02:00
TransformedVertex * drawBuffer = NULL ;
int numTrans ;
bool drawIndexed = false ;
u16 * inds = decIndex ;
2017-12-02 09:58:13 +01:00
SoftwareTransformResult result { } ;
2016-03-12 13:37:08 -08:00
// TODO: Keep this static? Faster than repopulating?
2017-12-02 09:58:13 +01:00
SoftwareTransformParams params { } ;
2016-03-12 13:37:08 -08:00
params . decoded = decoded ;
params . transformed = transformed ;
params . transformedExpanded = transformedExpanded ;
params . fbman = framebufferManager_ ;
params . texCache = textureCache_ ;
2017-12-02 09:58:13 +01:00
params . allowClear = true ;
2016-03-12 13:37:08 -08:00
params . allowSeparateAlphaClear = true ;
2015-01-15 23:58:07 +01:00
int maxIndex = indexGen . MaxIndex ( ) ;
2017-11-10 15:59:36 +01:00
int vertexCount = indexGen . VertexCount ( ) ;
// TODO: Split up into multiple draw calls for GLES 2.0 where you can't guarantee support for more than 0x10000 verts.
# if defined(MOBILE_DEVICE)
if ( vertexCount > 0x10000 / 3 )
vertexCount = 0x10000 / 3 ;
# endif
2014-09-13 13:03:37 +02:00
SoftwareTransform (
2017-11-10 15:59:36 +01:00
prim , 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 ) ;
2015-09-13 06:53:25 -07:00
ApplyDrawStateLate ( ) ;
2015-09-25 19:08:48 +02:00
2016-01-17 20:29:34 +01:00
LinkedShader * program = shaderManager_ - > ApplyFragmentShader ( vsid , vshader , lastVType_ , prim ) ;
2015-09-13 06:53:25 -07:00
2014-09-13 13:03:37 +02:00
if ( result . action = = SW_DRAW_PRIMITIVES ) {
if ( result . setStencil ) {
glstate . stencilFunc . set ( GL_ALWAYS , result . stencilValue , 255 ) ;
}
const int vertexSize = sizeof ( transformed [ 0 ] ) ;
bool doTextureProjection = gstate . getUVGenMode ( ) = = GE_TEXMAP_TEXTURE_MATRIX ;
2015-12-13 22:48:21 -08:00
const uint8_t * bufferStart = ( const uint8_t * ) drawBuffer ;
if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) ) {
bufferStart = 0 ;
BindBuffer ( drawBuffer , vertexSize * maxIndex ) ;
if ( drawIndexed ) {
BindElementBuffer ( inds , sizeof ( short ) * numTrans ) ;
inds = 0 ;
}
} else {
glstate . arrayBuffer . unbind ( ) ;
glstate . elementArrayBuffer . unbind ( ) ;
}
glVertexAttribPointer ( ATTR_POSITION , 4 , GL_FLOAT , GL_FALSE , vertexSize , bufferStart ) ;
2014-09-13 13:03:37 +02:00
int attrMask = program - > attrMask ;
2015-12-13 22:48:21 -08:00
if ( attrMask & ( 1 < < ATTR_TEXCOORD ) ) glVertexAttribPointer ( ATTR_TEXCOORD , doTextureProjection ? 3 : 2 , GL_FLOAT , GL_FALSE , vertexSize , bufferStart + offsetof ( TransformedVertex , u ) ) ;
if ( attrMask & ( 1 < < ATTR_COLOR0 ) ) glVertexAttribPointer ( ATTR_COLOR0 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , vertexSize , bufferStart + offsetof ( TransformedVertex , color0 ) ) ;
if ( attrMask & ( 1 < < ATTR_COLOR1 ) ) glVertexAttribPointer ( ATTR_COLOR1 , 3 , GL_UNSIGNED_BYTE , GL_TRUE , vertexSize , bufferStart + offsetof ( TransformedVertex , color1 ) ) ;
2014-09-13 13:03:37 +02:00
if ( drawIndexed ) {
glDrawElements ( glprim [ prim ] , numTrans , GL_UNSIGNED_SHORT , inds ) ;
} else {
glDrawArrays ( glprim [ prim ] , 0 , numTrans ) ;
}
} else if ( result . action = = SW_CLEAR ) {
u32 clearColor = result . color ;
float clearDepth = result . depth ;
const float col [ 4 ] = {
( ( clearColor & 0xFF ) ) / 255.0f ,
( ( clearColor & 0xFF00 ) > > 8 ) / 255.0f ,
( ( clearColor & 0xFF0000 ) > > 16 ) / 255.0f ,
( ( clearColor & 0xFF000000 ) > > 24 ) / 255.0f ,
} ;
bool colorMask = gstate . isClearModeColorMask ( ) ;
bool alphaMask = gstate . isClearModeAlphaMask ( ) ;
bool depthMask = gstate . isClearModeDepthMask ( ) ;
if ( depthMask ) {
framebufferManager_ - > SetDepthUpdated ( ) ;
}
// Note that scissor may still apply while clearing. Turn off other tests for the clear.
glstate . stencilTest . disable ( ) ;
glstate . stencilMask . set ( 0xFF ) ;
glstate . depthTest . disable ( ) ;
GLbitfield target = 0 ;
if ( colorMask | | alphaMask ) target | = GL_COLOR_BUFFER_BIT ;
if ( alphaMask ) target | = GL_STENCIL_BUFFER_BIT ;
if ( depthMask ) target | = GL_DEPTH_BUFFER_BIT ;
2014-09-14 08:00:35 -07:00
glstate . colorMask . set ( colorMask , colorMask , colorMask , alphaMask ) ;
2014-09-13 13:03:37 +02:00
glClearColor ( col [ 0 ] , col [ 1 ] , col [ 2 ] , col [ 3 ] ) ;
# ifdef USING_GLES2
glClearDepthf ( clearDepth ) ;
# else
glClearDepth ( clearDepth ) ;
# endif
// Stencil takes alpha.
glClearStencil ( clearColor > > 24 ) ;
glClear ( target ) ;
2015-07-26 22:38:40 +02:00
framebufferManager_ - > SetColorUpdated ( gstate_c . skipDrawReason ) ;
2016-05-19 20:55:34 -07:00
2016-09-18 19:40:44 -07:00
int scissorX1 = gstate . getScissorX1 ( ) ;
int scissorY1 = gstate . getScissorY1 ( ) ;
2016-05-19 20:55:34 -07:00
int scissorX2 = gstate . getScissorX2 ( ) + 1 ;
int scissorY2 = gstate . getScissorY2 ( ) + 1 ;
framebufferManager_ - > SetSafeSize ( scissorX2 , scissorY2 ) ;
2016-09-18 19:40:44 -07:00
2017-01-28 10:04:50 +01:00
if ( g_Config . bBlockTransferGPU & & ( gstate_c . featureFlags & GPU_USE_CLEAR_RAM_HACK ) & & colorMask & & ( alphaMask | | gstate . FrameBufFormat ( ) = = GE_FORMAT_565 ) ) {
2017-04-09 15:10:07 -07:00
framebufferManager_ - > ApplyClearToMemory ( scissorX1 , scissorY1 , scissorX2 , scissorY2 , clearColor ) ;
2016-09-18 19:40:44 -07:00
}
2014-09-13 13:03:37 +02:00
}
2012-12-20 14:10:42 +01:00
}
2012-12-19 18:35:37 +01:00
2014-10-19 20:25:04 +02:00
gpuStats . numDrawCalls + = numDrawCalls ;
2017-06-02 12:03:46 +02:00
gpuStats . numVertsSubmitted + = vertexCountInDrawCalls_ ;
2014-10-19 20:25:04 +02:00
2012-12-21 19:16:17 +01:00
indexGen . Reset ( ) ;
2014-03-23 15:34:04 +01:00
decodedVerts_ = 0 ;
2013-01-19 17:05:08 +01:00
numDrawCalls = 0 ;
2017-06-02 12:03:46 +02:00
vertexCountInDrawCalls_ = 0 ;
2013-11-10 15:22:24 +01:00
decodeCounter_ = 0 ;
2014-03-23 01:51:51 +01:00
dcid_ = 0 ;
2013-08-25 19:51:06 +02:00
prevPrim_ = GE_PRIM_INVALID ;
2014-03-24 11:19:11 +01:00
gstate_c . vertexFullAlpha = true ;
2015-07-26 22:38:40 +02:00
framebufferManager_ - > SetColorUpdated ( gstate_c . skipDrawReason ) ;
2013-09-22 00:18:46 -07: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-02-08 10:29:22 -08:00
# ifndef MOBILE_DEVICE
2013-09-22 00:18:46 -07:00
host - > GPUNotifyDraw ( ) ;
# endif
2017-03-03 14:15:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2012-12-21 21:49:09 +01:00
}
2013-09-24 13:58:14 +02:00
2016-04-10 10:21:48 +02:00
GLuint DrawEngineGLES : : BindBuffer ( const void * p , size_t sz ) {
2015-12-13 22:48:21 -08:00
// Get a new buffer each time we need one.
2015-12-22 23:06:56 -08:00
GLuint buf = AllocateBuffer ( sz ) ;
2015-12-13 22:48:21 -08:00
glstate . arrayBuffer . bind ( buf ) ;
// These aren't used more than once per frame, so let's use GL_STREAM_DRAW.
glBufferData ( GL_ARRAY_BUFFER , sz , p , GL_STREAM_DRAW ) ;
buffersThisFrame_ . push_back ( buf ) ;
return buf ;
}
2016-04-10 10:21:48 +02:00
GLuint DrawEngineGLES : : BindBuffer ( const void * p1 , size_t sz1 , const void * p2 , size_t sz2 ) {
2015-12-22 23:06:56 -08:00
GLuint buf = AllocateBuffer ( sz1 + sz2 ) ;
2015-12-13 22:48:21 -08:00
glstate . arrayBuffer . bind ( buf ) ;
glBufferData ( GL_ARRAY_BUFFER , sz1 + sz2 , nullptr , GL_STREAM_DRAW ) ;
glBufferSubData ( GL_ARRAY_BUFFER , 0 , sz1 , p1 ) ;
glBufferSubData ( GL_ARRAY_BUFFER , sz1 , sz2 , p2 ) ;
buffersThisFrame_ . push_back ( buf ) ;
return buf ;
}
2016-04-10 10:21:48 +02:00
GLuint DrawEngineGLES : : BindElementBuffer ( const void * p , size_t sz ) {
2015-12-22 23:06:56 -08:00
GLuint buf = AllocateBuffer ( sz ) ;
2015-12-13 22:48:21 -08:00
glstate . elementArrayBuffer . bind ( buf ) ;
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sz , p , GL_STREAM_DRAW ) ;
buffersThisFrame_ . push_back ( buf ) ;
return buf ;
}
2016-04-10 10:21:48 +02:00
void DrawEngineGLES : : DecimateBuffers ( ) {
2015-12-13 22:48:21 -08:00
for ( GLuint buf : buffersThisFrame_ ) {
FreeBuffer ( buf ) ;
}
buffersThisFrame_ . clear ( ) ;
2015-12-23 01:29:42 -08:00
if ( - - bufferDecimationCounter_ < = 0 ) {
bufferDecimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL ;
} else {
return ;
}
// Let's not keep too many around, will eat up memory.
// First check if there's any to free, and only check if it seems somewhat full.
bool hasOld = false ;
2015-12-23 10:02:01 -08:00
if ( bufferNameCacheSize_ > VERTEXCACHE_NAME_CACHE_FULL_BYTES ) {
2015-12-23 01:29:42 -08:00
for ( GLuint buf : bufferNameCache_ ) {
const BufferNameInfo & info = bufferNameInfo_ [ buf ] ;
const int age = gpuStats . numFlips - info . lastFrame ;
if ( ! info . used & & age > VERTEXCACHE_NAME_CACHE_MAX_AGE ) {
hasOld = true ;
break ;
}
}
}
if ( hasOld ) {
2015-12-23 10:02:01 -08:00
// Okay, it is. Let's rebuild the array.
2015-12-23 01:29:42 -08:00
std : : vector < GLuint > toFree ;
std : : vector < GLuint > toKeep ;
2015-12-23 10:02:01 -08:00
toKeep . reserve ( bufferNameCache_ . size ( ) ) ;
2015-12-23 01:29:42 -08:00
2015-12-23 10:02:01 -08:00
for ( size_t i = 0 , n = bufferNameCache_ . size ( ) ; i < n ; + + i ) {
const GLuint buf = bufferNameCache_ [ i ] ;
2015-12-23 01:29:42 -08:00
const BufferNameInfo & info = bufferNameInfo_ [ buf ] ;
const int age = gpuStats . numFlips - info . lastFrame ;
if ( ! info . used & & age > VERTEXCACHE_NAME_CACHE_MAX_AGE ) {
toFree . push_back ( buf ) ;
2015-12-23 10:02:01 -08:00
bufferNameCacheSize_ - = bufferNameInfo_ [ buf ] . sz ;
2015-12-23 01:29:42 -08:00
bufferNameInfo_ . erase ( buf ) ;
2015-12-23 10:02:01 -08:00
// If we've removed all we want to this round, keep the rest and abort.
if ( toFree . size ( ) > = VERTEXCACHE_NAME_DECIMATION_MAX & & i + 1 < bufferNameCache_ . size ( ) ) {
toKeep . insert ( toKeep . end ( ) , bufferNameCache_ . begin ( ) + i + 1 , bufferNameCache_ . end ( ) ) ;
break ;
}
2015-12-23 01:29:42 -08:00
} else {
toKeep . push_back ( buf ) ;
}
}
if ( ! toFree . empty ( ) ) {
2016-02-28 15:10:41 -08:00
bufferNameCache_ = toKeep ;
// TODO: Rebuild?
freeSizedBuffers_ . clear ( ) ;
2015-12-23 01:29:42 -08:00
glstate . arrayBuffer . unbind ( ) ;
glstate . elementArrayBuffer . unbind ( ) ;
glDeleteBuffers ( ( GLsizei ) toFree . size ( ) , & toFree [ 0 ] ) ;
}
}
2015-12-13 22:48:21 -08:00
}
2016-04-10 10:21:48 +02:00
bool DrawEngineGLES : : IsCodePtrVertexDecoder ( const u8 * ptr ) const {
2014-03-29 16:51:38 -07:00
return decJitCache_ - > IsInSpace ( ptr ) ;
}
2017-01-10 14:41:01 +09:00
2017-01-09 03:37:21 +09:00
void DrawEngineGLES : : TessellationDataTransferGLES : : SendDataToShader ( const float * pos , const float * tex , const float * col , int size , bool hasColor , bool hasTexCoords ) {
2017-01-27 12:49:27 +09:00
# ifndef USING_GLES2
2017-03-03 12:32:01 +01:00
if ( isAllowTexture1D_ ) {
2017-01-26 22:17:31 +09:00
// Position
2017-02-25 17:22:54 +09:00
glActiveTexture ( GL_TEXTURE4 ) ;
2017-01-26 22:17:31 +09:00
glBindTexture ( GL_TEXTURE_1D , data_tex [ 0 ] ) ;
2017-01-10 14:16:03 +09:00
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
2017-01-26 22:17:31 +09:00
if ( prevSize < size ) {
2017-03-24 13:29:05 +09:00
glTexImage1D ( GL_TEXTURE_1D , 0 , GL_RGBA32F , size , 0 , GL_RGBA , GL_FLOAT , ( GLfloat * ) pos ) ;
2017-01-26 22:17:31 +09:00
prevSize = size ;
2017-01-09 03:37:21 +09:00
} else {
2017-03-24 13:29:05 +09:00
glTexSubImage1D ( GL_TEXTURE_1D , 0 , 0 , size , GL_RGBA , GL_FLOAT , ( GLfloat * ) pos ) ;
2017-01-09 03:37:21 +09:00
}
2017-01-26 22:17:31 +09:00
// Texcoords
if ( hasTexCoords ) {
2017-02-25 17:22:54 +09:00
glActiveTexture ( GL_TEXTURE5 ) ;
2017-01-26 22:17:31 +09:00
glBindTexture ( GL_TEXTURE_1D , data_tex [ 1 ] ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
if ( prevSizeTex < size ) {
2017-03-24 13:29:05 +09:00
glTexImage1D ( GL_TEXTURE_1D , 0 , GL_RGBA32F , size , 0 , GL_RGBA , GL_FLOAT , ( GLfloat * ) tex ) ;
2017-01-26 22:17:31 +09:00
prevSizeTex = size ;
} else {
2017-03-24 13:29:05 +09:00
glTexSubImage1D ( GL_TEXTURE_1D , 0 , 0 , size , GL_RGBA , GL_FLOAT , ( GLfloat * ) tex ) ;
2017-01-26 22:17:31 +09:00
}
}
// Color
2017-02-25 17:22:54 +09:00
glActiveTexture ( GL_TEXTURE6 ) ;
2017-01-26 22:17:31 +09:00
glBindTexture ( GL_TEXTURE_1D , data_tex [ 2 ] ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_1D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
int sizeColor = hasColor ? size : 1 ;
if ( prevSizeCol < sizeColor ) {
glTexImage1D ( GL_TEXTURE_1D , 0 , GL_RGBA32F , sizeColor , 0 , GL_RGBA , GL_FLOAT , ( GLfloat * ) col ) ;
prevSizeCol = sizeColor ;
} else {
glTexSubImage1D ( GL_TEXTURE_1D , 0 , 0 , sizeColor , GL_RGBA , GL_FLOAT , ( GLfloat * ) col ) ;
}
2017-01-27 12:49:27 +09:00
} else
# endif
{
2017-01-26 22:17:31 +09:00
// Position
2017-02-25 17:22:54 +09:00
glActiveTexture ( GL_TEXTURE4 ) ;
2017-01-26 22:17:31 +09:00
glBindTexture ( GL_TEXTURE_2D , data_tex [ 0 ] ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
if ( prevSize < size ) {
2017-03-24 13:29:05 +09:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA32F , size , 1 , 0 , GL_RGBA , GL_FLOAT , ( GLfloat * ) pos ) ;
2017-01-26 22:17:31 +09:00
prevSize = size ;
} else {
2017-03-24 13:29:05 +09:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , size , 1 , GL_RGBA , GL_FLOAT , ( GLfloat * ) pos ) ;
2017-01-26 22:17:31 +09:00
}
// Texcoords
if ( hasTexCoords ) {
2017-02-25 17:22:54 +09:00
glActiveTexture ( GL_TEXTURE5 ) ;
2017-01-26 22:17:31 +09:00
glBindTexture ( GL_TEXTURE_2D , data_tex [ 1 ] ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
if ( prevSizeTex < size ) {
2017-03-24 13:29:05 +09:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA32F , size , 1 , 0 , GL_RGBA , GL_FLOAT , ( GLfloat * ) tex ) ;
2017-01-26 22:17:31 +09:00
prevSizeTex = size ;
} else {
2017-03-24 13:29:05 +09:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , size , 1 , GL_RGBA , GL_FLOAT , ( GLfloat * ) tex ) ;
2017-01-26 22:17:31 +09:00
}
}
// Color
2017-02-25 17:22:54 +09:00
glActiveTexture ( GL_TEXTURE6 ) ;
2017-01-26 22:17:31 +09:00
glBindTexture ( GL_TEXTURE_2D , data_tex [ 2 ] ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
int sizeColor = hasColor ? size : 1 ;
if ( prevSizeCol < sizeColor ) {
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA32F , sizeColor , 1 , 0 , GL_RGBA , GL_FLOAT , ( GLfloat * ) col ) ;
prevSizeCol = sizeColor ;
} else {
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , sizeColor , 1 , GL_RGBA , GL_FLOAT , ( GLfloat * ) col ) ;
}
2017-01-09 03:37:21 +09:00
}
glActiveTexture ( GL_TEXTURE0 ) ;
2017-03-03 14:15:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-01-09 03:37:21 +09:00
}