2021-03-02 20:57:25 -08:00
# include "ppsspp_config.h"
2019-06-23 15:30:30 -07:00
# include <algorithm>
2020-08-15 15:51:41 +02:00
2020-10-04 23:24:14 +02:00
# include "Common/GPU/OpenGL/GLCommon.h"
# include "Common/GPU/OpenGL/GLDebugLog.h"
# include "Common/GPU/OpenGL/GLFeatures.h"
# include "Common/GPU/OpenGL/DataFormatGL.h"
2020-10-04 00:25:21 +02:00
# include "Common/Math/math_util.h"
2022-08-27 17:33:37 +02:00
# include "Common/VR/PPSSPPVR.h"
2017-11-18 15:42:39 +01:00
2020-08-15 15:51:41 +02:00
# include "Common/Log.h"
# include "Common/MemoryUtil.h"
2020-09-29 12:19:22 +02:00
# include "Common/StringUtils.h"
2020-10-04 00:25:21 +02:00
# include "Common/Data/Convert/SmallDataConvert.h"
2020-08-15 15:51:41 +02:00
# include "Core/Reporting.h"
# include "GLQueueRunner.h"
# include "GLRenderManager.h"
# include "DataFormatGL.h"
2021-09-11 17:53:50 -07:00
// These are the same value, alias for simplicity.
# if defined(GL_CLIP_DISTANCE0_EXT) && !defined(GL_CLIP_DISTANCE0)
# define GL_CLIP_DISTANCE0 GL_CLIP_DISTANCE0_EXT
2021-09-11 19:08:02 -07:00
# elif !defined(GL_CLIP_DISTANCE0)
# define GL_CLIP_DISTANCE0 0x3000
2021-09-11 17:53:50 -07:00
# endif
2021-06-12 14:10:57 -07:00
static constexpr int TEXCACHE_NAME_CACHE_SIZE = 16 ;
2017-11-18 15:42:39 +01:00
2021-03-02 20:57:25 -08:00
# if PPSSPP_PLATFORM(IOS)
2017-12-27 14:46:01 +01:00
extern void bindDefaultFBO ( ) ;
# endif
2017-12-13 01:05:33 +01:00
// Workaround for Retroarch. Simply declare
// extern GLuint g_defaultFBO;
// and set is as appropriate. Can adjust the variables in ext/native/base/display.h as
// appropriate.
GLuint g_defaultFBO = 0 ;
2017-11-19 17:37:56 +01:00
void GLQueueRunner : : CreateDeviceObjects ( ) {
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2022-08-07 11:09:34 +02:00
if ( caps_ . anisoSupported ) {
2018-03-12 23:11:58 +01:00
glGetFloatv ( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT , & maxAnisotropyLevel_ ) ;
} else {
maxAnisotropyLevel_ = 0.0f ;
}
2018-01-21 09:17:24 -08:00
if ( gl_extensions . ARB_vertex_array_object ) {
glGenVertexArrays ( 1 , & globalVAO_ ) ;
}
2018-01-20 09:35:43 -08:00
// An eternal optimist.
sawOutOfMemory_ = false ;
2018-01-21 08:49:34 -08:00
2020-12-14 19:34:41 +01:00
// Populate some strings from the GL thread so they can be queried from thin3d.
// TODO: Merge with GLFeatures.cpp/h
2018-01-21 08:49:34 -08:00
auto populate = [ & ] ( int name ) {
const GLubyte * value = glGetString ( name ) ;
if ( ! value )
glStrings_ [ name ] = " ? " ;
else
glStrings_ [ name ] = ( const char * ) value ;
} ;
populate ( GL_VENDOR ) ;
populate ( GL_RENDERER ) ;
populate ( GL_VERSION ) ;
populate ( GL_SHADING_LANGUAGE_VERSION ) ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2020-05-24 19:58:59 -07:00
useDebugGroups_ = ! gl_extensions . IsGLES & & gl_extensions . VersionGEThan ( 4 , 3 ) ;
2017-11-18 15:42:39 +01:00
}
void GLQueueRunner : : DestroyDeviceObjects ( ) {
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-19 17:37:56 +01:00
if ( ! nameCache_ . empty ( ) ) {
glDeleteTextures ( ( GLsizei ) nameCache_ . size ( ) , & nameCache_ [ 0 ] ) ;
nameCache_ . clear ( ) ;
}
2018-01-21 09:17:24 -08:00
if ( gl_extensions . ARB_vertex_array_object ) {
glDeleteVertexArrays ( 1 , & globalVAO_ ) ;
}
2018-01-28 21:28:16 +01:00
delete [ ] readbackBuffer_ ;
2018-11-21 22:15:01 +01:00
readbackBuffer_ = nullptr ;
2018-01-28 21:28:16 +01:00
readbackBufferSize_ = 0 ;
delete [ ] tempBuffer_ ;
2018-11-21 22:15:01 +01:00
tempBuffer_ = nullptr ;
2018-01-28 21:28:16 +01:00
tempBufferSize_ = 0 ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
}
2018-06-30 09:16:25 -07:00
template < typename Getiv , typename GetLog >
static std : : string GetInfoLog ( GLuint name , Getiv getiv , GetLog getLog ) {
GLint bufLength = 0 ;
getiv ( name , GL_INFO_LOG_LENGTH , & bufLength ) ;
if ( bufLength < = 0 )
bufLength = 2048 ;
std : : string infoLog ;
infoLog . resize ( bufLength ) ;
GLsizei len = 0 ;
getLog ( name , ( GLsizei ) infoLog . size ( ) , & len , & infoLog [ 0 ] ) ;
if ( len < = 0 )
return " (unknown reason) " ;
infoLog . resize ( len ) ;
return infoLog ;
}
2022-08-14 18:51:45 +02:00
int GLQueueRunner : : GetStereoBufferIndex ( const char * uniformName ) {
2022-08-16 17:39:54 +02:00
if ( ! uniformName ) return - 1 ;
else if ( strcmp ( uniformName , " u_view " ) = = 0 ) return 0 ;
2022-09-04 21:12:18 +02:00
else if ( strcmp ( uniformName , " u_proj_lens " ) = = 0 ) return 1 ;
2022-08-14 18:51:45 +02:00
else return - 1 ;
}
std : : string GLQueueRunner : : GetStereoBufferLayout ( const char * uniformName ) {
if ( strcmp ( uniformName , " u_view " ) = = 0 ) return " ViewMatrices " ;
2022-09-04 21:12:18 +02:00
else if ( strcmp ( uniformName , " u_proj_lens " ) = = 0 ) return " ProjectionMatrix " ;
2022-08-24 23:38:57 +02:00
else return " undefined " ;
2022-08-14 18:51:45 +02:00
}
2018-10-06 13:24:50 +02:00
void GLQueueRunner : : RunInitSteps ( const std : : vector < GLRInitStep > & steps , bool skipGLCalls ) {
if ( skipGLCalls ) {
// Some bookkeeping still needs to be done.
for ( size_t i = 0 ; i < steps . size ( ) ; i + + ) {
const GLRInitStep & step = steps [ i ] ;
switch ( step . stepType ) {
case GLRInitStepType : : BUFFER_SUBDATA :
{
if ( step . buffer_subdata . deleteData )
delete [ ] step . buffer_subdata . data ;
break ;
}
case GLRInitStepType : : TEXTURE_IMAGE :
{
if ( step . texture_image . allocType = = GLRAllocType : : ALIGNED ) {
FreeAlignedMemory ( step . texture_image . data ) ;
2018-10-06 21:36:47 +02:00
} else if ( step . texture_image . allocType = = GLRAllocType : : NEW ) {
2018-10-06 13:24:50 +02:00
delete [ ] step . texture_image . data ;
}
break ;
}
case GLRInitStepType : : CREATE_PROGRAM :
{
WARN_LOG ( G3D , " CREATE_PROGRAM found with skipGLCalls, not good " ) ;
break ;
}
case GLRInitStepType : : CREATE_SHADER :
{
2018-10-06 21:36:47 +02:00
WARN_LOG ( G3D , " CREATE_SHADER found with skipGLCalls, not good " ) ;
2018-10-06 13:24:50 +02:00
break ;
}
default :
break ;
}
}
return ;
}
2020-05-24 19:58:59 -07:00
# if !defined(USING_GLES2)
if ( useDebugGroups_ )
glPushDebugGroup ( GL_DEBUG_SOURCE_APPLICATION , 1 , - 1 , " InitSteps " ) ;
# endif
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-14 14:53:27 +01:00
glActiveTexture ( GL_TEXTURE0 ) ;
GLuint boundTexture = ( GLuint ) - 1 ;
2018-01-20 09:35:43 -08:00
bool allocatedTextures = false ;
2017-12-14 14:53:27 +01:00
2018-02-11 07:03:23 -08:00
for ( size_t i = 0 ; i < steps . size ( ) ; i + + ) {
2017-11-18 16:50:49 +01:00
const GLRInitStep & step = steps [ i ] ;
switch ( step . stepType ) {
case GLRInitStepType : : CREATE_TEXTURE :
2017-11-18 21:51:17 +01:00
{
GLRTexture * tex = step . create_texture . texture ;
glGenTextures ( 1 , & tex - > texture ) ;
glBindTexture ( tex - > target , tex - > texture ) ;
2017-12-14 14:53:27 +01:00
boundTexture = tex - > texture ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
break ;
2017-11-18 21:51:17 +01:00
}
2017-11-18 20:17:17 +01:00
case GLRInitStepType : : CREATE_BUFFER :
{
GLRBuffer * buffer = step . create_buffer . buffer ;
2018-10-06 13:24:50 +02:00
glGenBuffers ( 1 , & buffer - > buffer_ ) ;
glBindBuffer ( buffer - > target_ , buffer - > buffer_ ) ;
2017-11-18 20:17:17 +01:00
glBufferData ( buffer - > target_ , step . create_buffer . size , nullptr , step . create_buffer . usage ) ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 20:17:17 +01:00
break ;
}
case GLRInitStepType : : BUFFER_SUBDATA :
{
GLRBuffer * buffer = step . buffer_subdata . buffer ;
2020-05-16 23:48:56 -07:00
glBindBuffer ( buffer - > target_ , buffer - > buffer_ ) ;
glBufferSubData ( buffer - > target_ , step . buffer_subdata . offset , step . buffer_subdata . size , step . buffer_subdata . data ) ;
2017-11-19 00:23:52 +01:00
if ( step . buffer_subdata . deleteData )
delete [ ] step . buffer_subdata . data ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 20:17:17 +01:00
break ;
}
2017-11-18 16:50:49 +01:00
case GLRInitStepType : : CREATE_PROGRAM :
{
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
GLRProgram * program = step . create_program . program ;
program - > program = glCreateProgram ( ) ;
2020-07-19 17:47:02 +02:00
_assert_msg_ ( step . create_program . num_shaders > 0 , " Can't create a program with zero shaders " ) ;
2018-11-22 07:58:08 -08:00
bool anyFailed = false ;
2018-02-11 07:03:23 -08:00
for ( int j = 0 ; j < step . create_program . num_shaders ; j + + ) {
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( step . create_program . shaders [ j ] - > shader , " Can't create a program with a null shader " ) ;
2018-11-22 07:58:08 -08:00
anyFailed = anyFailed | | step . create_program . shaders [ j ] - > failed ;
2018-02-11 07:03:23 -08:00
glAttachShader ( program - > program , step . create_program . shaders [ j ] - > shader ) ;
2017-11-18 16:50:49 +01:00
}
for ( auto iter : program - > semantics_ ) {
glBindAttribLocation ( program - > program , iter . location , iter . attrib ) ;
}
2017-11-19 00:43:58 +01:00
# if !defined(USING_GLES2)
if ( step . create_program . support_dual_source ) {
2022-08-07 11:09:34 +02:00
_dbg_assert_msg_ ( caps_ . dualSourceBlend , " ARB/EXT_blend_func_extended required for dual src blend " ) ;
2017-11-19 00:43:58 +01:00
// Dual source alpha
glBindFragDataLocationIndexed ( program - > program , 0 , 0 , " fragColor0 " ) ;
glBindFragDataLocationIndexed ( program - > program , 0 , 1 , " fragColor1 " ) ;
2021-04-10 16:26:39 -07:00
} else if ( gl_extensions . VersionGEThan ( 3 , 0 , 0 ) ) {
2017-11-19 00:43:58 +01:00
glBindFragDataLocation ( program - > program , 0 , " fragColor0 " ) ;
}
2021-03-02 20:57:25 -08:00
# elif !PPSSPP_PLATFORM(IOS)
2022-02-21 08:57:21 -08:00
if ( gl_extensions . GLES3 & & step . create_program . support_dual_source ) {
// For GLES2, we use gl_SecondaryFragColorEXT as fragColor1.
_dbg_assert_msg_ ( gl_extensions . EXT_blend_func_extended , " EXT_blend_func_extended required for dual src " ) ;
2017-12-14 17:50:40 +01:00
glBindFragDataLocationIndexedEXT ( program - > program , 0 , 0 , " fragColor0 " ) ;
glBindFragDataLocationIndexedEXT ( program - > program , 0 , 1 , " fragColor1 " ) ;
2017-11-19 00:43:58 +01:00
}
# endif
2017-11-18 16:50:49 +01:00
glLinkProgram ( program - > program ) ;
GLint linkStatus = GL_FALSE ;
glGetProgramiv ( program - > program , GL_LINK_STATUS , & linkStatus ) ;
if ( linkStatus ! = GL_TRUE ) {
2018-06-30 09:16:25 -07:00
std : : string infoLog = GetInfoLog ( program - > program , glGetProgramiv , glGetProgramInfoLog ) ;
// TODO: Could be other than vs/fs. Also, we're assuming order here...
2018-06-30 10:10:42 -07:00
GLRShader * vs = step . create_program . shaders [ 0 ] ;
GLRShader * fs = step . create_program . num_shaders > 1 ? step . create_program . shaders [ 1 ] : nullptr ;
std : : string vsDesc = vs - > desc + ( vs - > failed ? " (failed) " : " " ) ;
std : : string fsDesc = fs ? ( fs - > desc + ( fs - > failed ? " (failed) " : " " ) ) : " (none) " ;
const char * vsCode = vs - > code . c_str ( ) ;
const char * fsCode = fs ? fs - > code . c_str ( ) : " (none) " ;
2018-11-22 07:58:08 -08:00
if ( ! anyFailed )
Reporting : : ReportMessage ( " Error in shader program link: info: %s \n fs: %s \n %s \n vs: %s \n %s " , infoLog . c_str ( ) , fsDesc . c_str ( ) , fsCode , vsDesc . c_str ( ) , vsCode ) ;
2018-06-30 09:16:25 -07:00
2020-08-15 15:51:41 +02:00
ERROR_LOG ( G3D , " Could not link program: \n %s " , infoLog . c_str ( ) ) ;
2018-06-30 10:10:42 -07:00
ERROR_LOG ( G3D , " VS desc: \n %s " , vsDesc . c_str ( ) ) ;
ERROR_LOG ( G3D , " FS desc: \n %s " , fsDesc . c_str ( ) ) ;
2018-06-30 09:16:25 -07:00
ERROR_LOG ( G3D , " VS: \n %s \n " , vsCode ) ;
ERROR_LOG ( G3D , " FS: \n %s \n " , fsCode ) ;
2018-01-20 08:58:09 -08:00
2017-11-18 16:50:49 +01:00
# ifdef _WIN32
2018-06-30 09:16:25 -07:00
OutputDebugStringUTF8 ( infoLog . c_str ( ) ) ;
if ( vsCode )
OutputDebugStringUTF8 ( LineNumberString ( vsCode ) . c_str ( ) ) ;
if ( fsCode )
OutputDebugStringUTF8 ( LineNumberString ( fsCode ) . c_str ( ) ) ;
2017-11-18 16:50:49 +01:00
# endif
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
break ;
}
glUseProgram ( program - > program ) ;
2017-11-19 00:43:58 +01:00
// Query all the uniforms.
2018-02-11 07:03:23 -08:00
for ( size_t j = 0 ; j < program - > queries_ . size ( ) ; j + + ) {
2022-08-05 10:00:27 +02:00
auto & query = program - > queries_ [ j ] ;
_dbg_assert_ ( query . name ) ;
2022-08-27 17:33:37 +02:00
2022-08-14 18:51:45 +02:00
int location = - 1 ;
2022-08-28 14:49:27 +02:00
if ( IsVRBuild ( ) & & IsMultiviewSupported ( ) ) {
2022-08-27 17:33:37 +02:00
int index = GetStereoBufferIndex ( query . name ) ;
if ( index > = 0 ) {
std : : string layout = GetStereoBufferLayout ( query . name ) ;
glUniformBlockBinding ( program - > program , glGetUniformBlockIndex ( program - > program , layout . c_str ( ) ) , index ) ;
GLuint buffer = 0 ;
glGenBuffers ( 1 , & buffer ) ;
glBindBuffer ( GL_UNIFORM_BUFFER , buffer ) ;
glBufferData ( GL_UNIFORM_BUFFER , 2 * 16 * sizeof ( float ) , NULL , GL_STATIC_DRAW ) ;
glBindBuffer ( GL_UNIFORM_BUFFER , 0 ) ;
location = buffer ;
} else {
location = glGetUniformLocation ( program - > program , query . name ) ;
}
2022-08-14 18:51:45 +02:00
} else {
location = glGetUniformLocation ( program - > program , query . name ) ;
}
2022-08-27 17:33:37 +02:00
2022-08-05 10:00:27 +02:00
if ( location < 0 & & query . required ) {
WARN_LOG ( G3D , " Required uniform query for '%s' failed " , query . name ) ;
}
* query . dest = location ;
2017-11-19 00:43:58 +01:00
}
2017-11-19 12:25:57 +01:00
// Run initializers.
2018-02-11 07:03:23 -08:00
for ( size_t j = 0 ; j < program - > initialize_ . size ( ) ; j + + ) {
auto & init = program - > initialize_ [ j ] ;
2017-11-19 12:25:57 +01:00
GLint uniform = * init . uniform ;
if ( uniform ! = - 1 ) {
switch ( init . type ) {
case 0 :
glUniform1i ( uniform , init . value ) ;
2018-12-16 17:32:31 -08:00
break ;
2017-11-19 12:25:57 +01:00
}
}
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
break ;
2018-01-10 10:18:18 +01:00
}
2017-11-18 16:50:49 +01:00
case GLRInitStepType : : CREATE_SHADER :
{
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
GLuint shader = glCreateShader ( step . create_shader . stage ) ;
step . create_shader . shader - > shader = shader ;
const char * code = step . create_shader . code ;
glShaderSource ( shader , 1 , & code , nullptr ) ;
glCompileShader ( shader ) ;
GLint success = 0 ;
glGetShaderiv ( shader , GL_COMPILE_STATUS , & success ) ;
2020-11-05 00:48:35 +01:00
std : : string infoLog = GetInfoLog ( shader , glGetShaderiv , glGetShaderInfoLog ) ;
2017-11-18 16:50:49 +01:00
if ( ! success ) {
2020-11-05 00:48:35 +01:00
std : : string errorString = StringFromFormat (
" Error in shader compilation for: %s \n "
" Info log: %s \n "
" Shader source: \n %s \n //END \n \n " ,
step . create_shader . shader - > desc . c_str ( ) ,
infoLog . c_str ( ) ,
LineNumberString ( code ) . c_str ( ) ) ;
2020-11-06 22:42:09 +01:00
std : : vector < std : : string > lines ;
SplitString ( errorString , ' \n ' , lines ) ;
for ( auto & line : lines ) {
ERROR_LOG ( G3D , " %s " , line . c_str ( ) ) ;
}
2020-11-05 00:48:35 +01:00
if ( errorCallback_ ) {
std : : string desc = StringFromFormat ( " Shader compilation failed: %s " , step . create_shader . stage = = GL_VERTEX_SHADER ? " vertex " : " fragment " ) ;
errorCallback_ ( desc . c_str ( ) , errorString . c_str ( ) , errorCallbackUserData_ ) ;
}
2018-06-30 09:16:25 -07:00
Reporting : : ReportMessage ( " Error in shader compilation: info: %s \n %s \n %s " , infoLog . c_str ( ) , step . create_shader . shader - > desc . c_str ( ) , ( const char * ) code ) ;
2018-01-10 10:48:46 +01:00
# ifdef SHADERLOG
2018-06-30 09:16:25 -07:00
OutputDebugStringUTF8 ( infoLog . c_str ( ) ) ;
2018-01-10 10:48:46 +01:00
# endif
2018-01-20 08:35:24 -08:00
step . create_shader . shader - > failed = true ;
2020-11-05 00:48:35 +01:00
step . create_shader . shader - > error = infoLog ; // Hm, we never use this.
2017-11-18 16:50:49 +01:00
}
2018-01-20 08:58:09 -08:00
// Before we throw away the code, attach it to the shader for debugging.
step . create_shader . shader - > code = code ;
2018-01-10 16:37:02 +01:00
delete [ ] step . create_shader . code ;
2017-12-12 15:32:15 +01:00
step . create_shader . shader - > valid = true ;
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
break ;
}
2017-11-18 21:12:11 +01:00
case GLRInitStepType : : CREATE_INPUT_LAYOUT :
{
2018-06-24 07:34:07 -07:00
// GLRInputLayout *layout = step.create_input_layout.inputLayout;
2017-11-19 17:37:56 +01:00
// Nothing to do unless we want to create vertexbuffer objects (GL 4.5)
break ;
}
case GLRInitStepType : : CREATE_FRAMEBUFFER :
{
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-14 14:53:27 +01:00
boundTexture = ( GLuint ) - 1 ;
2017-12-13 01:05:33 +01:00
InitCreateFramebuffer ( step ) ;
2018-01-20 09:35:43 -08:00
allocatedTextures = true ;
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 21:12:11 +01:00
break ;
}
2017-11-18 20:17:17 +01:00
case GLRInitStepType : : TEXTURE_IMAGE :
{
GLRTexture * tex = step . texture_image . texture ;
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-14 16:54:21 +01:00
if ( boundTexture ! = tex - > texture ) {
glBindTexture ( tex - > target , tex - > texture ) ;
boundTexture = tex - > texture ;
2017-12-14 14:53:27 +01:00
}
2018-06-29 23:50:07 +09:00
if ( ! step . texture_image . data & & step . texture_image . allocType ! = GLRAllocType : : NONE )
2018-01-20 00:05:59 +01:00
Crash ( ) ;
// For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage.
2019-10-24 22:40:26 +02:00
GLenum internalFormat , format , type ;
int alignment ;
Thin3DFormatToFormatAndType ( step . texture_image . format , internalFormat , format , type , alignment ) ;
2022-07-30 21:33:24 +02:00
if ( step . texture_image . depth = = 1 ) {
glTexImage2D ( tex - > target ,
step . texture_image . level , internalFormat ,
step . texture_image . width , step . texture_image . height , 0 ,
format , type , step . texture_image . data ) ;
} else {
glTexImage3D ( tex - > target ,
step . texture_image . level , internalFormat ,
step . texture_image . width , step . texture_image . height , step . texture_image . depth , 0 ,
format , type , step . texture_image . data ) ;
}
2018-01-20 09:35:43 -08:00
allocatedTextures = true ;
2018-02-11 08:12:51 -08:00
if ( step . texture_image . allocType = = GLRAllocType : : ALIGNED ) {
FreeAlignedMemory ( step . texture_image . data ) ;
2018-06-29 23:50:07 +09:00
} else if ( step . texture_image . allocType = = GLRAllocType : : NEW ) {
2018-02-11 08:12:51 -08:00
delete [ ] step . texture_image . data ;
}
2017-11-18 20:17:17 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-01-20 00:05:59 +01:00
tex - > wrapS = GL_CLAMP_TO_EDGE ;
tex - > wrapT = GL_CLAMP_TO_EDGE ;
tex - > magFilter = step . texture_image . linearFilter ? GL_LINEAR : GL_NEAREST ;
tex - > minFilter = step . texture_image . linearFilter ? GL_LINEAR : GL_NEAREST ;
glTexParameteri ( tex - > target , GL_TEXTURE_WRAP_S , tex - > wrapS ) ;
glTexParameteri ( tex - > target , GL_TEXTURE_WRAP_T , tex - > wrapT ) ;
glTexParameteri ( tex - > target , GL_TEXTURE_MAG_FILTER , tex - > magFilter ) ;
glTexParameteri ( tex - > target , GL_TEXTURE_MIN_FILTER , tex - > minFilter ) ;
2022-07-30 21:33:24 +02:00
if ( step . texture_image . depth > 1 ) {
glTexParameteri ( tex - > target , GL_TEXTURE_WRAP_R , GL_CLAMP_TO_EDGE ) ;
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-14 16:54:21 +01:00
break ;
}
case GLRInitStepType : : TEXTURE_FINALIZE :
{
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-14 16:54:21 +01:00
GLRTexture * tex = step . texture_finalize . texture ;
if ( boundTexture ! = tex - > texture ) {
glBindTexture ( tex - > target , tex - > texture ) ;
boundTexture = tex - > texture ;
}
2022-07-30 21:33:24 +02:00
if ( ( ! gl_extensions . IsGLES | | gl_extensions . GLES3 ) & & step . texture_finalize . loadedLevels > 1 ) {
2022-07-29 12:59:43 +02:00
glTexParameteri ( tex - > target , GL_TEXTURE_MAX_LEVEL , step . texture_finalize . loadedLevels - 1 ) ;
2018-04-12 14:35:25 +02:00
}
2022-07-29 12:59:43 +02:00
tex - > maxLod = ( float ) step . texture_finalize . loadedLevels - 1 ;
2017-12-14 17:08:45 +01:00
if ( step . texture_finalize . genMips ) {
glGenerateMipmap ( tex - > target ) ;
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 20:17:17 +01:00
break ;
}
2017-11-18 21:51:17 +01:00
default :
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 21:51:17 +01:00
Crash ( ) ;
2017-12-14 14:53:27 +01:00
break ;
2017-11-18 16:50:49 +01:00
}
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-01-20 09:35:43 -08:00
// TODO: Use GL_KHR_no_error or a debug callback, where supported?
2020-11-09 00:30:24 +01:00
if ( false & & allocatedTextures ) {
2018-01-20 09:35:43 -08:00
// Users may use replacements or scaling, with high render resolutions, and run out of VRAM.
// This detects that, rather than looking like PPSSPP is broken.
2020-11-06 22:42:09 +01:00
// Calling glGetError() isn't great, but at the end of init, only after creating textures, shouldn't be too bad...
2018-01-20 09:35:43 -08:00
GLenum err = glGetError ( ) ;
if ( err = = GL_OUT_OF_MEMORY ) {
WARN_LOG_REPORT ( G3D , " GL ran out of GPU memory; switching to low memory mode " ) ;
sawOutOfMemory_ = true ;
} else if ( err ! = GL_NO_ERROR ) {
// We checked the err anyway, might as well log if there is one.
2020-11-06 22:42:09 +01:00
std : : string errorString = GLEnumToString ( err ) ;
WARN_LOG ( G3D , " Got an error after init: %08x (%s) " , err , errorString . c_str ( ) ) ;
if ( errorCallback_ ) {
errorCallback_ ( " GL frame init error " , errorString . c_str ( ) , errorCallbackUserData_ ) ;
}
2018-01-20 09:35:43 -08:00
}
}
2020-05-24 19:58:59 -07:00
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
# if !defined(USING_GLES2)
if ( useDebugGroups_ )
glPopDebugGroup ( ) ;
# endif
2017-11-18 15:42:39 +01:00
}
2017-12-13 01:05:33 +01:00
void GLQueueRunner : : InitCreateFramebuffer ( const GLRInitStep & step ) {
GLRFramebuffer * fbo = step . create_framebuffer . framebuffer ;
# ifndef USING_GLES2
if ( ! gl_extensions . ARB_framebuffer_object & & gl_extensions . EXT_framebuffer_object ) {
fbo_ext_create ( step ) ;
2018-10-28 14:29:44 -07:00
} else if ( ! gl_extensions . ARB_framebuffer_object & & ! gl_extensions . IsGLES ) {
2017-12-13 01:05:33 +01:00
return ;
}
// If GLES2, we have basic FBO support and can just proceed.
# endif
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-12-16 13:21:10 -08:00
auto initFBOTexture = [ & ] ( GLRTexture & tex , GLint internalFormat , GLenum format , GLenum type , bool linear ) {
glGenTextures ( 1 , & tex . texture ) ;
tex . target = GL_TEXTURE_2D ;
tex . maxLod = 0.0f ;
// Create the surfaces.
glBindTexture ( GL_TEXTURE_2D , tex . texture ) ;
glTexImage2D ( GL_TEXTURE_2D , 0 , internalFormat , fbo - > width , fbo - > height , 0 , format , type , nullptr ) ;
tex . wrapS = GL_CLAMP_TO_EDGE ;
tex . wrapT = GL_CLAMP_TO_EDGE ;
tex . magFilter = linear ? GL_LINEAR : GL_NEAREST ;
tex . minFilter = linear ? GL_LINEAR : GL_NEAREST ;
2020-10-22 00:03:26 +02:00
2018-12-16 13:21:10 -08:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , tex . wrapS ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , tex . wrapT ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , tex . magFilter ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , tex . minFilter ) ;
if ( ! gl_extensions . IsGLES | | gl_extensions . GLES3 ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 0 ) ;
}
} ;
2017-12-13 01:05:33 +01:00
// Color texture is same everywhere
glGenFramebuffers ( 1 , & fbo - > handle ) ;
2018-12-16 13:21:10 -08:00
initFBOTexture ( fbo - > color_texture , GL_RGBA , GL_RGBA , GL_UNSIGNED_BYTE , true ) ;
2017-12-13 01:05:33 +01:00
2018-12-16 13:28:48 -08:00
retry_depth :
if ( ! fbo - > z_stencil_ ) {
2020-08-15 16:21:30 +02:00
INFO_LOG ( G3D , " Creating %d x %d FBO using no depth " , fbo - > width , fbo - > height ) ;
2018-12-16 13:28:48 -08:00
fbo - > z_stencil_buffer = 0 ;
fbo - > stencil_buffer = 0 ;
fbo - > z_buffer = 0 ;
// Bind it all together
glBindFramebuffer ( GL_FRAMEBUFFER , fbo - > handle ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , fbo - > color_texture . texture , 0 ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , 0 ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_RENDERBUFFER , 0 ) ;
} else if ( gl_extensions . IsGLES ) {
2018-12-16 13:21:10 -08:00
if ( gl_extensions . OES_packed_depth_stencil & & ( gl_extensions . OES_depth_texture | | gl_extensions . GLES3 ) ) {
2020-08-15 16:21:30 +02:00
INFO_LOG ( G3D , " Creating %d x %d FBO using DEPTH24_STENCIL8 texture " , fbo - > width , fbo - > height ) ;
2018-12-16 13:21:10 -08:00
fbo - > z_stencil_buffer = 0 ;
fbo - > stencil_buffer = 0 ;
fbo - > z_buffer = 0 ;
2017-12-13 01:05:33 +01:00
2018-12-16 13:21:10 -08:00
if ( gl_extensions . GLES3 ) {
initFBOTexture ( fbo - > z_stencil_texture , GL_DEPTH24_STENCIL8 , GL_DEPTH_STENCIL , GL_UNSIGNED_INT_24_8 , false ) ;
} else {
initFBOTexture ( fbo - > z_stencil_texture , GL_DEPTH_STENCIL , GL_DEPTH_STENCIL , GL_UNSIGNED_INT_24_8 , false ) ;
}
2017-12-13 01:05:33 +01:00
2018-12-16 13:21:10 -08:00
// Bind it all together
glBindFramebuffer ( GL_FRAMEBUFFER , fbo - > handle ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , fbo - > color_texture . texture , 0 ) ;
if ( gl_extensions . GLES3 ) {
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_DEPTH_STENCIL_ATTACHMENT , GL_TEXTURE_2D , fbo - > z_stencil_texture . texture , 0 ) ;
} else {
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D , fbo - > z_stencil_texture . texture , 0 ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_TEXTURE_2D , fbo - > z_stencil_texture . texture , 0 ) ;
}
} else if ( gl_extensions . OES_packed_depth_stencil ) {
2020-08-15 16:21:30 +02:00
INFO_LOG ( G3D , " Creating %d x %d FBO using DEPTH24_STENCIL8 " , fbo - > width , fbo - > height ) ;
2017-12-13 01:05:33 +01:00
// Standard method
fbo - > stencil_buffer = 0 ;
fbo - > z_buffer = 0 ;
// 24-bit Z, 8-bit stencil combined
glGenRenderbuffers ( 1 , & fbo - > z_stencil_buffer ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , fbo - > z_stencil_buffer ) ;
glRenderbufferStorage ( GL_RENDERBUFFER , GL_DEPTH24_STENCIL8_OES , fbo - > width , fbo - > height ) ;
// Bind it all together
glBindFramebuffer ( GL_FRAMEBUFFER , fbo - > handle ) ;
2018-01-20 13:45:32 +01:00
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , fbo - > color_texture . texture , 0 ) ;
2017-12-13 01:05:33 +01:00
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , fbo - > z_stencil_buffer ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_RENDERBUFFER , fbo - > z_stencil_buffer ) ;
} else {
2020-08-15 16:21:30 +02:00
INFO_LOG ( G3D , " Creating %d x %d FBO using separate stencil " , fbo - > width , fbo - > height ) ;
2017-12-13 01:05:33 +01:00
// TEGRA
fbo - > z_stencil_buffer = 0 ;
// 16/24-bit Z, separate 8-bit stencil
glGenRenderbuffers ( 1 , & fbo - > z_buffer ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , fbo - > z_buffer ) ;
// Don't forget to make sure fbo_standard_z_depth() matches.
glRenderbufferStorage ( GL_RENDERBUFFER , gl_extensions . OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16 , fbo - > width , fbo - > height ) ;
// 8-bit stencil buffer
glGenRenderbuffers ( 1 , & fbo - > stencil_buffer ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , fbo - > stencil_buffer ) ;
glRenderbufferStorage ( GL_RENDERBUFFER , GL_STENCIL_INDEX8 , fbo - > width , fbo - > height ) ;
// Bind it all together
glBindFramebuffer ( GL_FRAMEBUFFER , fbo - > handle ) ;
2018-01-20 13:45:32 +01:00
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , fbo - > color_texture . texture , 0 ) ;
2017-12-13 01:05:33 +01:00
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , fbo - > z_buffer ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_RENDERBUFFER , fbo - > stencil_buffer ) ;
}
2018-12-16 13:21:10 -08:00
} else if ( gl_extensions . VersionGEThan ( 3 , 0 ) ) {
2020-08-15 16:21:30 +02:00
INFO_LOG ( G3D , " Creating %d x %d FBO using DEPTH24_STENCIL8 texture " , fbo - > width , fbo - > height ) ;
2018-12-16 13:21:10 -08:00
fbo - > z_stencil_buffer = 0 ;
fbo - > stencil_buffer = 0 ;
fbo - > z_buffer = 0 ;
initFBOTexture ( fbo - > z_stencil_texture , GL_DEPTH24_STENCIL8 , GL_DEPTH_STENCIL , GL_UNSIGNED_INT_24_8 , false ) ;
// Bind it all together
glBindFramebuffer ( GL_FRAMEBUFFER , fbo - > handle ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , fbo - > color_texture . texture , 0 ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_DEPTH_STENCIL_ATTACHMENT , GL_TEXTURE_2D , fbo - > z_stencil_texture . texture , 0 ) ;
2017-12-13 01:05:33 +01:00
} else {
fbo - > stencil_buffer = 0 ;
fbo - > z_buffer = 0 ;
// 24-bit Z, 8-bit stencil
glGenRenderbuffers ( 1 , & fbo - > z_stencil_buffer ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , fbo - > z_stencil_buffer ) ;
glRenderbufferStorage ( GL_RENDERBUFFER , GL_DEPTH24_STENCIL8 , fbo - > width , fbo - > height ) ;
// Bind it all together
glBindFramebuffer ( GL_FRAMEBUFFER , fbo - > handle ) ;
2018-01-20 13:45:32 +01:00
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , fbo - > color_texture . texture , 0 ) ;
2017-12-13 01:05:33 +01:00
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , fbo - > z_stencil_buffer ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_RENDERBUFFER , fbo - > z_stencil_buffer ) ;
}
GLenum status = glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ;
2018-12-16 13:28:48 -08:00
if ( status ! = GL_FRAMEBUFFER_COMPLETE & & ! fbo - > z_buffer ) {
CHECK_GL_ERROR_IF_DEBUG ( ) ;
// Uh oh, maybe we need a z/stencil. Platforms sometimes, right?
fbo - > z_stencil_ = true ;
goto retry_depth ;
}
2017-12-13 01:05:33 +01:00
switch ( status ) {
case GL_FRAMEBUFFER_COMPLETE :
2020-08-15 16:21:30 +02:00
// INFO_LOG(G3D, "Framebuffer verified complete.");
2017-12-13 01:05:33 +01:00
break ;
case GL_FRAMEBUFFER_UNSUPPORTED :
2020-08-15 15:51:41 +02:00
ERROR_LOG ( G3D , " GL_FRAMEBUFFER_UNSUPPORTED " ) ;
2017-12-13 01:05:33 +01:00
break ;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT :
2020-08-15 15:51:41 +02:00
ERROR_LOG ( G3D , " GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT " ) ;
2017-12-13 01:05:33 +01:00
break ;
default :
2020-08-15 16:21:30 +02:00
_assert_msg_ ( false , " Other framebuffer error: %d " , status ) ;
2017-12-13 01:05:33 +01:00
break ;
}
// Unbind state we don't need
glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
CHECK_GL_ERROR_IF_DEBUG ( ) ;
currentDrawHandle_ = fbo - > handle ;
currentReadHandle_ = fbo - > handle ;
}
2022-09-04 14:01:17 +02:00
void GLQueueRunner : : RunSteps ( const std : : vector < GLRStep * > & steps , bool skipGLCalls , bool keepSteps ) {
2018-10-06 13:24:50 +02:00
if ( skipGLCalls ) {
2022-09-12 22:55:53 +02:00
if ( keepSteps ) {
return ;
}
2018-10-06 13:24:50 +02:00
// Dry run
for ( size_t i = 0 ; i < steps . size ( ) ; i + + ) {
const GLRStep & step = * steps [ i ] ;
switch ( step . stepType ) {
case GLRStepType : : RENDER :
2018-10-08 00:29:41 +09:00
for ( const auto & c : step . commands ) {
switch ( c . cmd ) {
case GLRRenderCommand : : TEXTURE_SUBIMAGE :
if ( c . texture_subimage . data ) {
if ( c . texture_subimage . allocType = = GLRAllocType : : ALIGNED ) {
FreeAlignedMemory ( c . texture_subimage . data ) ;
} else if ( c . texture_subimage . allocType = = GLRAllocType : : NEW ) {
delete [ ] c . texture_subimage . data ;
}
}
break ;
2020-03-10 21:00:30 -07:00
default :
break ;
2018-10-08 00:29:41 +09:00
}
}
2018-10-06 13:24:50 +02:00
break ;
2020-03-10 21:00:30 -07:00
default :
break ;
2018-10-06 13:24:50 +02:00
}
delete steps [ i ] ;
}
return ;
}
2020-05-24 19:39:14 -07:00
size_t totalRenderCount = 0 ;
for ( auto & step : steps ) {
if ( step - > stepType = = GLRStepType : : RENDER ) {
// Skip empty render steps.
if ( step - > commands . empty ( ) ) {
step - > stepType = GLRStepType : : RENDER_SKIP ;
continue ;
}
totalRenderCount + + ;
}
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2020-05-24 19:39:14 -07:00
size_t renderCount = 0 ;
2018-02-11 07:03:23 -08:00
for ( size_t i = 0 ; i < steps . size ( ) ; i + + ) {
2017-11-18 15:42:39 +01:00
const GLRStep & step = * steps [ i ] ;
2020-05-24 19:58:59 -07:00
# if !defined(USING_GLES2)
if ( useDebugGroups_ )
glPushDebugGroup ( GL_DEBUG_SOURCE_APPLICATION , ( GLuint ) i + 10000 , - 1 , step . tag ) ;
# endif
2017-11-18 15:42:39 +01:00
switch ( step . stepType ) {
case GLRStepType : : RENDER :
2020-05-24 19:39:14 -07:00
renderCount + + ;
PerformRenderPass ( step , renderCount = = 1 , renderCount = = totalRenderCount ) ;
2017-11-18 15:42:39 +01:00
break ;
case GLRStepType : : COPY :
PerformCopy ( step ) ;
break ;
case GLRStepType : : BLIT :
PerformBlit ( step ) ;
break ;
case GLRStepType : : READBACK :
PerformReadback ( step ) ;
break ;
case GLRStepType : : READBACK_IMAGE :
PerformReadbackImage ( step ) ;
break ;
2020-05-24 19:39:14 -07:00
case GLRStepType : : RENDER_SKIP :
break ;
2017-11-18 21:51:17 +01:00
default :
Crash ( ) ;
break ;
2017-11-18 15:42:39 +01:00
}
2020-05-24 19:58:59 -07:00
# if !defined(USING_GLES2)
if ( useDebugGroups_ )
glPopDebugGroup ( ) ;
# endif
2022-09-04 14:01:17 +02:00
if ( ! keepSteps ) {
delete steps [ i ] ;
}
2017-11-18 15:42:39 +01:00
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
}
void GLQueueRunner : : LogSteps ( const std : : vector < GLRStep * > & steps ) {
}
void GLQueueRunner : : PerformBlit ( const GLRStep & step ) {
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-13 01:05:33 +01:00
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
// in ES 2.0 that actually separate them anyway of course, so doesn't matter.
2017-12-14 16:08:55 +01:00
fbo_bind_fb_target ( false , step . blit . dst - > handle ) ;
fbo_bind_fb_target ( true , step . blit . src - > handle ) ;
int srcX1 = step . blit . srcRect . x ;
int srcY1 = step . blit . srcRect . y ;
int srcX2 = step . blit . srcRect . x + step . blit . srcRect . w ;
int srcY2 = step . blit . srcRect . y + step . blit . srcRect . h ;
int dstX1 = step . blit . dstRect . x ;
int dstY1 = step . blit . dstRect . y ;
int dstX2 = step . blit . dstRect . x + step . blit . dstRect . w ;
int dstY2 = step . blit . dstRect . y + step . blit . dstRect . h ;
2017-12-13 01:05:33 +01:00
if ( gl_extensions . GLES3 | | gl_extensions . ARB_framebuffer_object ) {
2017-12-14 16:08:55 +01:00
glBlitFramebuffer ( srcX1 , srcY1 , srcX2 , srcY2 , dstX1 , dstY1 , dstX2 , dstY2 , step . blit . aspectMask , step . blit . filter ? GL_LINEAR : GL_NEAREST ) ;
CHECK_GL_ERROR_IF_DEBUG ( ) ;
# if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC.
2017-12-13 01:05:33 +01:00
} else if ( gl_extensions . NV_framebuffer_blit ) {
2017-12-14 16:08:55 +01:00
glBlitFramebufferNV ( srcX1 , srcY1 , srcX2 , srcY2 , dstX1 , dstY1 , dstX2 , dstY2 , step . blit . aspectMask , step . blit . filter ? GL_LINEAR : GL_NEAREST ) ;
CHECK_GL_ERROR_IF_DEBUG ( ) ;
# endif // defined(USING_GLES2) && defined(__ANDROID__)
2017-12-13 01:05:33 +01:00
} else {
2017-12-14 16:08:55 +01:00
ERROR_LOG ( G3D , " GLQueueRunner: Tried to blit without the capability " ) ;
}
2017-11-18 15:42:39 +01:00
}
2020-05-24 19:39:14 -07:00
void GLQueueRunner : : PerformRenderPass ( const GLRStep & step , bool first , bool last ) {
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
PerformBindFramebufferAsRenderTarget ( step ) ;
2020-05-24 19:39:14 -07:00
if ( first ) {
glDisable ( GL_DEPTH_TEST ) ;
glDisable ( GL_STENCIL_TEST ) ;
glDisable ( GL_BLEND ) ;
glDisable ( GL_CULL_FACE ) ;
glDisable ( GL_DITHER ) ;
glEnable ( GL_SCISSOR_TEST ) ;
2018-01-19 20:24:09 +01:00
# ifndef USING_GLES2
2020-05-24 19:39:14 -07:00
if ( ! gl_extensions . IsGLES ) {
glDisable ( GL_COLOR_LOGIC_OP ) ;
}
2018-01-19 20:24:09 +01:00
# endif
2020-05-24 19:39:14 -07:00
}
2017-11-19 17:37:56 +01:00
2020-05-24 19:39:14 -07:00
if ( first & & gl_extensions . ARB_vertex_array_object ) {
2018-01-21 09:17:24 -08:00
glBindVertexArray ( globalVAO_ ) ;
}
2017-11-18 22:15:12 +01:00
2017-11-18 20:17:17 +01:00
GLRProgram * curProgram = nullptr ;
2022-08-24 10:22:58 +02:00
int activeSlot = - 1 ;
2017-11-18 15:42:39 +01:00
2017-12-19 12:49:48 +01:00
// State filtering tracking.
2017-11-19 00:43:58 +01:00
int attrMask = 0 ;
2017-12-14 17:50:40 +01:00
int colorMask = - 1 ;
int depthMask = - 1 ;
int depthFunc = - 1 ;
2020-05-24 19:39:14 -07:00
GLuint curArrayBuffer = ( GLuint ) 0 ;
GLuint curElemArrayBuffer = ( GLuint ) 0 ;
2017-12-19 12:49:48 +01:00
bool depthEnabled = false ;
2018-02-28 13:28:39 +01:00
bool stencilEnabled = false ;
2017-12-19 12:49:48 +01:00
bool blendEnabled = false ;
bool cullEnabled = false ;
bool ditherEnabled = false ;
2020-11-10 23:30:24 +01:00
bool depthClampEnabled = false ;
2018-06-24 07:34:07 -07:00
# ifndef USING_GLES2
int logicOp = - 1 ;
2018-01-19 20:24:09 +01:00
bool logicEnabled = false ;
2018-06-24 07:34:07 -07:00
# endif
2021-09-11 17:53:50 -07:00
bool clipDistance0Enabled = false ;
2017-12-19 12:49:48 +01:00
GLuint blendEqColor = ( GLuint ) - 1 ;
GLuint blendEqAlpha = ( GLuint ) - 1 ;
2017-12-13 01:05:33 +01:00
2021-06-12 14:10:57 -07:00
GLRTexture * curTex [ MAX_GL_TEXTURE_SLOTS ] { } ;
2017-12-27 14:33:18 +01:00
2018-10-28 14:29:44 -07:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
auto & commands = step . commands ;
for ( const auto & c : commands ) {
switch ( c . cmd ) {
case GLRRenderCommand : : DEPTH :
if ( c . depth . enabled ) {
2017-12-19 12:49:48 +01:00
if ( ! depthEnabled ) {
glEnable ( GL_DEPTH_TEST ) ;
depthEnabled = true ;
}
2017-12-14 17:50:40 +01:00
if ( c . depth . write ! = depthMask ) {
glDepthMask ( c . depth . write ) ;
depthMask = c . depth . write ;
}
if ( c . depth . func ! = depthFunc ) {
glDepthFunc ( c . depth . func ) ;
depthFunc = c . depth . func ;
}
2017-12-19 12:49:48 +01:00
} else if ( ! c . depth . enabled & & depthEnabled ) {
2017-11-18 15:42:39 +01:00
glDisable ( GL_DEPTH_TEST ) ;
2017-12-19 12:49:48 +01:00
depthEnabled = false ;
}
break ;
case GLRRenderCommand : : STENCILFUNC :
if ( c . stencilFunc . enabled ) {
2018-02-28 13:28:39 +01:00
if ( ! stencilEnabled ) {
glEnable ( GL_STENCIL_TEST ) ;
stencilEnabled = true ;
}
2017-12-19 12:49:48 +01:00
glStencilFunc ( c . stencilFunc . func , c . stencilFunc . ref , c . stencilFunc . compareMask ) ;
2018-02-28 13:28:39 +01:00
} else if ( stencilEnabled ) {
2017-12-19 12:49:48 +01:00
glDisable ( GL_STENCIL_TEST ) ;
2018-02-28 13:28:39 +01:00
stencilEnabled = false ;
2017-11-18 15:42:39 +01:00
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
2017-12-19 12:49:48 +01:00
case GLRRenderCommand : : STENCILOP :
glStencilOp ( c . stencilOp . sFail , c . stencilOp . zFail , c . stencilOp . pass ) ;
glStencilMask ( c . stencilOp . writeMask ) ;
break ;
2017-11-18 15:42:39 +01:00
case GLRRenderCommand : : BLEND :
if ( c . blend . enabled ) {
2017-12-19 12:49:48 +01:00
if ( ! blendEnabled ) {
glEnable ( GL_BLEND ) ;
blendEnabled = true ;
}
if ( blendEqColor ! = c . blend . funcColor | | blendEqAlpha ! = c . blend . funcAlpha ) {
glBlendEquationSeparate ( c . blend . funcColor , c . blend . funcAlpha ) ;
blendEqColor = c . blend . funcColor ;
blendEqAlpha = c . blend . funcAlpha ;
}
2017-11-18 15:42:39 +01:00
glBlendFuncSeparate ( c . blend . srcColor , c . blend . dstColor , c . blend . srcAlpha , c . blend . dstAlpha ) ;
2017-12-19 12:49:48 +01:00
} else if ( ! c . blend . enabled & & blendEnabled ) {
2017-11-18 15:42:39 +01:00
glDisable ( GL_BLEND ) ;
2017-12-19 12:49:48 +01:00
blendEnabled = false ;
2017-11-18 15:42:39 +01:00
}
2017-12-14 17:50:40 +01:00
if ( c . blend . mask ! = colorMask ) {
glColorMask ( c . blend . mask & 1 , ( c . blend . mask > > 1 ) & 1 , ( c . blend . mask > > 2 ) & 1 , ( c . blend . mask > > 3 ) & 1 ) ;
colorMask = c . blend . mask ;
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
2018-01-19 20:24:09 +01:00
case GLRRenderCommand : : LOGICOP :
# ifndef USING_GLES2
if ( c . logic . enabled ) {
if ( ! logicEnabled ) {
glEnable ( GL_COLOR_LOGIC_OP ) ;
logicEnabled = true ;
}
if ( logicOp ! = c . logic . logicOp ) {
glLogicOp ( c . logic . logicOp ) ;
}
} else if ( ! c . logic . enabled & & logicEnabled ) {
glDisable ( GL_COLOR_LOGIC_OP ) ;
logicEnabled = false ;
}
# endif
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-01-19 20:24:09 +01:00
break ;
2017-11-18 15:42:39 +01:00
case GLRRenderCommand : : CLEAR :
2018-02-08 16:47:04 +01:00
// Scissor test is on, and should be on after leaving this case. If we disable it,
// we re-enable it at the end.
if ( c . clear . scissorW = = 0 ) {
2018-01-31 17:07:20 +01:00
glDisable ( GL_SCISSOR_TEST ) ;
2018-02-08 16:47:04 +01:00
} else {
glScissor ( c . clear . scissorX , c . clear . scissorY , c . clear . scissorW , c . clear . scissorH ) ;
2018-01-31 17:07:20 +01:00
}
2018-01-18 22:17:29 -08:00
if ( c . clear . colorMask ! = colorMask ) {
glColorMask ( c . clear . colorMask & 1 , ( c . clear . colorMask > > 1 ) & 1 , ( c . clear . colorMask > > 2 ) & 1 , ( c . clear . colorMask > > 3 ) & 1 ) ;
}
2017-11-18 21:12:11 +01:00
if ( c . clear . clearMask & GL_COLOR_BUFFER_BIT ) {
2017-11-18 15:42:39 +01:00
float color [ 4 ] ;
Uint8x4ToFloat4 ( color , c . clear . clearColor ) ;
glClearColor ( color [ 0 ] , color [ 1 ] , color [ 2 ] , color [ 3 ] ) ;
}
2017-11-18 21:12:11 +01:00
if ( c . clear . clearMask & GL_DEPTH_BUFFER_BIT ) {
# if defined(USING_GLES2)
glClearDepthf ( c . clear . clearZ ) ;
# else
2018-10-28 14:29:44 -07:00
if ( gl_extensions . IsGLES ) {
glClearDepthf ( c . clear . clearZ ) ;
} else {
glClearDepth ( c . clear . clearZ ) ;
}
2017-11-18 21:12:11 +01:00
# endif
2017-11-18 15:42:39 +01:00
}
2017-11-18 22:15:12 +01:00
if ( c . clear . clearMask & GL_STENCIL_BUFFER_BIT ) {
2017-11-18 15:42:39 +01:00
glClearStencil ( c . clear . clearStencil ) ;
}
2017-11-18 21:12:11 +01:00
glClear ( c . clear . clearMask ) ;
2020-05-24 19:18:04 +02:00
// Restore the color mask if it was different.
if ( c . clear . colorMask ! = colorMask ) {
glColorMask ( colorMask & 1 , ( colorMask > > 1 ) & 1 , ( colorMask > > 2 ) & 1 , ( colorMask > > 3 ) & 1 ) ;
}
2018-02-08 16:47:04 +01:00
if ( c . clear . scissorW = = 0 ) {
2018-02-08 00:50:29 +01:00
glEnable ( GL_SCISSOR_TEST ) ;
2018-01-31 17:07:20 +01:00
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
case GLRRenderCommand : : BLENDCOLOR :
glBlendColor ( c . blendColor . color [ 0 ] , c . blendColor . color [ 1 ] , c . blendColor . color [ 2 ] , c . blendColor . color [ 3 ] ) ;
break ;
case GLRRenderCommand : : VIEWPORT :
2017-12-12 15:04:46 +01:00
{
float y = c . viewport . vp . y ;
2017-12-19 12:59:57 +01:00
if ( ! curFB_ )
2017-12-12 15:04:46 +01:00
y = curFBHeight_ - y - c . viewport . vp . h ;
2017-11-18 15:42:39 +01:00
// TODO: Support FP viewports through glViewportArrays
2017-12-12 15:04:46 +01:00
glViewport ( ( GLint ) c . viewport . vp . x , ( GLint ) y , ( GLsizei ) c . viewport . vp . w , ( GLsizei ) c . viewport . vp . h ) ;
2017-12-14 17:50:40 +01:00
# if !defined(USING_GLES2)
2018-10-28 14:29:44 -07:00
if ( gl_extensions . IsGLES ) {
glDepthRangef ( c . viewport . vp . minZ , c . viewport . vp . maxZ ) ;
} else {
glDepthRange ( c . viewport . vp . minZ , c . viewport . vp . maxZ ) ;
}
2017-12-14 17:50:40 +01:00
# else
glDepthRangef ( c . viewport . vp . minZ , c . viewport . vp . maxZ ) ;
# endif
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
2017-12-12 15:04:46 +01:00
}
2017-11-18 15:42:39 +01:00
case GLRRenderCommand : : SCISSOR :
2017-12-12 15:04:46 +01:00
{
int y = c . scissor . rc . y ;
2017-12-19 12:59:57 +01:00
if ( ! curFB_ )
2017-12-12 15:04:46 +01:00
y = curFBHeight_ - y - c . scissor . rc . h ;
glScissor ( c . scissor . rc . x , y , c . scissor . rc . w , c . scissor . rc . h ) ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
2017-12-12 15:04:46 +01:00
}
2017-11-18 15:42:39 +01:00
case GLRRenderCommand : : UNIFORM4F :
2017-11-18 20:17:17 +01:00
{
2017-11-19 00:43:58 +01:00
int loc = c . uniform4 . loc ? * c . uniform4 . loc : - 1 ;
2017-11-18 20:17:17 +01:00
if ( c . uniform4 . name ) {
loc = curProgram - > GetUniformLoc ( c . uniform4 . name ) ;
}
if ( loc > = 0 ) {
switch ( c . uniform4 . count ) {
case 1 :
glUniform1f ( loc , c . uniform4 . v [ 0 ] ) ;
break ;
case 2 :
glUniform2fv ( loc , 1 , c . uniform4 . v ) ;
break ;
case 3 :
glUniform3fv ( loc , 1 , c . uniform4 . v ) ;
break ;
case 4 :
glUniform4fv ( loc , 1 , c . uniform4 . v ) ;
break ;
}
2017-11-18 15:42:39 +01:00
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
2017-11-18 20:17:17 +01:00
}
2020-11-08 23:17:06 +01:00
case GLRRenderCommand : : UNIFORM4UI :
{
_dbg_assert_ ( curProgram ) ;
int loc = c . uniform4 . loc ? * c . uniform4 . loc : - 1 ;
if ( c . uniform4 . name ) {
loc = curProgram - > GetUniformLoc ( c . uniform4 . name ) ;
}
if ( loc > = 0 ) {
switch ( c . uniform4 . count ) {
2022-08-05 10:00:27 +02:00
case 1 : glUniform1uiv ( loc , 1 , ( GLuint * ) c . uniform4 . v ) ; break ;
case 2 : glUniform2uiv ( loc , 1 , ( GLuint * ) c . uniform4 . v ) ; break ;
case 3 : glUniform3uiv ( loc , 1 , ( GLuint * ) c . uniform4 . v ) ; break ;
case 4 : glUniform4uiv ( loc , 1 , ( GLuint * ) c . uniform4 . v ) ; break ;
2020-11-08 23:17:06 +01:00
}
}
CHECK_GL_ERROR_IF_DEBUG ( ) ;
break ;
}
2017-11-19 00:43:58 +01:00
case GLRRenderCommand : : UNIFORM4I :
{
2020-08-16 00:38:55 +02:00
_dbg_assert_ ( curProgram ) ;
2017-11-19 00:43:58 +01:00
int loc = c . uniform4 . loc ? * c . uniform4 . loc : - 1 ;
if ( c . uniform4 . name ) {
loc = curProgram - > GetUniformLoc ( c . uniform4 . name ) ;
}
if ( loc > = 0 ) {
switch ( c . uniform4 . count ) {
2022-08-05 10:00:27 +02:00
case 1 : glUniform1iv ( loc , 1 , ( GLint * ) c . uniform4 . v ) ; break ;
case 2 : glUniform2iv ( loc , 1 , ( GLint * ) c . uniform4 . v ) ; break ;
case 3 : glUniform3iv ( loc , 1 , ( GLint * ) c . uniform4 . v ) ; break ;
case 4 : glUniform4iv ( loc , 1 , ( GLint * ) c . uniform4 . v ) ; break ;
2017-11-19 00:43:58 +01:00
}
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-19 00:43:58 +01:00
break ;
}
2022-08-14 18:51:45 +02:00
case GLRRenderCommand : : UNIFORMSTEREOMATRIX :
{
_dbg_assert_ ( curProgram ) ;
2022-09-05 20:49:25 +02:00
if ( IsMultiviewSupported ( ) ) {
int layout = GetStereoBufferIndex ( c . uniformMatrix4 . name ) ;
if ( layout > = 0 ) {
int size = 2 * 16 * sizeof ( float ) ;
glBindBufferBase ( GL_UNIFORM_BUFFER , layout , * c . uniformMatrix4 . loc ) ;
glBindBuffer ( GL_UNIFORM_BUFFER , * c . uniformMatrix4 . loc ) ;
void * matrices = glMapBufferRange ( GL_UNIFORM_BUFFER , 0 , size , GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT ) ;
memcpy ( matrices , c . uniformMatrix4 . m , size ) ;
glUnmapBuffer ( GL_UNIFORM_BUFFER ) ;
glBindBuffer ( GL_UNIFORM_BUFFER , 0 ) ;
}
} else {
int loc = c . uniformMatrix4 . loc ? * c . uniformMatrix4 . loc : - 1 ;
if ( c . uniformMatrix4 . name ) {
loc = curProgram - > GetUniformLoc ( c . uniformMatrix4 . name ) ;
}
if ( loc > = 0 ) {
if ( GetVRFBOIndex ( ) = = 0 ) {
glUniformMatrix4fv ( loc , 1 , false , c . uniformMatrix4 . m ) ;
} else {
glUniformMatrix4fv ( loc , 1 , false , & c . uniformMatrix4 . m [ 16 ] ) ;
}
}
2022-08-14 18:51:45 +02:00
}
CHECK_GL_ERROR_IF_DEBUG ( ) ;
break ;
}
2017-11-18 15:42:39 +01:00
case GLRRenderCommand : : UNIFORMMATRIX :
2017-11-18 20:17:17 +01:00
{
2020-08-16 00:38:55 +02:00
_dbg_assert_ ( curProgram ) ;
2020-05-24 19:39:14 -07:00
int loc = c . uniformMatrix4 . loc ? * c . uniformMatrix4 . loc : - 1 ;
if ( c . uniformMatrix4 . name ) {
loc = curProgram - > GetUniformLoc ( c . uniformMatrix4 . name ) ;
2017-11-18 20:17:17 +01:00
}
if ( loc > = 0 ) {
glUniformMatrix4fv ( loc , 1 , false , c . uniformMatrix4 . m ) ;
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
2017-11-18 20:17:17 +01:00
}
2017-11-18 15:42:39 +01:00
case GLRRenderCommand : : BINDTEXTURE :
{
2017-12-12 15:04:46 +01:00
GLint slot = c . texture . slot ;
2017-12-27 14:33:18 +01:00
if ( slot ! = activeSlot ) {
2017-12-12 15:04:46 +01:00
glActiveTexture ( GL_TEXTURE0 + slot ) ;
2017-12-27 14:33:18 +01:00
activeSlot = slot ;
2017-12-12 15:04:46 +01:00
}
if ( c . texture . texture ) {
2017-12-27 14:33:18 +01:00
if ( curTex [ slot ] ! = c . texture . texture ) {
glBindTexture ( c . texture . texture - > target , c . texture . texture - > texture ) ;
curTex [ slot ] = c . texture . texture ;
}
2017-12-12 15:04:46 +01:00
} else {
2017-12-14 14:53:27 +01:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ; // Which target? Well we only use this one anyway...
2017-12-27 14:33:18 +01:00
curTex [ slot ] = nullptr ;
2017-12-14 14:53:27 +01:00
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-14 14:53:27 +01:00
break ;
}
case GLRRenderCommand : : BIND_FB_TEXTURE :
{
GLint slot = c . bind_fb_texture . slot ;
2017-12-27 14:33:18 +01:00
if ( slot ! = activeSlot ) {
2017-12-14 14:53:27 +01:00
glActiveTexture ( GL_TEXTURE0 + slot ) ;
2017-12-27 14:33:18 +01:00
activeSlot = slot ;
2017-12-14 14:53:27 +01:00
}
if ( c . bind_fb_texture . aspect = = GL_COLOR_BUFFER_BIT ) {
2022-08-24 11:01:57 +02:00
if ( curTex [ slot ] ! = & c . bind_fb_texture . framebuffer - > color_texture ) {
2018-12-16 17:32:31 -08:00
glBindTexture ( GL_TEXTURE_2D , c . bind_fb_texture . framebuffer - > color_texture . texture ) ;
2022-08-24 11:01:57 +02:00
curTex [ slot ] = & c . bind_fb_texture . framebuffer - > color_texture ;
}
2018-12-16 17:32:31 -08:00
} else if ( c . bind_fb_texture . aspect = = GL_DEPTH_BUFFER_BIT ) {
2022-08-24 11:01:57 +02:00
if ( curTex [ slot ] ! = & c . bind_fb_texture . framebuffer - > z_stencil_texture ) {
2018-12-16 17:32:31 -08:00
glBindTexture ( GL_TEXTURE_2D , c . bind_fb_texture . framebuffer - > z_stencil_texture . texture ) ;
2022-08-24 11:01:57 +02:00
curTex [ slot ] = & c . bind_fb_texture . framebuffer - > z_stencil_texture ;
}
2017-12-14 14:53:27 +01:00
} else {
2022-08-24 11:01:57 +02:00
// Can't texture from stencil buffers.
2018-01-20 13:45:32 +01:00
curTex [ slot ] = nullptr ;
2017-11-18 15:42:39 +01:00
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
break ;
}
case GLRRenderCommand : : BINDPROGRAM :
{
2017-12-14 17:50:40 +01:00
if ( curProgram ! = c . program . program ) {
glUseProgram ( c . program . program - > program ) ;
2021-09-11 17:53:50 -07:00
if ( c . program . program - > use_clip_distance0 ! = clipDistance0Enabled ) {
if ( c . program . program - > use_clip_distance0 )
glEnable ( GL_CLIP_DISTANCE0 ) ;
else
glDisable ( GL_CLIP_DISTANCE0 ) ;
clipDistance0Enabled = c . program . program - > use_clip_distance0 ;
}
2017-12-14 17:50:40 +01:00
curProgram = c . program . program ;
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
}
2017-12-19 12:25:13 +01:00
case GLRRenderCommand : : BIND_VERTEX_BUFFER :
2017-11-18 21:12:11 +01:00
{
2017-12-19 12:25:13 +01:00
// TODO: Add fast path for glBindVertexBuffer
GLRInputLayout * layout = c . bindVertexBuffer . inputLayout ;
2018-10-06 13:24:50 +02:00
GLuint buf = c . bindVertexBuffer . buffer ? c . bindVertexBuffer . buffer - > buffer_ : 0 ;
2021-10-25 06:27:45 -07:00
_dbg_assert_ ( ! c . bindVertexBuffer . buffer | | ! c . bindVertexBuffer . buffer - > Mapped ( ) ) ;
2017-12-19 12:25:13 +01:00
if ( buf ! = curArrayBuffer ) {
glBindBuffer ( GL_ARRAY_BUFFER , buf ) ;
curArrayBuffer = buf ;
}
int enable = layout - > semanticsMask_ & ~ attrMask ;
int disable = ( ~ layout - > semanticsMask_ ) & attrMask ;
2017-11-18 21:12:11 +01:00
for ( int i = 0 ; i < 7 ; i + + ) { // SEM_MAX
2017-11-19 00:43:58 +01:00
if ( enable & ( 1 < < i ) ) {
2017-11-18 21:12:11 +01:00
glEnableVertexAttribArray ( i ) ;
}
2017-11-19 00:43:58 +01:00
if ( disable & ( 1 < < i ) ) {
glDisableVertexAttribArray ( i ) ;
}
2017-11-18 21:12:11 +01:00
}
2017-11-19 17:37:56 +01:00
attrMask = layout - > semanticsMask_ ;
2018-01-10 10:18:18 +01:00
for ( size_t i = 0 ; i < layout - > entries . size ( ) ; i + + ) {
2017-11-18 21:51:17 +01:00
auto & entry = layout - > entries [ i ] ;
2017-12-19 12:25:13 +01:00
glVertexAttribPointer ( entry . location , entry . count , entry . type , entry . normalized , entry . stride , ( const void * ) ( c . bindVertexBuffer . offset + entry . offset ) ) ;
2017-11-18 21:12:11 +01:00
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 21:12:11 +01:00
break ;
}
2017-12-14 14:53:27 +01:00
case GLRRenderCommand : : BIND_BUFFER :
2017-11-19 00:23:52 +01:00
{
2017-12-14 14:53:27 +01:00
if ( c . bind_buffer . target = = GL_ARRAY_BUFFER ) {
2017-12-19 12:25:13 +01:00
Crash ( ) ;
2017-12-14 14:53:27 +01:00
} else if ( c . bind_buffer . target = = GL_ELEMENT_ARRAY_BUFFER ) {
2018-10-06 13:24:50 +02:00
GLuint buf = c . bind_buffer . buffer ? c . bind_buffer . buffer - > buffer_ : 0 ;
2020-08-16 00:38:55 +02:00
_dbg_assert_ ( ! c . bind_buffer . buffer - > Mapped ( ) ) ;
2017-12-14 14:53:27 +01:00
if ( buf ! = curElemArrayBuffer ) {
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , buf ) ;
curElemArrayBuffer = buf ;
}
} else {
2018-10-06 13:24:50 +02:00
GLuint buf = c . bind_buffer . buffer ? c . bind_buffer . buffer - > buffer_ : 0 ;
2020-08-16 00:38:55 +02:00
_dbg_assert_ ( ! c . bind_buffer . buffer - > Mapped ( ) ) ;
2017-12-14 14:53:27 +01:00
glBindBuffer ( c . bind_buffer . target , buf ) ;
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-19 12:25:57 +01:00
break ;
}
2017-11-18 20:17:17 +01:00
case GLRRenderCommand : : GENMIPS :
2017-12-14 14:53:27 +01:00
// TODO: Should we include the texture handle in the command?
2017-12-19 12:25:13 +01:00
// Also, should this not be an init command?
2017-11-18 20:17:17 +01:00
glGenerateMipmap ( GL_TEXTURE_2D ) ;
break ;
2017-11-18 15:42:39 +01:00
case GLRRenderCommand : : DRAW :
glDrawArrays ( c . draw . mode , c . draw . first , c . draw . count ) ;
break ;
case GLRRenderCommand : : DRAW_INDEXED :
if ( c . drawIndexed . instances = = 1 ) {
glDrawElements ( c . drawIndexed . mode , c . drawIndexed . count , c . drawIndexed . indexType , c . drawIndexed . indices ) ;
2017-12-19 12:25:13 +01:00
} else {
glDrawElementsInstanced ( c . drawIndexed . mode , c . drawIndexed . count , c . drawIndexed . indexType , c . drawIndexed . indices , c . drawIndexed . instances ) ;
2017-11-18 15:42:39 +01:00
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
break ;
2017-11-18 20:17:17 +01:00
case GLRRenderCommand : : TEXTURESAMPLER :
2017-12-27 14:33:18 +01:00
{
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-02-07 19:49:31 +01:00
GLint slot = c . textureSampler . slot ;
if ( slot ! = activeSlot ) {
glActiveTexture ( GL_TEXTURE0 + slot ) ;
activeSlot = slot ;
}
GLRTexture * tex = curTex [ slot ] ;
2017-12-27 14:33:18 +01:00
if ( ! tex ) {
break ;
}
2020-09-17 20:48:24 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-03-17 01:53:19 +01:00
if ( tex - > canWrap ) {
if ( tex - > wrapS ! = c . textureSampler . wrapS ) {
2022-07-30 21:33:24 +02:00
glTexParameteri ( tex - > target , GL_TEXTURE_WRAP_S , c . textureSampler . wrapS ) ;
2018-03-17 01:53:19 +01:00
tex - > wrapS = c . textureSampler . wrapS ;
}
if ( tex - > wrapT ! = c . textureSampler . wrapT ) {
2022-07-30 21:33:24 +02:00
glTexParameteri ( tex - > target , GL_TEXTURE_WRAP_T , c . textureSampler . wrapT ) ;
2018-03-17 01:53:19 +01:00
tex - > wrapT = c . textureSampler . wrapT ;
}
2017-12-27 14:33:18 +01:00
}
2020-09-17 20:48:24 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-27 14:33:18 +01:00
if ( tex - > magFilter ! = c . textureSampler . magFilter ) {
2022-07-30 21:33:24 +02:00
glTexParameteri ( tex - > target , GL_TEXTURE_MAG_FILTER , c . textureSampler . magFilter ) ;
2017-12-27 14:33:18 +01:00
tex - > magFilter = c . textureSampler . magFilter ;
}
2020-09-17 20:48:24 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-27 14:33:18 +01:00
if ( tex - > minFilter ! = c . textureSampler . minFilter ) {
2022-07-30 21:33:24 +02:00
glTexParameteri ( tex - > target , GL_TEXTURE_MIN_FILTER , c . textureSampler . minFilter ) ;
2017-12-27 14:33:18 +01:00
tex - > minFilter = c . textureSampler . minFilter ;
}
2020-09-17 20:48:24 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-27 14:33:18 +01:00
if ( tex - > anisotropy ! = c . textureSampler . anisotropy ) {
2018-01-16 15:19:35 +01:00
if ( c . textureSampler . anisotropy ! = 0.0f ) {
2022-07-30 21:33:24 +02:00
glTexParameterf ( tex - > target , GL_TEXTURE_MAX_ANISOTROPY_EXT , c . textureSampler . anisotropy ) ;
2018-01-16 15:19:35 +01:00
}
2017-12-27 14:33:18 +01:00
tex - > anisotropy = c . textureSampler . anisotropy ;
2017-11-19 17:37:56 +01:00
}
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 20:17:17 +01:00
break ;
2017-12-27 14:33:18 +01:00
}
2017-12-13 01:05:33 +01:00
case GLRRenderCommand : : TEXTURELOD :
2017-12-27 14:33:18 +01:00
{
2018-02-07 19:49:31 +01:00
GLint slot = c . textureSampler . slot ;
if ( slot ! = activeSlot ) {
glActiveTexture ( GL_TEXTURE0 + slot ) ;
activeSlot = slot ;
}
GLRTexture * tex = curTex [ slot ] ;
2017-12-27 14:33:18 +01:00
if ( ! tex ) {
break ;
}
2017-12-13 01:05:33 +01:00
# ifndef USING_GLES2
2018-10-28 14:29:44 -07:00
if ( tex - > lodBias ! = c . textureLod . lodBias & & ! gl_extensions . IsGLES ) {
2022-07-30 21:33:24 +02:00
glTexParameterf ( tex - > target , GL_TEXTURE_LOD_BIAS , c . textureLod . lodBias ) ;
2017-12-27 14:33:18 +01:00
tex - > lodBias = c . textureLod . lodBias ;
}
2017-12-13 01:05:33 +01:00
# endif
2017-12-27 14:33:18 +01:00
if ( tex - > minLod ! = c . textureLod . minLod ) {
2022-07-30 21:33:24 +02:00
glTexParameterf ( tex - > target , GL_TEXTURE_MIN_LOD , c . textureLod . minLod ) ;
2017-12-27 14:33:18 +01:00
tex - > minLod = c . textureLod . minLod ;
}
if ( tex - > maxLod ! = c . textureLod . maxLod ) {
2022-07-30 21:33:24 +02:00
glTexParameterf ( tex - > target , GL_TEXTURE_MAX_LOD , c . textureLod . maxLod ) ;
2017-12-27 14:33:18 +01:00
tex - > maxLod = c . textureLod . maxLod ;
}
2017-12-13 01:05:33 +01:00
break ;
2017-12-27 14:33:18 +01:00
}
2018-07-21 00:18:34 +09:00
case GLRRenderCommand : : TEXTURE_SUBIMAGE :
{
2022-08-24 10:22:58 +02:00
GLint slot = c . texture_subimage . slot ;
if ( slot ! = activeSlot ) {
glActiveTexture ( GL_TEXTURE0 + slot ) ;
activeSlot = slot ;
}
2018-07-21 00:18:34 +09:00
// TODO: Need bind?
2022-08-24 10:22:58 +02:00
GLRTexture * tex = c . texture_subimage . texture ;
2018-07-21 00:18:34 +09:00
if ( ! c . texture_subimage . data )
Crash ( ) ;
2022-07-30 21:33:24 +02:00
_assert_ ( tex - > target = = GL_TEXTURE_2D ) ;
2018-07-21 00:18:34 +09:00
// For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage.
2019-10-24 22:40:26 +02:00
GLuint internalFormat , format , type ;
int alignment ;
Thin3DFormatToFormatAndType ( c . texture_subimage . format , internalFormat , format , type , alignment ) ;
glTexSubImage2D ( tex - > target , c . texture_subimage . level , c . texture_subimage . x , c . texture_subimage . y , c . texture_subimage . width , c . texture_subimage . height , format , type , c . texture_subimage . data ) ;
2018-07-21 00:18:34 +09:00
if ( c . texture_subimage . allocType = = GLRAllocType : : ALIGNED ) {
FreeAlignedMemory ( c . texture_subimage . data ) ;
} else if ( c . texture_subimage . allocType = = GLRAllocType : : NEW ) {
delete [ ] c . texture_subimage . data ;
}
CHECK_GL_ERROR_IF_DEBUG ( ) ;
break ;
}
2017-11-18 21:51:17 +01:00
case GLRRenderCommand : : RASTER :
if ( c . raster . cullEnable ) {
2017-12-19 12:49:48 +01:00
if ( ! cullEnabled ) {
glEnable ( GL_CULL_FACE ) ;
cullEnabled = true ;
}
2017-11-18 21:51:17 +01:00
glFrontFace ( c . raster . frontFace ) ;
glCullFace ( c . raster . cullFace ) ;
2017-12-19 12:49:48 +01:00
} else if ( ! c . raster . cullEnable & & cullEnabled ) {
2017-11-18 21:51:17 +01:00
glDisable ( GL_CULL_FACE ) ;
2017-12-19 12:49:48 +01:00
cullEnabled = false ;
2017-11-18 21:51:17 +01:00
}
2017-11-19 00:43:58 +01:00
if ( c . raster . ditherEnable ) {
2017-12-19 12:49:48 +01:00
if ( ! ditherEnabled ) {
glEnable ( GL_DITHER ) ;
ditherEnabled = true ;
}
} else if ( ! c . raster . ditherEnable & & ditherEnabled ) {
2017-11-19 00:43:58 +01:00
glDisable ( GL_DITHER ) ;
2017-12-19 12:49:48 +01:00
ditherEnabled = false ;
2017-11-19 00:43:58 +01:00
}
2021-02-01 00:20:28 +01:00
# ifndef USING_GLES2
2020-11-10 23:30:24 +01:00
if ( c . raster . depthClampEnable ) {
if ( ! depthClampEnabled ) {
glEnable ( GL_DEPTH_CLAMP ) ;
depthClampEnabled = true ;
}
} else if ( ! c . raster . depthClampEnable & & depthClampEnabled ) {
glDisable ( GL_DEPTH_CLAMP ) ;
depthClampEnabled = false ;
}
2021-02-01 00:20:28 +01:00
# endif
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 21:51:17 +01:00
break ;
default :
Crash ( ) ;
break ;
2017-11-18 15:42:39 +01:00
}
}
2017-11-18 21:51:17 +01:00
2017-11-19 00:43:58 +01:00
for ( int i = 0 ; i < 7 ; i + + ) {
if ( attrMask & ( 1 < < i ) ) {
glDisableVertexAttribArray ( i ) ;
}
}
2018-02-07 19:49:31 +01:00
if ( activeSlot ! = 0 ) {
2017-11-18 15:42:39 +01:00
glActiveTexture ( GL_TEXTURE0 ) ;
2018-02-07 19:49:31 +01:00
activeSlot = 0 ; // doesn't matter, just nice.
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-14 14:53:27 +01:00
// Wipe out the current state.
2020-05-24 19:39:14 -07:00
if ( curArrayBuffer ! = 0 )
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
if ( curElemArrayBuffer ! = 0 )
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
if ( last & & gl_extensions . ARB_vertex_array_object ) {
2018-01-21 09:17:24 -08:00
glBindVertexArray ( 0 ) ;
}
2020-05-24 19:39:14 -07:00
if ( last )
glDisable ( GL_SCISSOR_TEST ) ;
if ( depthEnabled )
glDisable ( GL_DEPTH_TEST ) ;
if ( stencilEnabled )
glDisable ( GL_STENCIL_TEST ) ;
if ( blendEnabled )
glDisable ( GL_BLEND ) ;
if ( cullEnabled )
glDisable ( GL_CULL_FACE ) ;
2021-02-01 00:20:28 +01:00
# ifndef USING_GLES2
2020-11-10 23:30:24 +01:00
if ( depthClampEnabled )
glDisable ( GL_DEPTH_CLAMP ) ;
2020-05-24 19:39:14 -07:00
if ( ! gl_extensions . IsGLES & & logicEnabled ) {
2018-10-28 14:29:44 -07:00
glDisable ( GL_COLOR_LOGIC_OP ) ;
}
2018-01-19 20:24:09 +01:00
# endif
2021-09-11 17:53:50 -07:00
if ( clipDistance0Enabled )
glDisable ( GL_CLIP_DISTANCE0 ) ;
2020-05-24 19:39:14 -07:00
if ( ( colorMask & 15 ) ! = 15 )
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
}
void GLQueueRunner : : PerformCopy ( const GLRStep & step ) {
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
GLuint srcTex = 0 ;
GLuint dstTex = 0 ;
GLuint target = GL_TEXTURE_2D ;
const GLRect2D & srcRect = step . copy . srcRect ;
const GLOffset2D & dstPos = step . copy . dstPos ;
GLRFramebuffer * src = step . copy . src ;
2018-01-19 21:19:52 -08:00
GLRFramebuffer * dst = step . copy . dst ;
2017-11-18 15:42:39 +01:00
int srcLevel = 0 ;
int dstLevel = 0 ;
int srcZ = 0 ;
int dstZ = 0 ;
int depth = 1 ;
switch ( step . copy . aspectMask ) {
2017-11-18 21:12:11 +01:00
case GL_COLOR_BUFFER_BIT :
2018-01-20 13:45:32 +01:00
srcTex = src - > color_texture . texture ;
dstTex = dst - > color_texture . texture ;
2017-11-18 15:42:39 +01:00
break ;
2017-11-18 21:12:11 +01:00
case GL_DEPTH_BUFFER_BIT :
2018-01-10 10:18:18 +01:00
// TODO: Support depth copies.
2020-07-19 17:47:02 +02:00
_assert_msg_ ( false , " Depth copies not yet supported - soon " ) ;
2017-11-18 15:42:39 +01:00
target = GL_RENDERBUFFER ;
2017-12-13 01:05:33 +01:00
/*
2017-11-18 15:42:39 +01:00
srcTex = src - > depth . texture ;
dstTex = src - > depth . texture ;
2017-12-13 01:05:33 +01:00
*/
2017-11-18 15:42:39 +01:00
break ;
}
2017-12-19 14:35:24 +01:00
2020-07-19 17:47:02 +02:00
_dbg_assert_ ( srcTex ) ;
_dbg_assert_ ( dstTex ) ;
2017-12-19 14:35:24 +01:00
2022-08-07 11:09:34 +02:00
_assert_msg_ ( caps_ . framebufferCopySupported , " Image copy extension expected " ) ;
2017-11-18 15:42:39 +01:00
# if defined(USING_GLES2)
2022-08-07 12:04:41 +02:00
# if !PPSSPP_PLATFORM(IOS)
2017-11-18 15:42:39 +01:00
glCopyImageSubDataOES (
srcTex , target , srcLevel , srcRect . x , srcRect . y , srcZ ,
dstTex , target , dstLevel , dstPos . x , dstPos . y , dstZ ,
srcRect . w , srcRect . h , depth ) ;
2022-08-07 12:04:41 +02:00
# endif
2017-11-18 15:42:39 +01:00
# else
if ( gl_extensions . ARB_copy_image ) {
glCopyImageSubData (
srcTex , target , srcLevel , srcRect . x , srcRect . y , srcZ ,
dstTex , target , dstLevel , dstPos . x , dstPos . y , dstZ ,
srcRect . w , srcRect . h , depth ) ;
} else if ( gl_extensions . NV_copy_image ) {
// Older, pre GL 4.x NVIDIA cards.
glCopyImageSubDataNV (
srcTex , target , srcLevel , srcRect . x , srcRect . y , srcZ ,
dstTex , target , dstLevel , dstPos . x , dstPos . y , dstZ ,
srcRect . w , srcRect . h , depth ) ;
}
# endif
2018-04-12 14:35:04 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
}
2017-11-18 16:50:49 +01:00
void GLQueueRunner : : PerformReadback ( const GLRStep & pass ) {
2018-01-28 21:28:16 +01:00
using namespace Draw ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-01-28 21:28:16 +01:00
2017-12-19 14:35:24 +01:00
GLRFramebuffer * fb = pass . readback . src ;
fbo_bind_fb_target ( true , fb ? fb - > handle : 0 ) ;
2018-06-01 22:24:51 +02:00
// Reads from the "bound for read" framebuffer. Note that if there's no fb, it's not valid to call this.
if ( fb & & ( gl_extensions . GLES3 | | ! gl_extensions . IsGLES ) )
2017-12-19 14:35:24 +01:00
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-06-16 16:04:25 -07:00
// Always read back in 8888 format for the color aspect.
GLuint internalFormat = GL_RGBA ;
GLuint format = GL_RGBA ;
GLuint type = GL_UNSIGNED_BYTE ;
int srcAlignment = 4 ;
2018-02-05 16:21:39 +01:00
int dstAlignment = ( int ) DataFormatSizeInBytes ( pass . readback . dstFormat ) ;
2018-01-28 21:28:16 +01:00
2018-06-16 16:04:25 -07:00
# ifndef USING_GLES2
if ( pass . readback . aspectMask & GL_DEPTH_BUFFER_BIT ) {
internalFormat = GL_DEPTH_COMPONENT ;
format = GL_DEPTH_COMPONENT ;
type = GL_FLOAT ;
srcAlignment = 4 ;
} else if ( pass . readback . aspectMask & GL_STENCIL_BUFFER_BIT ) {
internalFormat = GL_STENCIL_INDEX ;
format = GL_STENCIL_INDEX ;
type = GL_UNSIGNED_BYTE ;
srcAlignment = 1 ;
}
# endif
2017-12-19 14:35:24 +01:00
int pixelStride = pass . readback . srcRect . w ;
// Apply the correct alignment.
2018-01-28 21:28:16 +01:00
glPixelStorei ( GL_PACK_ALIGNMENT , srcAlignment ) ;
2017-12-19 14:35:24 +01:00
if ( ! gl_extensions . IsGLES | | gl_extensions . GLES3 ) {
// Some drivers seem to require we specify this. See #8254.
glPixelStorei ( GL_PACK_ROW_LENGTH , pixelStride ) ;
}
2017-11-18 16:50:49 +01:00
2017-12-19 14:35:24 +01:00
GLRect2D rect = pass . readback . srcRect ;
2018-06-16 16:04:25 -07:00
bool convert = internalFormat = = GL_RGBA & & pass . readback . dstFormat ! = DataFormat : : R8G8B8A8_UNORM ;
2018-01-28 21:28:16 +01:00
int tempSize = srcAlignment * rect . w * rect . h ;
int readbackSize = dstAlignment * rect . w * rect . h ;
if ( convert & & tempSize > tempBufferSize_ ) {
delete [ ] tempBuffer_ ;
tempBuffer_ = new uint8_t [ tempSize ] ;
tempBufferSize_ = tempSize ;
}
if ( readbackSize > readbackBufferSize_ ) {
2017-12-19 14:35:24 +01:00
delete [ ] readbackBuffer_ ;
2018-01-28 21:28:16 +01:00
readbackBuffer_ = new uint8_t [ readbackSize ] ;
readbackBufferSize_ = readbackSize ;
2017-12-19 14:35:24 +01:00
}
2018-01-28 21:28:16 +01:00
glReadPixels ( rect . x , rect . y , rect . w , rect . h , format , type , convert ? tempBuffer_ : readbackBuffer_ ) ;
2017-12-19 14:35:24 +01:00
# ifdef DEBUG_READ_PIXELS
LogReadPixelsError ( glGetError ( ) ) ;
# endif
if ( ! gl_extensions . IsGLES | | gl_extensions . GLES3 ) {
glPixelStorei ( GL_PACK_ROW_LENGTH , 0 ) ;
}
2018-11-21 22:15:01 +01:00
if ( convert & & tempBuffer_ & & readbackBuffer_ ) {
2018-01-28 21:28:16 +01:00
ConvertFromRGBA8888 ( readbackBuffer_ , tempBuffer_ , pixelStride , pixelStride , rect . w , rect . h , pass . readback . dstFormat ) ;
}
2017-12-19 14:35:24 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
}
void GLQueueRunner : : PerformReadbackImage ( const GLRStep & pass ) {
2019-06-23 15:30:30 -07:00
# ifndef USING_GLES2
2019-08-17 10:58:19 -07:00
GLRTexture * tex = pass . readback_image . texture ;
2019-06-23 15:30:30 -07:00
GLRect2D rect = pass . readback_image . srcRect ;
2018-01-19 22:41:18 -08:00
2019-06-23 15:30:30 -07:00
if ( gl_extensions . VersionGEThan ( 4 , 5 ) ) {
int size = 4 * rect . w * rect . h ;
if ( size > readbackBufferSize_ ) {
delete [ ] readbackBuffer_ ;
readbackBuffer_ = new uint8_t [ size ] ;
readbackBufferSize_ = size ;
}
2018-01-19 22:41:18 -08:00
2019-06-23 15:30:30 -07:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
glGetTextureSubImage ( tex - > texture , pass . readback_image . mipLevel , rect . x , rect . y , 0 , rect . w , rect . h , 1 , GL_RGBA , GL_UNSIGNED_BYTE , readbackBufferSize_ , readbackBuffer_ ) ;
} else {
glBindTexture ( GL_TEXTURE_2D , tex - > texture ) ;
2018-01-19 22:41:18 -08:00
2019-06-23 15:30:30 -07:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-01-19 22:41:18 -08:00
2019-06-23 15:30:30 -07:00
GLint w , h ;
// This is only used for debugging (currently), and GL doesn't support a subrectangle.
glGetTexLevelParameteriv ( GL_TEXTURE_2D , pass . readback_image . mipLevel , GL_TEXTURE_WIDTH , & w ) ;
glGetTexLevelParameteriv ( GL_TEXTURE_2D , pass . readback_image . mipLevel , GL_TEXTURE_HEIGHT , & h ) ;
int size = 4 * std : : max ( ( int ) w , rect . x + rect . w ) * std : : max ( ( int ) h , rect . h ) ;
if ( size > readbackBufferSize_ ) {
delete [ ] readbackBuffer_ ;
readbackBuffer_ = new uint8_t [ size ] ;
readbackBufferSize_ = size ;
}
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
glPixelStorei ( GL_PACK_ROW_LENGTH , rect . x + rect . w ) ;
glGetTexImage ( GL_TEXTURE_2D , pass . readback_image . mipLevel , GL_RGBA , GL_UNSIGNED_BYTE , readbackBuffer_ ) ;
glPixelStorei ( GL_PACK_ROW_LENGTH , 0 ) ;
2018-01-19 22:41:18 -08:00
2019-06-23 15:30:30 -07:00
if ( rect . x ! = 0 | | rect . y ! = 0 ) {
int dstStride = 4 * rect . w ;
int srcStride = 4 * ( rect . x + rect . w ) ;
int xoff = 4 * rect . x ;
int yoff = rect . y * srcStride ;
for ( int y = 0 ; y < rect . h ; + + y ) {
memmove ( readbackBuffer_ + h * dstStride , readbackBuffer_ + yoff + h * srcStride + xoff , dstStride ) ;
}
}
}
2018-01-20 13:55:51 +01:00
# endif
2018-01-19 22:41:18 -08:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 16:50:49 +01:00
}
2017-11-18 15:42:39 +01:00
void GLQueueRunner : : PerformBindFramebufferAsRenderTarget ( const GLRStep & pass ) {
2017-12-12 15:04:46 +01:00
if ( pass . render . framebuffer ) {
curFBWidth_ = pass . render . framebuffer - > width ;
curFBHeight_ = pass . render . framebuffer - > height ;
} else {
curFBWidth_ = targetWidth_ ;
curFBHeight_ = targetHeight_ ;
}
2017-12-13 01:05:33 +01:00
2017-12-14 14:53:27 +01:00
curFB_ = pass . render . framebuffer ;
if ( curFB_ ) {
2017-12-13 01:05:33 +01:00
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
// in ES 2.0 that actually separate them anyway of course, so doesn't matter.
2017-12-14 14:53:27 +01:00
fbo_bind_fb_target ( false , curFB_ - > handle ) ;
2017-12-13 01:05:33 +01:00
} else {
fbo_unbind ( ) ;
2017-12-14 14:53:27 +01:00
// Backbuffer is now bound.
2017-12-13 01:05:33 +01:00
}
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-18 15:42:39 +01:00
}
void GLQueueRunner : : CopyReadbackBuffer ( int width , int height , Draw : : DataFormat srcFormat , Draw : : DataFormat destFormat , int pixelStride , uint8_t * pixels ) {
2017-12-19 14:35:24 +01:00
// TODO: Maybe move data format conversion here, and always read back 8888. Drivers
2017-12-15 12:40:38 +01:00
// don't usually provide very optimized conversion implementations, though some do.
2018-01-18 21:45:26 -08:00
// Just need to be careful about dithering, which may break Danganronpa.
2017-12-15 12:40:38 +01:00
int bpp = ( int ) Draw : : DataFormatSizeInBytes ( destFormat ) ;
2018-11-24 17:14:15 +01:00
if ( ! readbackBuffer_ | | bpp < = 0 | | ! pixels ) {
// Something went wrong during the read and no readback buffer was allocated, probably.
return ;
}
2017-12-19 14:35:24 +01:00
for ( int y = 0 ; y < height ; y + + ) {
2018-01-18 21:45:26 -08:00
memcpy ( pixels + y * pixelStride * bpp , readbackBuffer_ + y * width * bpp , width * bpp ) ;
2017-12-19 14:35:24 +01:00
}
2017-11-18 15:42:39 +01:00
}
2017-11-19 17:37:56 +01:00
GLuint GLQueueRunner : : AllocTextureName ( ) {
if ( nameCache_ . empty ( ) ) {
nameCache_ . resize ( TEXCACHE_NAME_CACHE_SIZE ) ;
glGenTextures ( TEXCACHE_NAME_CACHE_SIZE , & nameCache_ [ 0 ] ) ;
}
u32 name = nameCache_ . back ( ) ;
nameCache_ . pop_back ( ) ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-11-19 17:37:56 +01:00
return name ;
}
2017-12-13 01:05:33 +01:00
// On PC, we always use GL_DEPTH24_STENCIL8.
// On Android, we try to use what's available.
# ifndef USING_GLES2
void GLQueueRunner : : fbo_ext_create ( const GLRInitStep & step ) {
GLRFramebuffer * fbo = step . create_framebuffer . framebuffer ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-13 01:05:33 +01:00
// Color texture is same everywhere
glGenFramebuffersEXT ( 1 , & fbo - > handle ) ;
2018-01-20 13:45:32 +01:00
glGenTextures ( 1 , & fbo - > color_texture . texture ) ;
2017-12-13 01:05:33 +01:00
// Create the surfaces.
2018-01-20 13:45:32 +01:00
glBindTexture ( GL_TEXTURE_2D , fbo - > color_texture . texture ) ;
2017-12-13 01:05:33 +01:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , fbo - > width , fbo - > height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , NULL ) ;
2018-01-20 13:45:32 +01:00
fbo - > color_texture . target = GL_TEXTURE_2D ;
fbo - > color_texture . wrapS = GL_CLAMP_TO_EDGE ;
fbo - > color_texture . wrapT = GL_CLAMP_TO_EDGE ;
2018-01-20 09:05:52 -08:00
fbo - > color_texture . magFilter = GL_LINEAR ;
fbo - > color_texture . minFilter = GL_LINEAR ;
2018-01-20 13:45:32 +01:00
fbo - > color_texture . maxLod = 0.0f ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , fbo - > color_texture . wrapS ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , fbo - > color_texture . wrapT ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , fbo - > color_texture . magFilter ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , fbo - > color_texture . minFilter ) ;
2017-12-13 01:05:33 +01:00
fbo - > stencil_buffer = 0 ;
fbo - > z_buffer = 0 ;
// 24-bit Z, 8-bit stencil
glGenRenderbuffersEXT ( 1 , & fbo - > z_stencil_buffer ) ;
glBindRenderbufferEXT ( GL_RENDERBUFFER_EXT , fbo - > z_stencil_buffer ) ;
glRenderbufferStorageEXT ( GL_RENDERBUFFER_EXT , GL_DEPTH_STENCIL_EXT , fbo - > width , fbo - > height ) ;
2017-12-19 12:49:48 +01:00
// glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height);
2017-12-13 01:05:33 +01:00
// Bind it all together
glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT , fbo - > handle ) ;
2018-01-20 13:45:32 +01:00
glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT0_EXT , GL_TEXTURE_2D , fbo - > color_texture . texture , 0 ) ;
2017-12-13 01:05:33 +01:00
glFramebufferRenderbufferEXT ( GL_FRAMEBUFFER_EXT , GL_DEPTH_ATTACHMENT_EXT , GL_RENDERBUFFER_EXT , fbo - > z_stencil_buffer ) ;
glFramebufferRenderbufferEXT ( GL_FRAMEBUFFER_EXT , GL_STENCIL_ATTACHMENT_EXT , GL_RENDERBUFFER_EXT , fbo - > z_stencil_buffer ) ;
GLenum status = glCheckFramebufferStatusEXT ( GL_FRAMEBUFFER_EXT ) ;
switch ( status ) {
case GL_FRAMEBUFFER_COMPLETE_EXT :
2020-08-15 16:21:30 +02:00
// INFO_LOG(G3D, "Framebuffer verified complete.");
2017-12-13 01:05:33 +01:00
break ;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT :
2020-08-15 15:51:41 +02:00
ERROR_LOG ( G3D , " GL_FRAMEBUFFER_UNSUPPORTED " ) ;
2017-12-13 01:05:33 +01:00
break ;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT :
2020-08-15 15:51:41 +02:00
ERROR_LOG ( G3D , " GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT " ) ;
2017-12-13 01:05:33 +01:00
break ;
default :
2020-08-15 16:21:30 +02:00
_assert_msg_ ( false , " Other framebuffer error: %d " , status ) ;
2017-12-13 01:05:33 +01:00
break ;
}
// Unbind state we don't need
glBindRenderbufferEXT ( GL_RENDERBUFFER_EXT , 0 ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-13 01:05:33 +01:00
currentDrawHandle_ = fbo - > handle ;
currentReadHandle_ = fbo - > handle ;
}
# endif
GLenum GLQueueRunner : : fbo_get_fb_target ( bool read , GLuint * * cached ) {
bool supportsBlit = gl_extensions . ARB_framebuffer_object ;
if ( gl_extensions . IsGLES ) {
supportsBlit = ( gl_extensions . GLES3 | | gl_extensions . NV_framebuffer_blit ) ;
}
// Note: GL_FRAMEBUFFER_EXT and GL_FRAMEBUFFER have the same value, same with _NV.
if ( supportsBlit ) {
if ( read ) {
* cached = & currentReadHandle_ ;
return GL_READ_FRAMEBUFFER ;
} else {
* cached = & currentDrawHandle_ ;
return GL_DRAW_FRAMEBUFFER ;
}
} else {
* cached = & currentDrawHandle_ ;
return GL_FRAMEBUFFER ;
}
}
void GLQueueRunner : : fbo_bind_fb_target ( bool read , GLuint name ) {
2017-12-14 14:53:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-13 01:05:33 +01:00
GLuint * cached ;
GLenum target = fbo_get_fb_target ( read , & cached ) ;
if ( * cached ! = name ) {
if ( gl_extensions . ARB_framebuffer_object | | gl_extensions . IsGLES ) {
glBindFramebuffer ( target , name ) ;
} else {
# ifndef USING_GLES2
glBindFramebufferEXT ( target , name ) ;
# endif
}
* cached = name ;
}
2017-12-14 14:53:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-13 01:05:33 +01:00
}
void GLQueueRunner : : fbo_unbind ( ) {
2017-12-14 14:53:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-13 01:05:33 +01:00
# ifndef USING_GLES2
if ( gl_extensions . ARB_framebuffer_object | | gl_extensions . IsGLES ) {
glBindFramebuffer ( GL_FRAMEBUFFER , g_defaultFBO ) ;
} else if ( gl_extensions . EXT_framebuffer_object ) {
glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT , g_defaultFBO ) ;
}
# else
glBindFramebuffer ( GL_FRAMEBUFFER , g_defaultFBO ) ;
# endif
2021-03-02 20:57:25 -08:00
# if PPSSPP_PLATFORM(IOS)
2017-12-13 01:05:33 +01:00
bindDefaultFBO ( ) ;
# endif
2022-08-27 17:33:37 +02:00
if ( IsVRBuild ( ) ) {
BindVRFramebuffer ( ) ;
}
2022-07-15 15:41:21 +02:00
2017-12-13 01:05:33 +01:00
currentDrawHandle_ = 0 ;
currentReadHandle_ = 0 ;
2017-12-14 14:53:27 +01:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-12-13 01:05:33 +01:00
}
GLRFramebuffer : : ~ GLRFramebuffer ( ) {
2018-10-06 13:24:50 +02:00
if ( handle = = 0 & & z_stencil_buffer = = 0 & & z_buffer = = 0 & & stencil_buffer = = 0 )
return ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-12-16 13:21:10 -08:00
if ( handle ) {
if ( gl_extensions . ARB_framebuffer_object | | gl_extensions . IsGLES ) {
2017-12-13 01:05:33 +01:00
glBindFramebuffer ( GL_FRAMEBUFFER , handle ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , 0 , 0 ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , 0 ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , g_defaultFBO ) ;
glDeleteFramebuffers ( 1 , & handle ) ;
# ifndef USING_GLES2
2018-12-16 13:21:10 -08:00
} else if ( gl_extensions . EXT_framebuffer_object ) {
2017-12-13 01:05:33 +01:00
glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT , handle ) ;
glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , 0 , 0 ) ;
glFramebufferRenderbufferEXT ( GL_FRAMEBUFFER_EXT , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER_EXT , 0 ) ;
glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT , g_defaultFBO ) ;
glDeleteFramebuffersEXT ( 1 , & handle ) ;
# endif
2018-12-16 13:21:10 -08:00
}
2017-12-13 01:05:33 +01:00
}
2018-12-16 13:21:10 -08:00
// These can only be set when supported.
if ( z_stencil_buffer )
glDeleteRenderbuffers ( 1 , & z_stencil_buffer ) ;
if ( z_buffer )
glDeleteRenderbuffers ( 1 , & z_buffer ) ;
if ( stencil_buffer )
glDeleteRenderbuffers ( 1 , & stencil_buffer ) ;
2018-05-30 18:14:13 +02:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2018-01-10 10:18:18 +01:00
}