2019-03-02 11:53:14 +01:00
/* RetroArch - A frontend for libretro.
* Copyright ( C ) 2019 - Hans - Kristian Arntzen
*
* RetroArch is free software : you can redistribute it and / or modify it under the terms
* of the GNU General Public License as published by the Free Software Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
* RetroArch 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 for more details .
*
* You should have received a copy of the GNU General Public License along with RetroArch .
* If not , see < http : //www.gnu.org/licenses/>.
*/
2019-03-18 16:02:28 +01:00
/* Modern OpenGL driver.
*
2021-09-26 14:16:09 +02:00
* Minimum version ( desktop ) : OpenGL 3.2 + ( 2009 )
* Minimum version ( mobile ) : OpenGLES 3.0 + ( 2012 )
2019-03-18 16:02:28 +01:00
*/
2019-03-02 11:53:14 +01:00
# ifdef HAVE_CONFIG_H
2021-08-18 14:19:48 -04:00
# include "../../config.h"
2019-03-02 11:53:14 +01:00
# endif
2021-09-26 14:16:09 +02:00
# include "../common/gl3_common.h"
2019-03-02 11:53:14 +01:00
# include <gfx/gl_capabilities.h>
# include <gfx/video_frame.h>
# include <glsym/glsym.h>
# include <string/stdstring.h>
2019-03-13 17:00:52 +01:00
# include <retro_math.h>
2019-03-02 11:53:14 +01:00
# include "../../configuration.h"
# include "../../dynamic.h"
2020-07-01 21:04:05 +02:00
# ifdef HAVE_REWIND
2020-10-15 05:29:20 +02:00
# include "../../state_manager.h"
2020-07-01 21:04:05 +02:00
# endif
2019-03-02 11:53:14 +01:00
# include "../../retroarch.h"
# include "../../verbosity.h"
# ifdef HAVE_THREADS
# include "../video_thread_wrapper.h"
# endif
# include "../font_driver.h"
2021-11-10 02:34:04 +01:00
# include "../../record/record_driver.h"
2019-03-02 11:53:14 +01:00
# ifdef HAVE_MENU
# include "../../menu/menu_driver.h"
# endif
2020-02-17 21:28:42 +01:00
# ifdef HAVE_GFX_WIDGETS
# include "../gfx_widgets.h"
2019-03-02 11:53:14 +01:00
# endif
2021-12-24 13:34:30 +00:00
2021-11-23 08:45:54 +01:00
static const struct video_ortho gl3_default_ortho = { 0 , 1 , 0 , 1 , - 1 , 1 } ;
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
void gl3_framebuffer_copy (
2020-01-22 13:12:00 +01:00
GLuint fb_id ,
GLuint quad_program ,
GLuint quad_vbo ,
GLint flat_ubo_vertex ,
struct Size2D size ,
GLuint image )
{
glBindFramebuffer ( GL_FRAMEBUFFER , fb_id ) ;
glActiveTexture ( GL_TEXTURE2 ) ;
glBindTexture ( GL_TEXTURE_2D , image ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_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 ) ;
glViewport ( 0 , 0 , size . width , size . height ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
glUseProgram ( quad_program ) ;
if ( flat_ubo_vertex > = 0 )
{
2022-04-02 23:48:34 +02:00
static float mvp [ 16 ] = {
2.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 2.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 2.0f , 0.0f ,
- 1.0f , - 1.0f , 0.0f , 1.0f
} ;
2020-01-22 13:12:00 +01:00
glUniform4fv ( flat_ubo_vertex , 4 , mvp ) ;
}
/* Draw quad */
glDisable ( GL_CULL_FACE ) ;
glDisable ( GL_BLEND ) ;
glDisable ( GL_DEPTH_TEST ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , quad_vbo ) ;
glVertexAttribPointer ( 0 , 2 , GL_FLOAT , GL_FALSE , 4 * sizeof ( float ) ,
( void * ) ( ( uintptr_t ) ( 0 ) ) ) ;
glVertexAttribPointer ( 1 , 2 , GL_FLOAT , GL_FALSE , 4 * sizeof ( float ) ,
( void * ) ( ( uintptr_t ) ( 2 * sizeof ( float ) ) ) ) ;
glDrawArrays ( GL_TRIANGLE_STRIP , 0 , 4 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glDisableVertexAttribArray ( 0 ) ;
glDisableVertexAttribArray ( 1 ) ;
glUseProgram ( 0 ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
}
2021-11-23 08:45:54 +01:00
void gl3_framebuffer_copy_partial (
2020-01-22 13:12:00 +01:00
GLuint fb_id ,
GLuint quad_program ,
GLint flat_ubo_vertex ,
struct Size2D size ,
GLuint image ,
float rx , float ry )
{
GLuint vbo ;
const float quad_data [ ] = {
0.0f , 0.0f , 0.0f , 0.0f ,
1.0f , 0.0f , rx , 0.0f ,
0.0f , 1.0f , 0.0f , ry ,
1.0f , 1.0f , rx , ry ,
} ;
glBindFramebuffer ( GL_FRAMEBUFFER , fb_id ) ;
glActiveTexture ( GL_TEXTURE2 ) ;
glBindTexture ( GL_TEXTURE_2D , image ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_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 ) ;
glViewport ( 0 , 0 , size . width , size . height ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
glUseProgram ( quad_program ) ;
if ( flat_ubo_vertex > = 0 )
{
2022-04-02 23:48:34 +02:00
static float mvp [ 16 ] = {
2.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 2.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 2.0f , 0.0f ,
- 1.0f , - 1.0f , 0.0f , 1.0f
} ;
2020-01-22 13:12:00 +01:00
glUniform4fv ( flat_ubo_vertex , 4 , mvp ) ;
}
glDisable ( GL_CULL_FACE ) ;
glDisable ( GL_BLEND ) ;
glDisable ( GL_DEPTH_TEST ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
/* A bit crude, but heeeey. */
glGenBuffers ( 1 , & vbo ) ;
glBindBuffer ( GL_ARRAY_BUFFER , vbo ) ;
glBufferData ( GL_ARRAY_BUFFER , sizeof ( quad_data ) , quad_data , GL_STREAM_DRAW ) ;
glVertexAttribPointer ( 0 , 2 , GL_FLOAT , GL_FALSE , 4 * sizeof ( float ) ,
( void * ) ( ( uintptr_t ) ( 0 ) ) ) ;
glVertexAttribPointer ( 1 , 2 , GL_FLOAT , GL_FALSE , 4 * sizeof ( float ) ,
( void * ) ( ( uintptr_t ) ( 2 * sizeof ( float ) ) ) ) ;
glDrawArrays ( GL_TRIANGLE_STRIP , 0 , 4 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glDeleteBuffers ( 1 , & vbo ) ;
glDisableVertexAttribArray ( 0 ) ;
glDisableVertexAttribArray ( 1 ) ;
glUseProgram ( 0 ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
}
2021-11-23 08:45:54 +01:00
static void gl3_deinit_fences ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
unsigned i ;
for ( i = 0 ; i < gl - > fence_count ; i + + )
{
if ( gl - > fences [ i ] )
glDeleteSync ( gl - > fences [ i ] ) ;
}
gl - > fence_count = 0 ;
memset ( gl - > fences , 0 , sizeof ( gl - > fences ) ) ;
}
2021-11-23 08:45:54 +01:00
static bool gl3_init_pbo_readback ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
2019-03-13 11:21:00 +01:00
unsigned i ;
struct scaler_ctx * scaler = NULL ;
2019-03-02 11:53:14 +01:00
glGenBuffers ( GL_CORE_NUM_PBOS , gl - > pbo_readback ) ;
for ( i = 0 ; i < GL_CORE_NUM_PBOS ; i + + )
{
glBindBuffer ( GL_PIXEL_PACK_BUFFER , gl - > pbo_readback [ i ] ) ;
glBufferData ( GL_PIXEL_PACK_BUFFER , gl - > vp . width * gl - > vp . height * sizeof ( uint32_t ) , NULL , GL_STREAM_READ ) ;
}
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
2019-03-13 11:21:00 +01:00
scaler = & gl - > pbo_readback_scaler ;
2019-03-02 11:53:14 +01:00
scaler - > in_width = gl - > vp . width ;
scaler - > in_height = gl - > vp . height ;
scaler - > out_width = gl - > vp . width ;
scaler - > out_height = gl - > vp . height ;
scaler - > in_stride = gl - > vp . width * sizeof ( uint32_t ) ;
scaler - > out_stride = gl - > vp . width * 3 ;
scaler - > in_fmt = SCALER_FMT_ABGR8888 ;
scaler - > out_fmt = SCALER_FMT_BGR24 ;
scaler - > scaler_type = SCALER_TYPE_POINT ;
if ( ! scaler_ctx_gen_filter ( scaler ) )
{
gl - > pbo_readback_enable = false ;
RARCH_ERR ( " [GLCore]: Failed to initialize pixel conversion for PBO. \n " ) ;
glDeleteBuffers ( 4 , gl - > pbo_readback ) ;
memset ( gl - > pbo_readback , 0 , sizeof ( gl - > pbo_readback ) ) ;
return false ;
}
return true ;
}
2021-11-23 08:45:54 +01:00
static void gl3_deinit_pbo_readback ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
2019-03-13 11:21:00 +01:00
unsigned i ;
2019-03-02 11:53:14 +01:00
for ( i = 0 ; i < GL_CORE_NUM_PBOS ; i + + )
if ( gl - > pbo_readback [ i ] ! = 0 )
glDeleteBuffers ( 1 , & gl - > pbo_readback [ i ] ) ;
memset ( gl - > pbo_readback , 0 , sizeof ( gl - > pbo_readback ) ) ;
scaler_ctx_gen_reset ( & gl - > pbo_readback_scaler ) ;
}
2021-11-23 08:45:54 +01:00
static void gl3_slow_readback ( gl3_t * gl , void * buffer )
2019-03-02 11:53:14 +01:00
{
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
glPixelStorei ( GL_PACK_ROW_LENGTH , 0 ) ;
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
# ifndef HAVE_OPENGLES
glReadBuffer ( GL_BACK ) ;
# endif
glReadPixels ( gl - > vp . x , gl - > vp . y ,
gl - > vp . width , gl - > vp . height ,
GL_RGBA , GL_UNSIGNED_BYTE , buffer ) ;
}
2021-11-23 08:45:54 +01:00
static void gl3_pbo_async_readback ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
glBindBuffer ( GL_PIXEL_PACK_BUFFER , gl - > pbo_readback [ gl - > pbo_readback_index + + ] ) ;
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
glPixelStorei ( GL_PACK_ROW_LENGTH , 0 ) ;
# ifndef HAVE_OPENGLES
glReadBuffer ( GL_BACK ) ;
# endif
if ( gl - > pbo_readback_index > = GL_CORE_NUM_PBOS )
gl - > pbo_readback_index = 0 ;
gl - > pbo_readback_valid [ gl - > pbo_readback_index ] = true ;
glReadPixels ( gl - > vp . x , gl - > vp . y ,
gl - > vp . width , gl - > vp . height ,
GL_RGBA , GL_UNSIGNED_BYTE , NULL ) ;
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
}
2021-11-23 08:45:54 +01:00
static void gl3_fence_iterate ( gl3_t * gl , unsigned hard_sync_frames )
2019-03-02 11:53:14 +01:00
{
if ( gl - > fence_count < GL_CORE_NUM_FENCES )
{
/*
* We need to do some work after the flip , or we risk fencing too early .
* Do as little work as possible .
*/
glEnable ( GL_SCISSOR_TEST ) ;
glScissor ( 0 , 0 , 1 , 1 ) ;
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
glDisable ( GL_SCISSOR_TEST ) ;
gl - > fences [ gl - > fence_count + + ] = glFenceSync ( GL_SYNC_GPU_COMMANDS_COMPLETE , 0 ) ;
}
while ( gl - > fence_count > hard_sync_frames )
{
glClientWaitSync ( gl - > fences [ 0 ] , GL_SYNC_FLUSH_COMMANDS_BIT , 1000000000 ) ;
glDeleteSync ( gl - > fences [ 0 ] ) ;
gl - > fence_count - - ;
memmove ( gl - > fences , gl - > fences + 1 , gl - > fence_count * sizeof ( GLsync ) ) ;
}
}
2021-11-23 08:45:54 +01:00
uint32_t gl3_get_cross_compiler_target_version ( void )
2020-01-22 11:57:06 +01:00
{
const char * version = ( const char * ) glGetString ( GL_VERSION ) ;
unsigned major = 0 ;
unsigned minor = 0 ;
# ifdef HAVE_OPENGLES3
if ( ! version | | sscanf ( version , " OpenGL ES %u.%u " , & major , & minor ) ! = 2 )
return 300 ;
if ( major = = 2 & & minor = = 0 )
return 100 ;
# else
if ( ! version | | sscanf ( version , " %u.%u " , & major , & minor ) ! = 2 )
return 150 ;
if ( major = = 3 )
{
switch ( minor )
{
case 2 :
return 150 ;
case 1 :
return 140 ;
case 0 :
return 130 ;
}
}
else if ( major = = 2 )
{
switch ( minor )
{
case 1 :
return 120 ;
case 0 :
return 110 ;
}
}
# endif
return 100 * major + 10 * minor ;
}
2021-11-23 08:45:54 +01:00
GLuint gl3_compile_shader ( GLenum stage , const char * source )
2020-01-22 11:57:06 +01:00
{
GLint status ;
GLuint shader = glCreateShader ( stage ) ;
const char * ptr = source ;
glShaderSource ( shader , 1 , & ptr , NULL ) ;
glCompileShader ( shader ) ;
glGetShaderiv ( shader , GL_COMPILE_STATUS , & status ) ;
if ( ! status )
{
GLint length ;
glGetShaderiv ( shader , GL_INFO_LOG_LENGTH , & length ) ;
if ( length > 0 )
{
char * info_log = ( char * ) malloc ( length ) ;
if ( info_log )
{
glGetShaderInfoLog ( shader , length , & length , info_log ) ;
RARCH_ERR ( " [GLCore]: Failed to compile shader: %s \n " , info_log ) ;
free ( info_log ) ;
glDeleteShader ( shader ) ;
return 0 ;
}
}
}
return shader ;
}
2021-11-23 08:45:54 +01:00
void gl3_framebuffer_clear ( GLuint id )
2020-01-22 11:57:06 +01:00
{
glBindFramebuffer ( GL_FRAMEBUFFER , id ) ;
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
glClearColor ( 0.0f , 0.0f , 0.0f , 0.0f ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
}
2021-11-23 08:45:54 +01:00
void gl3_bind_scratch_vbo ( gl3_t * gl , const void * data , size_t size )
2019-03-02 11:53:14 +01:00
{
if ( ! gl - > scratch_vbos [ gl - > scratch_vbo_index ] )
glGenBuffers ( 1 , & gl - > scratch_vbos [ gl - > scratch_vbo_index ] ) ;
glBindBuffer ( GL_ARRAY_BUFFER , gl - > scratch_vbos [ gl - > scratch_vbo_index ] ) ;
glBufferData ( GL_ARRAY_BUFFER , size , data , GL_STREAM_DRAW ) ;
gl - > scratch_vbo_index + + ;
if ( gl - > scratch_vbo_index > = GL_CORE_NUM_VBOS )
gl - > scratch_vbo_index = 0 ;
}
2019-07-10 05:39:50 +02:00
# ifdef HAVE_OVERLAY
2021-11-23 08:45:54 +01:00
static void gl3_free_overlay ( gl3_t * gl )
2019-07-10 05:39:50 +02:00
{
if ( gl - > overlay_tex )
glDeleteTextures ( gl - > overlays , gl - > overlay_tex ) ;
free ( gl - > overlay_tex ) ;
free ( gl - > overlay_vertex_coord ) ;
free ( gl - > overlay_tex_coord ) ;
free ( gl - > overlay_color_coord ) ;
gl - > overlay_tex = NULL ;
gl - > overlay_vertex_coord = NULL ;
gl - > overlay_tex_coord = NULL ;
gl - > overlay_color_coord = NULL ;
gl - > overlays = 0 ;
}
2021-11-23 08:45:54 +01:00
static void gl3_free_scratch_vbos ( gl3_t * gl )
2019-07-10 05:39:50 +02:00
{
unsigned i ;
for ( i = 0 ; i < GL_CORE_NUM_VBOS ; i + + )
if ( gl - > scratch_vbos [ i ] )
glDeleteBuffers ( 1 , & gl - > scratch_vbos [ i ] ) ;
}
2021-11-23 08:45:54 +01:00
static void gl3_overlay_vertex_geom ( void * data ,
2019-03-02 11:53:14 +01:00
unsigned image ,
float x , float y ,
float w , float h )
{
GLfloat * vertex = NULL ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
if ( image > gl - > overlays )
{
RARCH_ERR ( " [GLCore]: Invalid overlay id: %u \n " , image ) ;
return ;
}
vertex = ( GLfloat * ) & gl - > overlay_vertex_coord [ image * 8 ] ;
/* Flipped, so we preserve top-down semantics. */
y = 1.0f - y ;
h = - h ;
vertex [ 0 ] = x ;
vertex [ 1 ] = y ;
vertex [ 2 ] = x + w ;
vertex [ 3 ] = y ;
vertex [ 4 ] = x ;
vertex [ 5 ] = y + h ;
vertex [ 6 ] = x + w ;
vertex [ 7 ] = y + h ;
}
2021-11-23 08:45:54 +01:00
static void gl3_overlay_tex_geom ( void * data ,
2019-03-02 11:53:14 +01:00
unsigned image ,
GLfloat x , GLfloat y ,
GLfloat w , GLfloat h )
{
GLfloat * tex = NULL ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
tex = ( GLfloat * ) & gl - > overlay_tex_coord [ image * 8 ] ;
tex [ 0 ] = x ;
tex [ 1 ] = y ;
tex [ 2 ] = x + w ;
tex [ 3 ] = y ;
tex [ 4 ] = x ;
tex [ 5 ] = y + h ;
tex [ 6 ] = x + w ;
tex [ 7 ] = y + h ;
}
2021-11-23 08:45:54 +01:00
static void gl3_render_overlay ( gl3_t * gl ,
2020-03-10 19:32:21 +01:00
unsigned width , unsigned height )
2019-03-02 11:53:14 +01:00
{
unsigned i ;
glEnable ( GL_BLEND ) ;
glDisable ( GL_CULL_FACE ) ;
glDisable ( GL_DEPTH_TEST ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
glBlendEquation ( GL_FUNC_ADD ) ;
if ( gl - > overlay_full_screen )
glViewport ( 0 , 0 , width , height ) ;
/* Ensure that we reset the attrib array. */
glUseProgram ( gl - > pipelines . alpha_blend ) ;
if ( gl - > pipelines . alpha_blend_loc . flat_ubo_vertex > = 0 )
glUniform4fv ( gl - > pipelines . alpha_blend_loc . flat_ubo_vertex , 4 , gl - > mvp_no_rot . data ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glEnableVertexAttribArray ( 2 ) ;
2021-11-23 08:45:54 +01:00
gl3_bind_scratch_vbo ( gl , gl - > overlay_vertex_coord , 8 * sizeof ( float ) * gl - > overlays ) ;
2019-03-02 11:53:14 +01:00
glVertexAttribPointer ( 0 , 2 , GL_FLOAT , GL_FALSE , 2 * sizeof ( float ) , ( void * ) ( uintptr_t ) 0 ) ;
2021-11-23 08:45:54 +01:00
gl3_bind_scratch_vbo ( gl , gl - > overlay_tex_coord , 8 * sizeof ( float ) * gl - > overlays ) ;
2019-03-02 11:53:14 +01:00
glVertexAttribPointer ( 1 , 2 , GL_FLOAT , GL_FALSE , 2 * sizeof ( float ) , ( void * ) ( uintptr_t ) 0 ) ;
2021-11-23 08:45:54 +01:00
gl3_bind_scratch_vbo ( gl , gl - > overlay_color_coord , 16 * sizeof ( float ) * gl - > overlays ) ;
2019-03-02 11:53:14 +01:00
glVertexAttribPointer ( 2 , 4 , GL_FLOAT , GL_FALSE , 4 * sizeof ( float ) , ( void * ) ( uintptr_t ) 0 ) ;
for ( i = 0 ; i < gl - > overlays ; i + + )
{
glActiveTexture ( GL_TEXTURE1 ) ;
glBindTexture ( GL_TEXTURE_2D , gl - > overlay_tex [ i ] ) ;
glDrawArrays ( GL_TRIANGLE_STRIP , 4 * i , 4 ) ;
}
glDisableVertexAttribArray ( 0 ) ;
glDisableVertexAttribArray ( 1 ) ;
glDisableVertexAttribArray ( 2 ) ;
glDisable ( GL_BLEND ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
if ( gl - > overlay_full_screen )
glViewport ( gl - > vp . x , gl - > vp . y , gl - > vp . width , gl - > vp . height ) ;
}
# endif
2021-11-23 08:45:54 +01:00
# define gl3_context_bind_hw_render(gl, enable) \
2019-08-12 20:52:33 +02:00
if ( gl - > use_shared_context ) \
gl - > ctx_driver - > bind_hw_render ( gl - > ctx_data , enable )
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
static void gl3_deinit_hw_render ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
if ( gl - > hw_render_fbo )
glDeleteFramebuffers ( 1 , & gl - > hw_render_fbo ) ;
if ( gl - > hw_render_rb_ds )
glDeleteRenderbuffers ( 1 , & gl - > hw_render_rb_ds ) ;
if ( gl - > hw_render_texture )
glDeleteTextures ( 1 , & gl - > hw_render_texture ) ;
gl - > hw_render_fbo = 0 ;
gl - > hw_render_rb_ds = 0 ;
gl - > hw_render_texture = 0 ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
gl - > hw_render_enable = false ;
}
2021-11-23 08:45:54 +01:00
static void gl3_destroy_resources ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
2019-03-13 11:21:00 +01:00
unsigned i ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-09 20:53:29 +01:00
2019-03-02 11:53:14 +01:00
if ( gl - > filter_chain )
2021-11-23 08:45:54 +01:00
gl3_filter_chain_free ( gl - > filter_chain ) ;
2019-03-02 11:53:14 +01:00
gl - > filter_chain = NULL ;
glBindVertexArray ( 0 ) ;
if ( gl - > vao ! = 0 )
glDeleteVertexArrays ( 1 , & gl - > vao ) ;
for ( i = 0 ; i < GL_CORE_NUM_TEXTURES ; i + + )
{
if ( gl - > textures [ i ] . tex ! = 0 )
glDeleteTextures ( 1 , & gl - > textures [ i ] . tex ) ;
}
memset ( gl - > textures , 0 , sizeof ( gl - > textures ) ) ;
if ( gl - > menu_texture ! = 0 )
glDeleteTextures ( 1 , & gl - > menu_texture ) ;
if ( gl - > pipelines . alpha_blend )
glDeleteProgram ( gl - > pipelines . alpha_blend ) ;
if ( gl - > pipelines . font )
glDeleteProgram ( gl - > pipelines . font ) ;
if ( gl - > pipelines . ribbon )
glDeleteProgram ( gl - > pipelines . ribbon ) ;
if ( gl - > pipelines . ribbon_simple )
glDeleteProgram ( gl - > pipelines . ribbon_simple ) ;
if ( gl - > pipelines . snow_simple )
glDeleteProgram ( gl - > pipelines . snow_simple ) ;
if ( gl - > pipelines . snow )
glDeleteProgram ( gl - > pipelines . snow ) ;
if ( gl - > pipelines . bokeh )
glDeleteProgram ( gl - > pipelines . bokeh ) ;
2019-07-10 05:39:50 +02:00
# ifdef HAVE_OVERLAY
2021-11-23 08:45:54 +01:00
gl3_free_overlay ( gl ) ;
gl3_free_scratch_vbos ( gl ) ;
2019-07-10 05:39:50 +02:00
# endif
2021-11-23 08:45:54 +01:00
gl3_deinit_fences ( gl ) ;
gl3_deinit_pbo_readback ( gl ) ;
gl3_deinit_hw_render ( gl ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static bool gl3_init_hw_render ( gl3_t * gl , unsigned width , unsigned height )
2019-03-02 11:53:14 +01:00
{
GLint max_fbo_size ;
GLint max_rb_size ;
GLenum status ;
struct retro_hw_render_callback * hwr = video_driver_get_hw_context ( ) ;
2019-03-13 11:21:00 +01:00
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
2021-12-04 15:21:12 +02:00
RARCH_LOG ( " [GLCore]: Initializing HW render (%ux%u). \n " , width , height ) ;
2019-03-02 11:53:14 +01:00
glGetIntegerv ( GL_MAX_TEXTURE_SIZE , & max_fbo_size ) ;
glGetIntegerv ( GL_MAX_RENDERBUFFER_SIZE , & max_rb_size ) ;
RARCH_LOG ( " [GLCore]: Max texture size: %d px, renderbuffer size: %d px. \n " ,
max_fbo_size , max_rb_size ) ;
2019-04-12 17:28:53 +01:00
if ( width > ( unsigned ) max_fbo_size )
2019-03-09 12:30:01 +01:00
width = max_fbo_size ;
2019-04-12 17:28:53 +01:00
if ( width > ( unsigned ) max_rb_size )
2019-03-09 12:30:01 +01:00
width = max_rb_size ;
2019-04-12 17:28:53 +01:00
if ( height > ( unsigned ) max_fbo_size )
2019-03-09 12:30:01 +01:00
height = max_fbo_size ;
2019-04-12 17:28:53 +01:00
if ( height > ( unsigned ) max_rb_size )
2019-03-09 12:30:01 +01:00
height = max_rb_size ;
2019-03-02 11:53:14 +01:00
glGenFramebuffers ( 1 , & gl - > hw_render_fbo ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , gl - > hw_render_fbo ) ;
glGenTextures ( 1 , & gl - > hw_render_texture ) ;
glBindTexture ( GL_TEXTURE_2D , gl - > hw_render_texture ) ;
glTexStorage2D ( GL_TEXTURE_2D , 1 , GL_RGBA8 , width , height ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , gl - > hw_render_texture , 0 ) ;
gl - > hw_render_rb_ds = 0 ;
gl - > hw_render_bottom_left = hwr - > bottom_left_origin ;
if ( hwr - > depth )
{
glGenRenderbuffers ( 1 , & gl - > hw_render_rb_ds ) ;
2019-03-09 12:30:01 +01:00
glBindRenderbuffer ( GL_RENDERBUFFER , gl - > hw_render_rb_ds ) ;
2019-03-02 11:53:14 +01:00
glRenderbufferStorage ( GL_RENDERBUFFER , hwr - > stencil ? GL_DEPTH24_STENCIL8 : GL_DEPTH_COMPONENT16 ,
width , height ) ;
2019-03-09 12:30:01 +01:00
glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ;
2019-03-02 11:53:14 +01:00
if ( hwr - > stencil )
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_STENCIL_ATTACHMENT , GL_RENDERBUFFER , gl - > hw_render_rb_ds ) ;
else
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , gl - > hw_render_rb_ds ) ;
}
status = glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ;
if ( status ! = GL_FRAMEBUFFER_COMPLETE )
{
RARCH_ERR ( " [GLCore]: Framebuffer is not complete. \n " ) ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
return false ;
}
2019-03-09 13:15:14 +01:00
if ( hwr - > depth & & hwr - > stencil )
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
else if ( hwr - > depth )
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
else
glClear ( GL_COLOR_BUFFER_BIT ) ;
2019-03-02 11:53:14 +01:00
gl - > hw_render_enable = true ;
gl - > hw_render_max_width = width ;
gl - > hw_render_max_height = height ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
return true ;
}
2021-11-23 08:45:54 +01:00
static const gfx_ctx_driver_t * gl3_get_context ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
2019-03-13 11:21:00 +01:00
unsigned major ;
unsigned minor ;
2019-03-02 11:53:14 +01:00
enum gfx_ctx_api api ;
2019-03-13 11:21:00 +01:00
gfx_ctx_flags_t flags ;
2019-03-02 11:53:14 +01:00
const gfx_ctx_driver_t * gfx_ctx = NULL ;
void * ctx_data = NULL ;
2020-08-02 23:44:09 +02:00
settings_t * settings = config_get_ptr ( ) ;
struct retro_hw_render_callback * hwr = video_driver_get_hw_context ( ) ;
2019-03-02 11:53:14 +01:00
# ifdef HAVE_OPENGLES3
2020-08-02 23:44:09 +02:00
api = GFX_CTX_OPENGL_ES_API ;
major = 3 ;
minor = 0 ;
2019-03-02 11:53:14 +01:00
if ( hwr & & hwr - > context_type = = RETRO_HW_CONTEXT_OPENGLES_VERSION )
{
2020-08-02 23:44:09 +02:00
major = hwr - > version_major ;
minor = hwr - > version_minor ;
2019-03-02 11:53:14 +01:00
}
# else
2020-08-02 23:44:09 +02:00
api = GFX_CTX_OPENGL_API ;
2019-03-07 16:49:13 +01:00
if ( hwr & & hwr - > context_type ! = RETRO_HW_CONTEXT_NONE )
2019-03-02 11:53:14 +01:00
{
2020-08-02 23:44:09 +02:00
major = hwr - > version_major ;
minor = hwr - > version_minor ;
gl_query_core_context_set ( hwr - > context_type = = RETRO_HW_CONTEXT_OPENGL_CORE ) ;
2019-03-02 11:53:14 +01:00
if ( hwr - > context_type = = RETRO_HW_CONTEXT_OPENGL_CORE )
{
flags . flags = 0 ;
BIT32_SET ( flags . flags , GFX_CTX_FLAGS_GL_CORE_CONTEXT ) ;
video_context_driver_set_flags ( & flags ) ;
}
}
else
{
2020-08-02 23:44:09 +02:00
major = 3 ;
minor = 2 ;
2019-03-02 11:53:14 +01:00
gl_query_core_context_set ( true ) ;
2020-08-02 23:44:09 +02:00
flags . flags = 0 ;
2019-03-02 11:53:14 +01:00
BIT32_SET ( flags . flags , GFX_CTX_FLAGS_GL_CORE_CONTEXT ) ;
video_context_driver_set_flags ( & flags ) ;
}
# endif
/* Force shared context. */
2019-04-18 11:46:53 -04:00
if ( hwr )
2020-08-02 23:44:09 +02:00
gl - > use_shared_context = hwr - > context_type ! = RETRO_HW_CONTEXT_NONE ;
2019-03-02 11:53:14 +01:00
2020-08-02 23:44:09 +02:00
gfx_ctx = video_context_driver_init_first ( gl ,
settings - > arrays . video_context_driver ,
api , major , minor , gl - > use_shared_context , & ctx_data ) ;
2019-03-02 11:53:14 +01:00
if ( ctx_data )
2020-08-02 23:44:09 +02:00
gl - > ctx_data = ctx_data ;
2019-03-02 11:53:14 +01:00
2020-08-02 23:44:09 +02:00
/* Need to force here since video_context_driver_init also checks for global option. */
2019-03-02 11:53:14 +01:00
if ( gfx_ctx - > bind_hw_render )
gfx_ctx - > bind_hw_render ( ctx_data , gl - > use_shared_context ) ;
return gfx_ctx ;
}
2021-11-23 08:45:54 +01:00
static void gl3_set_projection ( gl3_t * gl ,
2019-08-12 21:07:12 +02:00
const struct video_ortho * ortho , bool allow_rotate )
2019-03-02 11:53:14 +01:00
{
2022-07-04 15:28:08 +02:00
static math_matrix_4x4 rot = {
{ 0.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 0.0f , 1.0f }
} ;
float radians , cosine , sine ;
2019-03-02 11:53:14 +01:00
/* Calculate projection. */
matrix_4x4_ortho ( gl - > mvp_no_rot , ortho - > left , ortho - > right ,
ortho - > bottom , ortho - > top , ortho - > znear , ortho - > zfar ) ;
if ( ! allow_rotate )
{
gl - > mvp = gl - > mvp_no_rot ;
return ;
}
2022-07-04 15:28:08 +02:00
radians = M_PI * gl - > rotation / 180.0f ;
cosine = cosf ( radians ) ;
sine = sinf ( radians ) ;
MAT_ELEM_4X4 ( rot , 0 , 0 ) = cosine ;
MAT_ELEM_4X4 ( rot , 0 , 1 ) = - sine ;
MAT_ELEM_4X4 ( rot , 1 , 0 ) = sine ;
MAT_ELEM_4X4 ( rot , 1 , 1 ) = cosine ;
2019-03-02 11:53:14 +01:00
matrix_4x4_multiply ( gl - > mvp , rot , gl - > mvp_no_rot ) ;
memcpy ( gl - > mvp_no_rot_yflip . data , gl - > mvp_no_rot . data , sizeof ( gl - > mvp_no_rot . data ) ) ;
MAT_ELEM_4X4 ( gl - > mvp_no_rot_yflip , 1 , 0 ) * = - 1.0f ;
MAT_ELEM_4X4 ( gl - > mvp_no_rot_yflip , 1 , 1 ) * = - 1.0f ;
MAT_ELEM_4X4 ( gl - > mvp_no_rot_yflip , 1 , 2 ) * = - 1.0f ;
MAT_ELEM_4X4 ( gl - > mvp_no_rot_yflip , 1 , 3 ) * = - 1.0f ;
memcpy ( gl - > mvp_yflip . data , gl - > mvp . data , sizeof ( gl - > mvp . data ) ) ;
MAT_ELEM_4X4 ( gl - > mvp_yflip , 1 , 0 ) * = - 1.0f ;
MAT_ELEM_4X4 ( gl - > mvp_yflip , 1 , 1 ) * = - 1.0f ;
MAT_ELEM_4X4 ( gl - > mvp_yflip , 1 , 2 ) * = - 1.0f ;
MAT_ELEM_4X4 ( gl - > mvp_yflip , 1 , 3 ) * = - 1.0f ;
}
2021-11-23 08:45:54 +01:00
static void gl3_set_viewport ( gl3_t * gl ,
2019-08-12 21:07:12 +02:00
unsigned viewport_width ,
unsigned viewport_height ,
bool force_full , bool allow_rotate )
2019-03-02 11:53:14 +01:00
{
2020-03-06 02:52:21 +01:00
unsigned height = gl - > video_height ;
int x = 0 ;
int y = 0 ;
settings_t * settings = config_get_ptr ( ) ;
float device_aspect = ( float ) viewport_width / viewport_height ;
bool video_scale_integer = settings - > bools . video_scale_integer ;
unsigned video_aspect_ratio_idx = settings - > uints . video_aspect_ratio_idx ;
2020-07-27 08:15:35 +02:00
if ( gl - > ctx_driver - > translate_aspect )
device_aspect = gl - > ctx_driver - > translate_aspect (
gl - > ctx_data , viewport_width , viewport_height ) ;
2019-03-02 11:53:14 +01:00
2020-03-06 02:52:21 +01:00
if ( video_scale_integer & & ! force_full )
2019-03-02 11:53:14 +01:00
{
video_viewport_get_scaled_integer ( & gl - > vp ,
2019-08-12 21:07:12 +02:00
viewport_width , viewport_height ,
video_driver_get_aspect_ratio ( ) , gl - > keep_aspect ) ;
2019-03-02 11:53:14 +01:00
viewport_width = gl - > vp . width ;
viewport_height = gl - > vp . height ;
}
else if ( gl - > keep_aspect & & ! force_full )
{
float desired_aspect = video_driver_get_aspect_ratio ( ) ;
# if defined(HAVE_MENU)
2020-03-06 02:52:21 +01:00
if ( video_aspect_ratio_idx = = ASPECT_RATIO_CUSTOM )
2019-03-02 11:53:14 +01:00
{
const struct video_viewport * custom = video_viewport_get_custom ( ) ;
/* GL has bottom-left origin viewport. */
x = custom - > x ;
y = height - custom - > y - custom - > height ;
viewport_width = custom - > width ;
viewport_height = custom - > height ;
}
else
# endif
{
float delta ;
if ( fabsf ( device_aspect - desired_aspect ) < 0.0001f )
{
/* If the aspect ratios of screen and desired aspect
* ratio are sufficiently equal ( floating point stuff ) ,
* assume they are actually equal .
*/
}
else if ( device_aspect > desired_aspect )
{
delta = ( desired_aspect / device_aspect - 1.0f ) / 2.0f + 0.5f ;
x = ( int ) roundf ( viewport_width * ( 0.5f - delta ) ) ;
viewport_width = ( unsigned ) roundf ( 2.0f * viewport_width * delta ) ;
}
else
{
delta = ( device_aspect / desired_aspect - 1.0f ) / 2.0f + 0.5f ;
y = ( int ) roundf ( viewport_height * ( 0.5f - delta ) ) ;
viewport_height = ( unsigned ) roundf ( 2.0f * viewport_height * delta ) ;
}
}
gl - > vp . x = x ;
gl - > vp . y = y ;
gl - > vp . width = viewport_width ;
gl - > vp . height = viewport_height ;
}
else
{
gl - > vp . x = gl - > vp . y = 0 ;
gl - > vp . width = viewport_width ;
gl - > vp . height = viewport_height ;
}
# if defined(RARCH_MOBILE)
/* In portrait mode, we want viewport to gravitate to top of screen. */
if ( device_aspect < 1.0f )
gl - > vp . y * = 2 ;
# endif
glViewport ( gl - > vp . x , gl - > vp . y , gl - > vp . width , gl - > vp . height ) ;
2021-11-23 08:45:54 +01:00
gl3_set_projection ( gl , & gl3_default_ortho , allow_rotate ) ;
2019-03-02 11:53:14 +01:00
/* Set last backbuffer viewport. */
if ( ! force_full )
{
gl - > vp_out_width = viewport_width ;
gl - > vp_out_height = viewport_height ;
}
gl - > filter_chain_vp . x = gl - > vp . x ;
gl - > filter_chain_vp . y = gl - > vp . y ;
gl - > filter_chain_vp . width = gl - > vp . width ;
gl - > filter_chain_vp . height = gl - > vp . height ;
#if 0
RARCH_LOG ( " Setting viewport @ %ux%u \n " , viewport_width , viewport_height ) ;
# endif
}
2021-11-23 08:45:54 +01:00
static bool gl3_init_pipelines ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
static const uint32_t alpha_blend_vert [ ] =
# include "vulkan_shaders/alpha_blend.vert.inc"
;
static const uint32_t alpha_blend_frag [ ] =
# include "vulkan_shaders/alpha_blend.frag.inc"
;
static const uint32_t font_frag [ ] =
# include "vulkan_shaders/font.frag.inc"
;
static const uint32_t pipeline_ribbon_vert [ ] =
# include "vulkan_shaders/pipeline_ribbon.vert.inc"
;
static const uint32_t pipeline_ribbon_frag [ ] =
# include "vulkan_shaders/pipeline_ribbon.frag.inc"
;
static const uint32_t pipeline_ribbon_simple_vert [ ] =
# include "vulkan_shaders/pipeline_ribbon_simple.vert.inc"
;
static const uint32_t pipeline_ribbon_simple_frag [ ] =
# include "vulkan_shaders/pipeline_ribbon_simple.frag.inc"
;
static const uint32_t pipeline_snow_simple_frag [ ] =
# include "vulkan_shaders/pipeline_snow_simple.frag.inc"
;
static const uint32_t pipeline_snow_frag [ ] =
# include "vulkan_shaders/pipeline_snow.frag.inc"
;
static const uint32_t pipeline_bokeh_frag [ ] =
# include "vulkan_shaders/pipeline_bokeh.frag.inc"
;
2021-11-23 08:45:54 +01:00
gl - > pipelines . alpha_blend = gl3_cross_compile_program ( alpha_blend_vert , sizeof ( alpha_blend_vert ) ,
2019-03-02 11:53:14 +01:00
alpha_blend_frag , sizeof ( alpha_blend_frag ) ,
& gl - > pipelines . alpha_blend_loc , true ) ;
if ( ! gl - > pipelines . alpha_blend )
return false ;
2021-11-23 08:45:54 +01:00
gl - > pipelines . font = gl3_cross_compile_program ( alpha_blend_vert , sizeof ( alpha_blend_vert ) ,
2019-03-02 11:53:14 +01:00
font_frag , sizeof ( font_frag ) ,
& gl - > pipelines . font_loc , true ) ;
if ( ! gl - > pipelines . font )
return false ;
2021-11-23 08:45:54 +01:00
gl - > pipelines . ribbon_simple = gl3_cross_compile_program ( pipeline_ribbon_simple_vert , sizeof ( pipeline_ribbon_simple_vert ) ,
2019-03-02 11:53:14 +01:00
pipeline_ribbon_simple_frag , sizeof ( pipeline_ribbon_simple_frag ) ,
& gl - > pipelines . ribbon_simple_loc , true ) ;
if ( ! gl - > pipelines . ribbon_simple )
return false ;
2021-11-23 08:45:54 +01:00
gl - > pipelines . ribbon = gl3_cross_compile_program ( pipeline_ribbon_vert , sizeof ( pipeline_ribbon_vert ) ,
2019-03-02 11:53:14 +01:00
pipeline_ribbon_frag , sizeof ( pipeline_ribbon_frag ) ,
& gl - > pipelines . ribbon_loc , true ) ;
if ( ! gl - > pipelines . ribbon )
return false ;
2021-11-23 08:45:54 +01:00
gl - > pipelines . bokeh = gl3_cross_compile_program ( alpha_blend_vert , sizeof ( alpha_blend_vert ) ,
2019-03-02 11:53:14 +01:00
pipeline_bokeh_frag , sizeof ( pipeline_bokeh_frag ) ,
& gl - > pipelines . bokeh_loc , true ) ;
if ( ! gl - > pipelines . bokeh )
return false ;
2021-11-23 08:45:54 +01:00
gl - > pipelines . snow_simple = gl3_cross_compile_program ( alpha_blend_vert , sizeof ( alpha_blend_vert ) ,
2019-03-02 11:53:14 +01:00
pipeline_snow_simple_frag , sizeof ( pipeline_snow_simple_frag ) ,
& gl - > pipelines . snow_simple_loc , true ) ;
if ( ! gl - > pipelines . snow_simple )
return false ;
2021-11-23 08:45:54 +01:00
gl - > pipelines . snow = gl3_cross_compile_program ( alpha_blend_vert , sizeof ( alpha_blend_vert ) ,
2019-03-02 11:53:14 +01:00
pipeline_snow_frag , sizeof ( pipeline_snow_frag ) ,
& gl - > pipelines . snow_loc , true ) ;
if ( ! gl - > pipelines . snow )
return false ;
return true ;
}
2021-11-23 08:45:54 +01:00
static bool gl3_init_default_filter_chain ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
if ( ! gl - > ctx_driver )
return false ;
2021-11-23 08:45:54 +01:00
gl - > filter_chain = gl3_filter_chain_create_default (
2020-08-16 02:05:38 +02:00
gl - > video_info . smooth
? GLSLANG_FILTER_CHAIN_LINEAR
: GLSLANG_FILTER_CHAIN_NEAREST ) ;
2019-03-02 11:53:14 +01:00
if ( ! gl - > filter_chain )
{
RARCH_ERR ( " Failed to create filter chain. \n " ) ;
return false ;
}
return true ;
}
2021-11-23 08:45:54 +01:00
static bool gl3_init_filter_chain_preset ( gl3_t * gl , const char * shader_path )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl - > filter_chain = gl3_filter_chain_create_from_preset (
2019-03-02 11:53:14 +01:00
shader_path ,
2020-08-16 02:05:38 +02:00
gl - > video_info . smooth
? GLSLANG_FILTER_CHAIN_LINEAR
: GLSLANG_FILTER_CHAIN_NEAREST ) ;
2019-03-02 11:53:14 +01:00
if ( ! gl - > filter_chain )
{
RARCH_ERR ( " [GLCore]: Failed to create preset: \" %s \" . \n " , shader_path ) ;
return false ;
}
return true ;
}
2021-11-23 08:45:54 +01:00
static bool gl3_init_filter_chain ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
2019-06-17 18:26:41 +02:00
const char * shader_path = retroarch_get_shader_preset ( ) ;
enum rarch_shader_type type = video_shader_parse_type ( shader_path ) ;
2019-03-02 11:53:14 +01:00
2019-06-04 21:52:58 +02:00
if ( string_is_empty ( shader_path ) )
2019-03-02 11:53:14 +01:00
{
RARCH_LOG ( " [GLCore]: Loading stock shader. \n " ) ;
2021-11-23 08:45:54 +01:00
return gl3_init_default_filter_chain ( gl ) ;
2019-03-02 11:53:14 +01:00
}
if ( type ! = RARCH_SHADER_SLANG )
{
2019-06-04 21:52:58 +02:00
RARCH_WARN ( " [GLCore]: Only Slang shaders are supported, falling back to stock. \n " ) ;
2021-11-23 08:45:54 +01:00
return gl3_init_default_filter_chain ( gl ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
if ( ! gl3_init_filter_chain_preset ( gl , shader_path ) )
gl3_init_default_filter_chain ( gl ) ;
2019-03-02 11:53:14 +01:00
return true ;
}
# ifdef GL_DEBUG
# ifdef HAVE_OPENGLES3
# define DEBUG_CALLBACK_TYPE GL_APIENTRY
# define GL_DEBUG_SOURCE_API GL_DEBUG_SOURCE_API_KHR
# define GL_DEBUG_SOURCE_WINDOW_SYSTEM GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR
# define GL_DEBUG_SOURCE_SHADER_COMPILER GL_DEBUG_SOURCE_SHADER_COMPILER_KHR
# define GL_DEBUG_SOURCE_THIRD_PARTY GL_DEBUG_SOURCE_THIRD_PARTY_KHR
# define GL_DEBUG_SOURCE_APPLICATION GL_DEBUG_SOURCE_APPLICATION_KHR
# define GL_DEBUG_SOURCE_OTHER GL_DEBUG_SOURCE_OTHER_KHR
# define GL_DEBUG_TYPE_ERROR GL_DEBUG_TYPE_ERROR_KHR
# define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR
# define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR
# define GL_DEBUG_TYPE_PORTABILITY GL_DEBUG_TYPE_PORTABILITY_KHR
# define GL_DEBUG_TYPE_PERFORMANCE GL_DEBUG_TYPE_PERFORMANCE_KHR
# define GL_DEBUG_TYPE_MARKER GL_DEBUG_TYPE_MARKER_KHR
# define GL_DEBUG_TYPE_PUSH_GROUP GL_DEBUG_TYPE_PUSH_GROUP_KHR
# define GL_DEBUG_TYPE_POP_GROUP GL_DEBUG_TYPE_POP_GROUP_KHR
# define GL_DEBUG_TYPE_OTHER GL_DEBUG_TYPE_OTHER_KHR
# define GL_DEBUG_SEVERITY_HIGH GL_DEBUG_SEVERITY_HIGH_KHR
# define GL_DEBUG_SEVERITY_MEDIUM GL_DEBUG_SEVERITY_MEDIUM_KHR
# define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR
# else
# define DEBUG_CALLBACK_TYPE APIENTRY
# endif
2021-11-23 08:45:54 +01:00
static void DEBUG_CALLBACK_TYPE gl3_debug_cb ( GLenum source , GLenum type ,
2019-03-02 11:53:14 +01:00
GLuint id , GLenum severity , GLsizei length ,
const GLchar * message , void * userParam )
{
const char * src = NULL ;
const char * typestr = NULL ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) userParam ; /* Useful for debugger. */
2019-03-02 11:53:14 +01:00
( void ) gl ;
( void ) id ;
( void ) length ;
switch ( source )
{
case GL_DEBUG_SOURCE_API :
src = " API " ;
break ;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM :
src = " Window system " ;
break ;
case GL_DEBUG_SOURCE_SHADER_COMPILER :
src = " Shader compiler " ;
break ;
case GL_DEBUG_SOURCE_THIRD_PARTY :
src = " 3rd party " ;
break ;
case GL_DEBUG_SOURCE_APPLICATION :
src = " Application " ;
break ;
case GL_DEBUG_SOURCE_OTHER :
src = " Other " ;
break ;
default :
src = " Unknown " ;
break ;
}
switch ( type )
{
case GL_DEBUG_TYPE_ERROR :
typestr = " Error " ;
break ;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR :
typestr = " Deprecated behavior " ;
break ;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR :
typestr = " Undefined behavior " ;
break ;
case GL_DEBUG_TYPE_PORTABILITY :
typestr = " Portability " ;
break ;
case GL_DEBUG_TYPE_PERFORMANCE :
typestr = " Performance " ;
break ;
case GL_DEBUG_TYPE_MARKER :
typestr = " Marker " ;
break ;
case GL_DEBUG_TYPE_PUSH_GROUP :
typestr = " Push group " ;
break ;
case GL_DEBUG_TYPE_POP_GROUP :
typestr = " Pop group " ;
break ;
case GL_DEBUG_TYPE_OTHER :
typestr = " Other " ;
break ;
default :
typestr = " Unknown " ;
break ;
}
switch ( severity )
{
case GL_DEBUG_SEVERITY_HIGH :
RARCH_ERR ( " [GL debug (High, %s, %s)]: %s \n " , src , typestr , message ) ;
break ;
case GL_DEBUG_SEVERITY_MEDIUM :
RARCH_WARN ( " [GL debug (Medium, %s, %s)]: %s \n " , src , typestr , message ) ;
break ;
case GL_DEBUG_SEVERITY_LOW :
RARCH_LOG ( " [GL debug (Low, %s, %s)]: %s \n " , src , typestr , message ) ;
break ;
}
}
2021-11-23 08:45:54 +01:00
static void gl3_begin_debug ( gl3_t * gl )
2019-03-02 11:53:14 +01:00
{
if ( gl_check_capability ( GL_CAPS_DEBUG ) )
{
# ifdef HAVE_OPENGLES3
2021-11-23 08:45:54 +01:00
glDebugMessageCallbackKHR ( gl3_debug_cb , gl ) ;
2019-03-02 11:53:14 +01:00
glDebugMessageControlKHR ( GL_DONT_CARE , GL_DONT_CARE , GL_DONT_CARE , 0 , NULL , GL_TRUE ) ;
glEnable ( GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR ) ;
glEnable ( GL_DEBUG_OUTPUT_KHR ) ;
# else
2021-11-23 08:45:54 +01:00
glDebugMessageCallback ( gl3_debug_cb , gl ) ;
2019-03-02 11:53:14 +01:00
glDebugMessageControl ( GL_DONT_CARE , GL_DONT_CARE , GL_DONT_CARE , 0 , NULL , GL_TRUE ) ;
glEnable ( GL_DEBUG_OUTPUT_SYNCHRONOUS ) ;
glEnable ( GL_DEBUG_OUTPUT ) ;
# endif
}
else
RARCH_ERR ( " [GLCore]: Neither GL_KHR_debug nor GL_ARB_debug_output are implemented. Cannot start GL debugging. \n " ) ;
}
# endif
2021-11-23 08:45:54 +01:00
static void gl3_set_viewport_wrapper ( void * data ,
2019-08-12 21:07:12 +02:00
unsigned viewport_width ,
unsigned viewport_height , bool force_full , bool allow_rotate )
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
gl3_set_viewport ( gl ,
2019-08-12 21:07:12 +02:00
viewport_width , viewport_height , force_full , allow_rotate ) ;
}
2021-11-23 08:45:54 +01:00
static void * gl3_init ( const video_info_t * video ,
2019-07-27 02:21:24 +02:00
input_driver_t * * input , void * * input_data )
2019-03-02 11:53:14 +01:00
{
unsigned full_x , full_y ;
settings_t * settings = config_get_ptr ( ) ;
2020-02-18 14:51:40 +01:00
bool video_gpu_record = settings - > bools . video_gpu_record ;
2019-03-02 11:53:14 +01:00
int interval = 0 ;
2020-07-27 14:33:21 +02:00
unsigned mode_width = 0 ;
unsigned mode_height = 0 ;
2019-03-02 11:53:14 +01:00
unsigned win_width = 0 ;
unsigned win_height = 0 ;
unsigned temp_width = 0 ;
unsigned temp_height = 0 ;
const char * vendor = NULL ;
const char * renderer = NULL ;
const char * version = NULL ;
char * error_string = NULL ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) calloc ( 1 , sizeof ( gl3_t ) ) ;
const gfx_ctx_driver_t * ctx_driver = gl3_get_context ( gl ) ;
2019-03-02 11:53:14 +01:00
struct retro_hw_render_callback * hwr = video_driver_get_hw_context ( ) ;
if ( ! gl | | ! ctx_driver )
goto error ;
video_context_driver_set ( ctx_driver ) ;
gl - > ctx_driver = ctx_driver ;
gl - > video_info = * video ;
2021-12-04 15:21:12 +02:00
RARCH_LOG ( " [GLCore]: Found GL context: \" %s \" . \n " , ctx_driver - > ident ) ;
2019-03-02 11:53:14 +01:00
2020-07-27 14:33:21 +02:00
if ( gl - > ctx_driver - > get_video_size )
gl - > ctx_driver - > get_video_size ( gl - > ctx_data ,
& mode_width , & mode_height ) ;
2019-03-02 11:53:14 +01:00
2020-07-27 14:33:21 +02:00
full_x = mode_width ;
full_y = mode_height ;
mode_width = 0 ;
mode_height = 0 ;
2019-03-02 11:53:14 +01:00
interval = 0 ;
2021-12-04 15:21:12 +02:00
RARCH_LOG ( " [GLCore]: Detecting screen resolution: %ux%u. \n " , full_x , full_y ) ;
2019-03-02 11:53:14 +01:00
if ( video - > vsync )
interval = video - > swap_interval ;
2019-08-28 21:12:51 +02:00
if ( gl - > ctx_driver - > swap_interval )
{
bool adaptive_vsync_enabled = video_driver_test_all_flags (
GFX_CTX_FLAGS_ADAPTIVE_VSYNC ) & & video - > adaptive_vsync ;
if ( adaptive_vsync_enabled & & interval = = 1 )
interval = - 1 ;
gl - > ctx_driver - > swap_interval ( gl - > ctx_data , interval ) ;
}
2019-03-02 11:53:14 +01:00
win_width = video - > width ;
win_height = video - > height ;
if ( video - > fullscreen & & ( win_width = = 0 ) & & ( win_height = = 0 ) )
{
win_width = full_x ;
win_height = full_y ;
}
2020-07-27 11:08:34 +02:00
if ( ! gl - > ctx_driver - > set_video_mode
| | ! gl - > ctx_driver - > set_video_mode ( gl - > ctx_data ,
win_width , win_height , video - > fullscreen ) )
2019-03-02 11:53:14 +01:00
goto error ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
rglgen_resolve_symbols ( ctx_driver - > get_proc_address ) ;
if ( hwr & & hwr - > context_type ! = RETRO_HW_CONTEXT_NONE )
2021-11-23 08:45:54 +01:00
gl3_init_hw_render ( gl , RARCH_SCALE_BASE * video - > input_scale , RARCH_SCALE_BASE * video - > input_scale ) ;
2019-03-02 11:53:14 +01:00
# ifdef GL_DEBUG
2021-11-23 08:45:54 +01:00
gl3_begin_debug ( gl ) ;
2019-03-02 11:53:14 +01:00
if ( gl - > hw_render_enable )
{
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
gl3_begin_debug ( gl ) ;
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
}
# endif
/* Clear out potential error flags in case we use cached context. */
glGetError ( ) ;
vendor = ( const char * ) glGetString ( GL_VENDOR ) ;
renderer = ( const char * ) glGetString ( GL_RENDERER ) ;
version = ( const char * ) glGetString ( GL_VERSION ) ;
RARCH_LOG ( " [GLCore]: Vendor: %s, Renderer: %s. \n " , vendor , renderer ) ;
RARCH_LOG ( " [GLCore]: Version: %s. \n " , version ) ;
if ( string_is_equal ( ctx_driver - > ident , " null " ) )
goto error ;
2021-11-23 08:45:54 +01:00
if ( ! gl3_init_pipelines ( gl ) )
2019-03-02 11:53:14 +01:00
{
RARCH_ERR ( " [GLCore]: Failed to cross-compile menu pipelines. \n " ) ;
goto error ;
}
if ( ! string_is_empty ( version ) )
2019-06-17 18:26:41 +02:00
sscanf ( version , " %u.%u " , & gl - > version_major , & gl - > version_minor ) ;
2019-03-02 11:53:14 +01:00
2019-05-21 21:43:21 -04:00
{
char device_str [ 128 ] ;
2022-08-04 03:44:19 +02:00
size_t len = strlcpy ( device_str , vendor , sizeof ( device_str ) ) ;
device_str [ len ] = ' ' ;
device_str [ len + 1 ] = ' \0 ' ;
2019-05-21 21:43:21 -04:00
strlcat ( device_str , renderer , sizeof ( device_str ) ) ;
video_driver_set_gpu_device_string ( device_str ) ;
video_driver_set_gpu_api_version_string ( version ) ;
}
# ifdef _WIN32
2021-09-28 00:50:48 +02:00
if ( string_is_equal ( vendor , " Microsoft Corporation " ) )
2019-05-21 21:43:21 -04:00
if ( string_is_equal ( renderer , " GDI Generic " ) )
# ifdef HAVE_OPENGL1
2021-09-28 00:50:48 +02:00
video_driver_force_fallback ( " gl1 " ) ;
2019-05-21 21:43:21 -04:00
# else
2021-09-28 00:50:48 +02:00
video_driver_force_fallback ( " gdi " ) ;
2019-05-21 21:43:21 -04:00
# endif
# endif
2019-03-02 11:53:14 +01:00
gl - > vsync = video - > vsync ;
gl - > fullscreen = video - > fullscreen ;
gl - > keep_aspect = video - > force_aspect ;
2020-07-27 14:33:21 +02:00
mode_width = 0 ;
mode_height = 0 ;
2019-03-02 11:53:14 +01:00
2020-07-27 14:33:21 +02:00
if ( gl - > ctx_driver - > get_video_size )
gl - > ctx_driver - > get_video_size ( gl - > ctx_data ,
& mode_width , & mode_height ) ;
temp_width = mode_width ;
temp_height = mode_height ;
2019-03-02 11:53:14 +01:00
/* Get real known video size, which might have been altered by context. */
if ( temp_width ! = 0 & & temp_height ! = 0 )
2020-01-31 03:47:50 +01:00
video_driver_set_size ( temp_width , temp_height ) ;
2019-03-02 11:53:14 +01:00
video_driver_get_size ( & temp_width , & temp_height ) ;
2019-08-13 12:23:02 +02:00
gl - > video_width = temp_width ;
gl - > video_height = temp_height ;
2019-03-02 11:53:14 +01:00
2021-12-04 15:21:12 +02:00
RARCH_LOG ( " [GLCore]: Using resolution %ux%u. \n " , temp_width , temp_height ) ;
2019-03-02 11:53:14 +01:00
/* Set the viewport to fix recording, since it needs to know
* the viewport sizes before we start running . */
2021-11-23 08:45:54 +01:00
gl3_set_viewport_wrapper ( gl , temp_width , temp_height , false , true ) ;
2019-03-02 11:53:14 +01:00
2020-08-03 00:47:58 +02:00
if ( gl - > ctx_driver - > input_driver )
{
const char * joypad_name = settings - > arrays . input_joypad_driver ;
gl - > ctx_driver - > input_driver (
gl - > ctx_data , joypad_name ,
input , input_data ) ;
}
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
if ( ! gl3_init_filter_chain ( gl ) )
2019-03-02 11:53:14 +01:00
{
RARCH_ERR ( " [GLCore]: Failed to init filter chain. \n " ) ;
goto error ;
}
if ( video - > font_enable )
{
2020-02-16 21:59:03 +01:00
font_driver_init_osd ( gl ,
video ,
false ,
video - > is_threaded ,
FONT_DRIVER_RENDER_OPENGL_CORE_API ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-10 02:34:04 +01:00
gl - > pbo_readback_enable = video_gpu_record
& & recording_state_get_ptr ( ) - > enable ;
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
if ( gl - > pbo_readback_enable & & gl3_init_pbo_readback ( gl ) )
2019-03-02 11:53:14 +01:00
{
RARCH_LOG ( " [GLCore]: Async PBO readback enabled. \n " ) ;
}
if ( ! gl_check_error ( & error_string ) )
{
RARCH_ERR ( " %s \n " , error_string ) ;
free ( error_string ) ;
goto error ;
}
glGenVertexArrays ( 1 , & gl - > vao ) ;
glBindVertexArray ( gl - > vao ) ;
glBindVertexArray ( 0 ) ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
return gl ;
error :
2021-09-28 01:16:53 +02:00
video_context_driver_free ( ) ;
2021-11-23 08:45:54 +01:00
gl3_destroy_resources ( gl ) ;
2020-08-03 15:19:07 +02:00
if ( gl )
free ( gl ) ;
2019-03-02 11:53:14 +01:00
return NULL ;
}
2021-11-23 08:45:54 +01:00
static unsigned gl3_num_miplevels ( unsigned width , unsigned height )
2019-03-02 11:53:14 +01:00
{
unsigned levels = 1 ;
if ( width < height )
width = height ;
while ( width > 1 )
{
levels + + ;
width > > = 1 ;
}
return levels ;
}
2021-11-23 08:45:54 +01:00
static void video_texture_load_gl3 (
2019-03-02 11:53:14 +01:00
const struct texture_image * ti ,
enum texture_filter_type filter_type ,
uintptr_t * idptr )
{
/* Generate the OpenGL texture object */
GLuint id ;
unsigned levels ;
GLenum mag_filter , min_filter ;
glGenTextures ( 1 , & id ) ;
* idptr = id ;
glBindTexture ( GL_TEXTURE_2D , id ) ;
levels = 1 ;
if ( filter_type = = TEXTURE_FILTER_MIPMAP_LINEAR | | filter_type = = TEXTURE_FILTER_MIPMAP_NEAREST )
2021-11-23 08:45:54 +01:00
levels = gl3_num_miplevels ( ti - > width , ti - > height ) ;
2019-03-02 11:53:14 +01:00
glTexStorage2D ( GL_TEXTURE_2D , levels , GL_RGBA8 , ti - > width , ti - > height ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
switch ( filter_type )
{
case TEXTURE_FILTER_LINEAR :
mag_filter = GL_LINEAR ;
min_filter = GL_LINEAR ;
break ;
case TEXTURE_FILTER_NEAREST :
mag_filter = GL_NEAREST ;
min_filter = GL_NEAREST ;
break ;
case TEXTURE_FILTER_MIPMAP_NEAREST :
mag_filter = GL_LINEAR ;
min_filter = GL_LINEAR_MIPMAP_NEAREST ;
break ;
case TEXTURE_FILTER_MIPMAP_LINEAR :
default :
mag_filter = GL_LINEAR ;
min_filter = GL_LINEAR_MIPMAP_LINEAR ;
break ;
}
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , mag_filter ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , min_filter ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 4 ) ;
glPixelStorei ( GL_UNPACK_ROW_LENGTH , 0 ) ;
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 ,
ti - > width , ti - > height , GL_RGBA , GL_UNSIGNED_BYTE , ti - > pixels ) ;
if ( levels > 1 )
glGenerateMipmap ( GL_TEXTURE_2D ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_R , GL_BLUE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_B , GL_RED ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
}
# ifdef HAVE_OVERLAY
2021-11-23 08:45:54 +01:00
static bool gl3_overlay_load ( void * data ,
2019-03-02 11:53:14 +01:00
const void * image_data , unsigned num_images )
{
unsigned i , j ;
uintptr_t id ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
const struct texture_image * images =
( const struct texture_image * ) image_data ;
if ( ! gl )
return false ;
2021-11-23 08:45:54 +01:00
gl3_free_overlay ( gl ) ;
2019-03-02 11:53:14 +01:00
gl - > overlay_tex = ( GLuint * )
calloc ( num_images , sizeof ( * gl - > overlay_tex ) ) ;
if ( ! gl - > overlay_tex )
return false ;
gl - > overlay_vertex_coord = ( GLfloat * )
calloc ( 2 * 4 * num_images , sizeof ( GLfloat ) ) ;
gl - > overlay_tex_coord = ( GLfloat * )
calloc ( 2 * 4 * num_images , sizeof ( GLfloat ) ) ;
gl - > overlay_color_coord = ( GLfloat * )
calloc ( 4 * 4 * num_images , sizeof ( GLfloat ) ) ;
if ( ! gl - > overlay_vertex_coord
| | ! gl - > overlay_tex_coord
| | ! gl - > overlay_color_coord )
return false ;
gl - > overlays = num_images ;
glGenTextures ( num_images , gl - > overlay_tex ) ;
for ( i = 0 ; i < num_images ; i + + )
{
2021-11-23 08:45:54 +01:00
video_texture_load_gl3 ( & images [ i ] , TEXTURE_FILTER_LINEAR , & id ) ;
2019-03-02 11:53:14 +01:00
gl - > overlay_tex [ i ] = id ;
/* Default. Stretch to whole screen. */
2021-11-23 08:45:54 +01:00
gl3_overlay_tex_geom ( gl , i , 0 , 0 , 1 , 1 ) ;
gl3_overlay_vertex_geom ( gl , i , 0 , 0 , 1 , 1 ) ;
2019-03-02 11:53:14 +01:00
for ( j = 0 ; j < 16 ; j + + )
gl - > overlay_color_coord [ 16 * i + j ] = 1.0f ;
}
return true ;
}
2021-11-23 08:45:54 +01:00
static void gl3_overlay_enable ( void * data , bool state )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
gl - > overlay_enable = state ;
2019-08-28 21:36:58 +02:00
if ( gl - > fullscreen & & gl - > ctx_driver - > show_mouse )
gl - > ctx_driver - > show_mouse ( gl - > ctx_data , state ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static void gl3_overlay_full_screen ( void * data , bool enable )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( gl )
gl - > overlay_full_screen = enable ;
}
2021-11-23 08:45:54 +01:00
static void gl3_overlay_set_alpha ( void * data , unsigned image , float mod )
2019-03-02 11:53:14 +01:00
{
GLfloat * color = NULL ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
color = ( GLfloat * ) & gl - > overlay_color_coord [ image * 16 ] ;
color [ 0 + 3 ] = mod ;
color [ 4 + 3 ] = mod ;
color [ 8 + 3 ] = mod ;
color [ 12 + 3 ] = mod ;
}
2021-11-23 08:45:54 +01:00
static const video_overlay_interface_t gl3_overlay_interface = {
gl3_overlay_enable ,
gl3_overlay_load ,
gl3_overlay_tex_geom ,
gl3_overlay_vertex_geom ,
gl3_overlay_full_screen ,
gl3_overlay_set_alpha ,
2019-03-02 11:53:14 +01:00
} ;
2021-11-23 08:45:54 +01:00
static void gl3_get_overlay_interface ( void * data ,
2019-03-02 11:53:14 +01:00
const video_overlay_interface_t * * iface )
{
( void ) data ;
2021-11-23 08:45:54 +01:00
* iface = & gl3_overlay_interface ;
2019-03-02 11:53:14 +01:00
}
# endif
2021-11-23 08:45:54 +01:00
static void gl3_free ( void * data )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
font_driver_free_osd ( ) ;
2021-11-23 08:45:54 +01:00
gl3_destroy_resources ( gl ) ;
2020-08-03 00:35:07 +02:00
if ( gl - > ctx_driver & & gl - > ctx_driver - > destroy )
gl - > ctx_driver - > destroy ( gl - > ctx_data ) ;
2019-03-09 20:53:29 +01:00
video_context_driver_free ( ) ;
2020-08-03 15:19:07 +02:00
free ( gl ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static bool gl3_alive ( void * data )
2019-03-02 11:53:14 +01:00
{
bool ret = false ;
bool quit = false ;
bool resize = false ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-08-13 12:23:02 +02:00
unsigned temp_width = gl - > video_width ;
unsigned temp_height = gl - > video_height ;
2019-03-02 11:53:14 +01:00
gl - > ctx_driver - > check_window ( gl - > ctx_data ,
2020-03-06 20:29:15 +01:00
& quit , & resize , & temp_width , & temp_height ) ;
2019-03-02 11:53:14 +01:00
2021-12-24 13:34:30 +00:00
# ifdef __WINRT__
if ( is_running_on_xbox ( ) )
{
//we can set it to 1920x1080 as xbox uwp windowsize is guaranteed to be 1920x1080 and currently there is now way to set angle to use a variable resolution swapchain so regardless of the size the window is always 1080p
temp_width = 1920 ;
temp_height = 1080 ;
}
# endif
2019-03-02 11:53:14 +01:00
if ( quit )
gl - > quitting = true ;
else if ( resize )
gl - > should_resize = true ;
ret = ! gl - > quitting ;
if ( temp_width ! = 0 & & temp_height ! = 0 )
2019-08-13 12:23:02 +02:00
{
2020-01-31 03:47:50 +01:00
video_driver_set_size ( temp_width , temp_height ) ;
2019-08-13 12:23:02 +02:00
gl - > video_width = temp_width ;
gl - > video_height = temp_height ;
}
2019-03-02 11:53:14 +01:00
return ret ;
}
2021-11-23 08:45:54 +01:00
static void gl3_set_nonblock_state ( void * data , bool state ,
2020-02-16 22:26:07 +01:00
bool adaptive_vsync_enabled ,
unsigned swap_interval )
2019-03-02 11:53:14 +01:00
{
int interval = 0 ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
if ( ! state )
2020-02-16 22:26:07 +01:00
interval = swap_interval ;
2019-03-02 11:53:14 +01:00
2019-08-28 21:12:51 +02:00
if ( gl - > ctx_driver - > swap_interval )
{
if ( adaptive_vsync_enabled & & interval = = 1 )
interval = - 1 ;
gl - > ctx_driver - > swap_interval ( gl - > ctx_data , interval ) ;
}
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static bool gl3_suppress_screensaver ( void * data , bool enable )
2019-03-02 11:53:14 +01:00
{
2019-08-28 22:52:41 +02:00
bool enabled = enable ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-08-28 22:52:41 +02:00
if ( gl - > ctx_data & & gl - > ctx_driver - > suppress_screensaver )
return gl - > ctx_driver - > suppress_screensaver ( gl - > ctx_data , enabled ) ;
return false ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static bool gl3_set_shader ( void * data ,
2019-03-02 11:53:14 +01:00
enum rarch_shader_type type , const char * path )
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return false ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
if ( gl - > filter_chain )
2021-11-23 08:45:54 +01:00
gl3_filter_chain_free ( gl - > filter_chain ) ;
2019-03-02 11:53:14 +01:00
gl - > filter_chain = NULL ;
2019-06-04 21:52:58 +02:00
if ( ! string_is_empty ( path ) & & type ! = RARCH_SHADER_SLANG )
{
RARCH_WARN ( " [GLCore]: Only Slang shaders are supported. Falling back to stock. \n " ) ;
path = NULL ;
}
if ( string_is_empty ( path ) )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_init_default_filter_chain ( gl ) ;
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
return true ;
}
2021-11-23 08:45:54 +01:00
if ( ! gl3_init_filter_chain_preset ( gl , path ) )
2019-03-02 11:53:14 +01:00
{
RARCH_ERR ( " [GLCore]: Failed to create filter chain: \" %s \" . Falling back to stock. \n " , path ) ;
2021-11-23 08:45:54 +01:00
gl3_init_default_filter_chain ( gl ) ;
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
return false ;
}
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
return true ;
}
2021-11-23 08:45:54 +01:00
static void gl3_set_rotation ( void * data , unsigned rotation )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
2019-06-06 20:11:08 +02:00
gl - > rotation = video_driver_is_hw_context ( ) & & gl - > hw_render_bottom_left ? 90 * rotation : 270 * rotation ;
2021-11-23 08:45:54 +01:00
gl3_set_projection ( gl , & gl3_default_ortho , true ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static void gl3_viewport_info ( void * data , struct video_viewport * vp )
2019-03-02 11:53:14 +01:00
{
unsigned top_y , top_dist ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-08-13 12:23:02 +02:00
unsigned width = gl - > video_width ;
unsigned height = gl - > video_height ;
2019-03-02 11:53:14 +01:00
* vp = gl - > vp ;
vp - > full_width = width ;
vp - > full_height = height ;
/* Adjust as GL viewport is bottom-up. */
top_y = vp - > y + vp - > height ;
top_dist = height - top_y ;
vp - > y = top_dist ;
}
2021-11-23 08:45:54 +01:00
static bool gl3_read_viewport ( void * data , uint8_t * buffer , bool is_idle )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
unsigned num_pixels = 0 ;
if ( ! gl )
return false ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
num_pixels = gl - > vp . width * gl - > vp . height ;
if ( gl - > pbo_readback_enable )
{
const void * ptr = NULL ;
struct scaler_ctx * ctx = & gl - > pbo_readback_scaler ;
/* Don't readback if we're in menu mode.
* We haven ' t buffered up enough frames yet , come back later . */
if ( ! gl - > pbo_readback_valid [ gl - > pbo_readback_index ] )
goto error ;
gl - > pbo_readback_valid [ gl - > pbo_readback_index ] = false ;
glBindBuffer ( GL_PIXEL_PACK_BUFFER , gl - > pbo_readback [ gl - > pbo_readback_index ] ) ;
ptr = glMapBufferRange ( GL_PIXEL_PACK_BUFFER , 0 , num_pixels * sizeof ( uint32_t ) , GL_MAP_READ_BIT ) ;
scaler_ctx_scale_direct ( ctx , buffer , ptr ) ;
glUnmapBuffer ( GL_PIXEL_PACK_BUFFER ) ;
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
}
else
{
/* Use slow synchronous readbacks. Use this with plain screenshots
as we don ' t really care about performance in this case . */
/* GLES only guarantees GL_RGBA/GL_UNSIGNED_BYTE
* readbacks so do just that .
* GLES also doesn ' t support reading back data
* from front buffer , so render a cached frame
* and have gl_frame ( ) do the readback while it ' s
* in the back buffer .
*
* Keep codepath similar for GLES and desktop GL .
*/
gl - > readback_buffer_screenshot = malloc ( num_pixels * sizeof ( uint32_t ) ) ;
if ( ! gl - > readback_buffer_screenshot )
goto error ;
if ( ! is_idle )
video_driver_cached_frame ( ) ;
video_frame_convert_rgba_to_bgr (
( const void * ) gl - > readback_buffer_screenshot ,
buffer ,
num_pixels ) ;
free ( gl - > readback_buffer_screenshot ) ;
gl - > readback_buffer_screenshot = NULL ;
}
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
return true ;
error :
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
return false ;
}
2021-11-23 08:45:54 +01:00
static void gl3_update_cpu_texture ( gl3_t * gl ,
struct gl3_streamed_texture * streamed ,
2019-03-02 11:53:14 +01:00
const void * frame , unsigned width , unsigned height , unsigned pitch )
{
if ( width ! = streamed - > width | | height ! = streamed - > height )
{
if ( streamed - > tex ! = 0 )
glDeleteTextures ( 1 , & streamed - > tex ) ;
glGenTextures ( 1 , & streamed - > tex ) ;
glBindTexture ( GL_TEXTURE_2D , streamed - > tex ) ;
glTexStorage2D ( GL_TEXTURE_2D , 1 , gl - > video_info . rgb32 ? GL_RGBA8 : GL_RGB565 ,
width , height ) ;
streamed - > width = width ;
streamed - > height = height ;
if ( gl - > video_info . rgb32 )
{
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_R , GL_BLUE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_B , GL_RED ) ;
}
}
else
glBindTexture ( GL_TEXTURE_2D , streamed - > tex ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
if ( gl - > video_info . rgb32 )
{
glPixelStorei ( GL_UNPACK_ROW_LENGTH , pitch > > 2 ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 4 ) ;
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 ,
width , height , GL_RGBA , GL_UNSIGNED_BYTE , frame ) ;
}
else
{
glPixelStorei ( GL_UNPACK_ROW_LENGTH , pitch > > 1 ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 2 ) ;
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 ,
width , height , GL_RGB , GL_UNSIGNED_SHORT_5_6_5 , frame ) ;
}
}
2019-05-04 08:26:36 -07:00
# if defined(HAVE_MENU)
2021-11-23 08:45:54 +01:00
static void gl3_draw_menu_texture ( gl3_t * gl ,
2020-03-10 19:32:21 +01:00
unsigned width , unsigned height )
2019-03-02 11:53:14 +01:00
{
2019-03-13 11:21:00 +01:00
const float vbo_data [ ] = {
0.0f , 0.0f , 0.0f , 0.0f , 1.0f , 1.0f , 1.0f , gl - > menu_texture_alpha ,
1.0f , 0.0f , 1.0f , 0.0f , 1.0f , 1.0f , 1.0f , gl - > menu_texture_alpha ,
0.0f , 1.0f , 0.0f , 1.0f , 1.0f , 1.0f , 1.0f , gl - > menu_texture_alpha ,
1.0f , 1.0f , 1.0f , 1.0f , 1.0f , 1.0f , 1.0f , gl - > menu_texture_alpha ,
} ;
2019-03-02 11:53:14 +01:00
glEnable ( GL_BLEND ) ;
glDisable ( GL_CULL_FACE ) ;
glDisable ( GL_DEPTH_TEST ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
glBlendEquation ( GL_FUNC_ADD ) ;
if ( gl - > menu_texture_full_screen )
2020-03-10 19:32:21 +01:00
glViewport ( 0 , 0 , width , height ) ;
2019-09-03 12:50:55 +01:00
else
glViewport ( gl - > vp . x , gl - > vp . y , gl - > vp . width , gl - > vp . height ) ;
2019-03-02 11:53:14 +01:00
glActiveTexture ( GL_TEXTURE0 + 1 ) ;
glBindTexture ( GL_TEXTURE_2D , gl - > menu_texture ) ;
glUseProgram ( gl - > pipelines . alpha_blend ) ;
if ( gl - > pipelines . alpha_blend_loc . flat_ubo_vertex > = 0 )
glUniform4fv ( gl - > pipelines . alpha_blend_loc . flat_ubo_vertex , 4 , gl - > mvp_no_rot_yflip . data ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glEnableVertexAttribArray ( 2 ) ;
2021-11-23 08:45:54 +01:00
gl3_bind_scratch_vbo ( gl , vbo_data , sizeof ( vbo_data ) ) ;
2019-03-02 11:53:14 +01:00
glVertexAttribPointer ( 0 , 2 , GL_FLOAT , GL_FALSE , 8 * sizeof ( float ) , ( void * ) ( uintptr_t ) 0 ) ;
glVertexAttribPointer ( 1 , 2 , GL_FLOAT , GL_FALSE , 8 * sizeof ( float ) , ( void * ) ( uintptr_t ) ( 2 * sizeof ( float ) ) ) ;
glVertexAttribPointer ( 2 , 4 , GL_FLOAT , GL_FALSE , 8 * sizeof ( float ) , ( void * ) ( uintptr_t ) ( 4 * sizeof ( float ) ) ) ;
glDrawArrays ( GL_TRIANGLE_STRIP , 0 , 4 ) ;
glDisableVertexAttribArray ( 0 ) ;
glDisableVertexAttribArray ( 1 ) ;
glDisableVertexAttribArray ( 2 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glDisable ( GL_BLEND ) ;
}
2019-05-04 08:26:36 -07:00
# endif
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
static bool gl3_frame ( void * data , const void * frame ,
2019-08-12 22:04:27 +02:00
unsigned frame_width , unsigned frame_height ,
uint64_t frame_count ,
unsigned pitch , const char * msg ,
video_frame_info_t * video_info )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
struct gl3_filter_chain_texture texture ;
struct gl3_streamed_texture * streamed = NULL ;
gl3_t * gl = ( gl3_t * ) data ;
2020-03-10 19:32:21 +01:00
unsigned width = video_info - > width ;
unsigned height = video_info - > height ;
struct font_params * osd_params = ( struct font_params * )
& video_info - > osd_stat_params ;
const char * stat_text = video_info - > stat_text ;
bool statistics_show = video_info - > statistics_show ;
bool msg_bgcolor_enable = video_info - > msg_bgcolor_enable ;
2020-09-18 11:57:32 -04:00
unsigned black_frame_insertion = video_info - > black_frame_insertion ;
2020-03-10 19:32:21 +01:00
unsigned hard_sync_frames = video_info - > hard_sync_frames ;
bool runloop_is_paused = video_info - > runloop_is_paused ;
bool runloop_is_slowmotion = video_info - > runloop_is_slowmotion ;
bool input_driver_nonblock_state = video_info - > input_driver_nonblock_state ;
2020-08-03 16:33:54 +02:00
# ifdef HAVE_MENU
2020-05-19 16:20:43 +02:00
bool menu_is_alive = video_info - > menu_is_alive ;
2020-08-03 16:33:54 +02:00
# endif
# ifdef HAVE_GFX_WIDGETS
bool widgets_active = video_info - > widgets_active ;
# endif
bool hard_sync = video_info - > hard_sync ;
2021-12-26 05:56:44 +02:00
bool overlay_behind_menu = video_info - > overlay_behind_menu ;
2020-05-19 16:20:43 +02:00
2019-03-02 11:53:14 +01:00
if ( ! gl )
return false ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
glBindVertexArray ( gl - > vao ) ;
if ( frame )
gl - > textures_index = ( gl - > textures_index + 1 ) & ( GL_CORE_NUM_TEXTURES - 1 ) ;
streamed = & gl - > textures [ gl - > textures_index ] ;
if ( frame )
{
if ( ! gl - > hw_render_enable )
2021-11-23 08:45:54 +01:00
gl3_update_cpu_texture ( gl , streamed , frame , frame_width , frame_height , pitch ) ;
2019-03-02 11:53:14 +01:00
else
{
streamed - > width = frame_width ;
streamed - > height = frame_height ;
}
}
if ( gl - > should_resize )
{
2020-07-09 05:36:29 +02:00
if ( gl - > ctx_driver - > set_resize )
2020-07-09 07:46:40 +02:00
gl - > ctx_driver - > set_resize ( gl - > ctx_data ,
2020-07-09 05:36:29 +02:00
width , height ) ;
2019-03-02 11:53:14 +01:00
gl - > should_resize = false ;
}
2021-11-23 08:45:54 +01:00
gl3_set_viewport ( gl , width , height , false , true ) ;
2019-03-02 11:53:14 +01:00
2019-08-12 20:55:46 +02:00
texture . image = 0 ;
texture . width = streamed - > width ;
texture . height = streamed - > height ;
texture . padded_width = 0 ;
texture . padded_height = 0 ;
texture . format = 0 ;
2019-03-02 11:53:14 +01:00
if ( gl - > hw_render_enable )
{
2019-08-12 20:55:46 +02:00
texture . image = gl - > hw_render_texture ;
texture . format = GL_RGBA8 ;
texture . padded_width = gl - > hw_render_max_width ;
2019-03-02 11:53:14 +01:00
texture . padded_height = gl - > hw_render_max_height ;
if ( texture . width = = 0 )
texture . width = 1 ;
if ( texture . height = = 0 )
texture . height = 1 ;
}
else
{
2019-08-12 20:55:46 +02:00
texture . image = streamed - > tex ;
texture . format = gl - > video_info . rgb32 ? GL_RGBA8 : GL_RGB565 ;
texture . padded_width = streamed - > width ;
2019-03-02 11:53:14 +01:00
texture . padded_height = streamed - > height ;
}
2021-11-23 08:45:54 +01:00
gl3_filter_chain_set_frame_count ( gl - > filter_chain , frame_count ) ;
2020-07-01 21:04:05 +02:00
# ifdef HAVE_REWIND
2021-11-23 08:45:54 +01:00
gl3_filter_chain_set_frame_direction ( gl - > filter_chain , state_manager_frame_is_reversed ( ) ? - 1 : 1 ) ;
2020-07-01 21:04:05 +02:00
# else
2021-11-23 08:45:54 +01:00
gl3_filter_chain_set_frame_direction ( gl - > filter_chain , 1 ) ;
2020-07-01 21:04:05 +02:00
# endif
2021-11-23 08:45:54 +01:00
gl3_filter_chain_set_input_texture ( gl - > filter_chain , & texture ) ;
gl3_filter_chain_build_offscreen_passes ( gl - > filter_chain , & gl - > filter_chain_vp ) ;
2019-03-02 11:53:14 +01:00
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
glClearColor ( 0.0f , 0.0f , 0.0f , 0.0f ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
2021-11-23 08:45:54 +01:00
gl3_filter_chain_build_viewport_pass ( gl - > filter_chain , & gl - > filter_chain_vp ,
2019-03-02 11:53:14 +01:00
gl - > hw_render_bottom_left ? gl - > mvp . data : gl - > mvp_yflip . data ) ;
2021-11-23 08:45:54 +01:00
gl3_filter_chain_end_frame ( gl - > filter_chain ) ;
2019-03-02 11:53:14 +01:00
2021-12-26 05:56:44 +02:00
# ifdef HAVE_OVERLAY
if ( gl - > overlay_enable & & overlay_behind_menu )
gl3_render_overlay ( gl , width , height ) ;
# endif
2019-03-02 11:53:14 +01:00
# if defined(HAVE_MENU)
if ( gl - > menu_texture_enable )
{
2020-05-19 16:20:43 +02:00
menu_driver_frame ( menu_is_alive , video_info ) ;
2019-03-02 11:53:14 +01:00
if ( gl - > menu_texture_enable & & gl - > menu_texture )
2021-11-23 08:45:54 +01:00
gl3_draw_menu_texture ( gl , width , height ) ;
2019-03-02 11:53:14 +01:00
}
2020-03-10 19:32:21 +01:00
else if ( statistics_show )
2019-03-02 11:53:14 +01:00
{
if ( osd_params )
2020-03-10 19:32:21 +01:00
font_driver_render_msg ( gl , stat_text ,
( const struct font_params * ) osd_params , NULL ) ;
2019-03-02 11:53:14 +01:00
}
# endif
# ifdef HAVE_OVERLAY
2021-12-26 05:56:44 +02:00
if ( gl - > overlay_enable & & ! overlay_behind_menu )
2021-11-23 08:45:54 +01:00
gl3_render_overlay ( gl , width , height ) ;
2019-03-02 11:53:14 +01:00
# endif
2020-02-17 21:28:42 +01:00
# ifdef HAVE_GFX_WIDGETS
2020-08-03 16:33:54 +02:00
if ( widgets_active )
2020-06-08 05:45:15 +02:00
gfx_widgets_frame ( video_info ) ;
2019-08-12 22:04:27 +02:00
# endif
2019-03-02 11:53:14 +01:00
if ( ! string_is_empty ( msg ) )
{
2019-03-13 11:21:00 +01:00
#if 0
2020-03-10 19:32:21 +01:00
if ( msg_bgcolor_enable )
2021-11-23 08:45:54 +01:00
gl3_render_osd_background ( gl , video_info , msg ) ;
2019-03-13 11:21:00 +01:00
# endif
2020-03-10 03:24:59 +01:00
font_driver_render_msg ( gl , msg , NULL , NULL ) ;
2019-03-02 11:53:14 +01:00
}
2020-04-29 13:50:21 +02:00
if ( gl - > ctx_driver - > update_window_title )
2020-07-09 07:46:40 +02:00
gl - > ctx_driver - > update_window_title ( gl - > ctx_data ) ;
2019-11-19 22:48:35 +01:00
2019-03-02 11:53:14 +01:00
if ( gl - > readback_buffer_screenshot )
{
/* For screenshots, just do the regular slow readback. */
2021-11-23 08:45:54 +01:00
gl3_slow_readback ( gl , gl - > readback_buffer_screenshot ) ;
2019-03-02 11:53:14 +01:00
}
else if ( gl - > pbo_readback_enable )
{
# ifdef HAVE_MENU
/* Don't readback if we're in menu mode. */
if ( ! gl - > menu_texture_enable )
# endif
2021-11-23 08:45:54 +01:00
gl3_pbo_async_readback ( gl ) ;
2019-03-02 11:53:14 +01:00
}
2020-09-18 11:57:32 -04:00
if ( gl - > ctx_driver - > swap_buffers )
gl - > ctx_driver - > swap_buffers ( gl - > ctx_data ) ;
/* Emscripten has to do black frame insertion in its main loop */
# ifndef EMSCRIPTEN
2019-03-02 11:53:14 +01:00
/* Disable BFI during fast forward, slow-motion,
* and pause to prevent flicker . */
2020-09-18 11:57:32 -04:00
if (
2020-03-10 19:32:21 +01:00
black_frame_insertion
& & ! input_driver_nonblock_state
& & ! runloop_is_slowmotion
2020-09-18 11:57:32 -04:00
& & ! runloop_is_paused
& & ! gl - > menu_texture_enable )
{
2020-09-19 13:28:21 -04:00
unsigned n ;
for ( n = 0 ; n < black_frame_insertion ; + + n )
2020-09-18 11:57:32 -04:00
{
glClearColor ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
if ( gl - > ctx_driver - > swap_buffers )
gl - > ctx_driver - > swap_buffers ( gl - > ctx_data ) ;
}
}
# endif
2019-03-02 11:53:14 +01:00
2021-11-20 03:26:54 +01:00
if ( hard_sync
& & ! input_driver_nonblock_state
)
2021-11-23 08:45:54 +01:00
gl3_fence_iterate ( gl , hard_sync_frames ) ;
2019-03-02 11:53:14 +01:00
glBindVertexArray ( 0 ) ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
return true ;
}
2021-11-23 08:45:54 +01:00
static uint32_t gl3_get_flags ( void * data )
2019-03-02 11:53:14 +01:00
{
uint32_t flags = 0 ;
2019-03-07 17:25:16 +01:00
BIT32_SET ( flags , GFX_CTX_FLAGS_HARD_SYNC ) ;
2019-03-02 11:53:14 +01:00
BIT32_SET ( flags , GFX_CTX_FLAGS_BLACK_FRAME_INSERTION ) ;
BIT32_SET ( flags , GFX_CTX_FLAGS_MENU_FRAME_FILTERING ) ;
2019-05-05 13:46:26 +02:00
BIT32_SET ( flags , GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED ) ;
2021-12-26 05:56:44 +02:00
BIT32_SET ( flags , GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED ) ;
2019-03-02 11:53:14 +01:00
return flags ;
}
2021-11-23 08:45:54 +01:00
static float gl3_get_refresh_rate ( void * data )
2019-03-02 11:53:14 +01:00
{
float refresh_rate ;
if ( video_context_driver_get_refresh_rate ( & refresh_rate ) )
return refresh_rate ;
return 0.0f ;
}
2021-11-23 08:45:54 +01:00
static void gl3_set_aspect_ratio ( void * data , unsigned aspect_ratio_idx )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
2019-08-12 12:52:40 +02:00
gl - > keep_aspect = true ;
2019-03-02 11:53:14 +01:00
gl - > should_resize = true ;
}
2021-11-23 08:45:54 +01:00
static void gl3_apply_state_changes ( void * data )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( gl )
gl - > should_resize = true ;
}
2021-11-23 08:45:54 +01:00
static struct video_shader * gl3_get_current_shader ( void * data )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl | | ! gl - > filter_chain )
return NULL ;
2021-11-23 08:45:54 +01:00
return gl3_filter_chain_get_preset ( gl - > filter_chain ) ;
2019-03-02 11:53:14 +01:00
}
# ifdef HAVE_THREADS
2021-11-23 08:45:54 +01:00
static int video_texture_load_wrap_gl3_mipmap ( void * data )
2019-03-02 11:53:14 +01:00
{
uintptr_t id = 0 ;
if ( ! data )
return 0 ;
2021-11-23 08:45:54 +01:00
video_texture_load_gl3 ( ( struct texture_image * ) data ,
2019-03-02 11:53:14 +01:00
TEXTURE_FILTER_MIPMAP_LINEAR , & id ) ;
return ( int ) id ;
}
2021-11-23 08:45:54 +01:00
static int video_texture_load_wrap_gl3 ( void * data )
2019-03-02 11:53:14 +01:00
{
uintptr_t id = 0 ;
if ( ! data )
return 0 ;
2021-11-23 08:45:54 +01:00
video_texture_load_gl3 ( ( struct texture_image * ) data ,
2019-03-02 11:53:14 +01:00
TEXTURE_FILTER_LINEAR , & id ) ;
return ( int ) id ;
}
# endif
2021-11-23 08:45:54 +01:00
static uintptr_t gl3_load_texture ( void * video_data , void * data ,
2020-02-18 04:18:31 +01:00
bool threaded , enum texture_filter_type filter_type )
2019-03-02 11:53:14 +01:00
{
uintptr_t id = 0 ;
# ifdef HAVE_THREADS
if ( threaded )
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) video_data ;
custom_command_method_t func = video_texture_load_wrap_gl3 ;
2019-03-02 11:53:14 +01:00
2020-07-27 10:26:13 +02:00
if ( gl - > ctx_driver - > make_current )
gl - > ctx_driver - > make_current ( false ) ;
2019-03-02 11:53:14 +01:00
switch ( filter_type )
{
case TEXTURE_FILTER_MIPMAP_LINEAR :
case TEXTURE_FILTER_MIPMAP_NEAREST :
2021-11-23 08:45:54 +01:00
func = video_texture_load_wrap_gl3_mipmap ;
2019-03-02 11:53:14 +01:00
break ;
default :
break ;
}
return video_thread_texture_load ( data , func ) ;
}
# endif
2021-11-23 08:45:54 +01:00
video_texture_load_gl3 ( ( struct texture_image * ) data , filter_type , & id ) ;
2019-03-02 11:53:14 +01:00
return id ;
}
2021-11-23 08:45:54 +01:00
static void gl3_unload_texture ( void * data , bool threaded ,
2020-07-27 10:15:28 +02:00
uintptr_t id )
2019-03-02 11:53:14 +01:00
{
GLuint glid ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! id )
return ;
2020-07-27 10:26:13 +02:00
# ifdef HAVE_THREADS
if ( threaded )
{
if ( gl - > ctx_driver - > make_current )
gl - > ctx_driver - > make_current ( false ) ;
}
# endif
2019-03-02 11:53:14 +01:00
glid = ( GLuint ) id ;
glDeleteTextures ( 1 , & glid ) ;
}
2021-11-23 08:45:54 +01:00
static void gl3_set_video_mode ( void * data , unsigned width , unsigned height ,
2020-07-27 10:26:13 +02:00
bool fullscreen )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2020-07-27 11:08:34 +02:00
if ( gl - > ctx_driver - > set_video_mode )
gl - > ctx_driver - > set_video_mode ( gl - > ctx_data ,
width , height , fullscreen ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static void gl3_show_mouse ( void * data , bool state )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-08-28 21:36:58 +02:00
if ( gl & & gl - > ctx_driver - > show_mouse )
gl - > ctx_driver - > show_mouse ( gl - > ctx_data , state ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static void gl3_set_texture_frame ( void * data ,
2019-03-02 11:53:14 +01:00
const void * frame , bool rgb32 , unsigned width , unsigned height ,
float alpha )
{
settings_t * settings = config_get_ptr ( ) ;
2020-02-18 14:51:40 +01:00
GLenum menu_filter = settings - > bools . menu_linear_filter
? GL_LINEAR : GL_NEAREST ;
2019-03-02 11:53:14 +01:00
unsigned base_size = rgb32 ? sizeof ( uint32_t ) : sizeof ( uint16_t ) ;
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , false ) ;
2019-03-02 11:53:14 +01:00
if ( gl - > menu_texture )
glDeleteTextures ( 1 , & gl - > menu_texture ) ;
glGenTextures ( 1 , & gl - > menu_texture ) ;
glBindTexture ( GL_TEXTURE_2D , gl - > menu_texture ) ;
2020-02-18 14:51:40 +01:00
glTexStorage2D ( GL_TEXTURE_2D , 1 , rgb32
? GL_RGBA8 : GL_RGBA4 , width , height ) ;
2019-03-02 11:53:14 +01:00
glPixelStorei ( GL_UNPACK_ALIGNMENT , base_size ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
glPixelStorei ( GL_UNPACK_ROW_LENGTH , 0 ) ;
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 ,
2020-02-18 14:51:40 +01:00
width , height , GL_RGBA , rgb32
? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT_4_4_4_4 , frame ) ;
2019-03-02 11:53:14 +01:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , menu_filter ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , menu_filter ) ;
if ( rgb32 )
{
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_R , GL_BLUE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_B , GL_RED ) ;
}
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
gl - > menu_texture_alpha = alpha ;
2021-11-23 08:45:54 +01:00
gl3_context_bind_hw_render ( gl , true ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static void gl3_set_texture_enable ( void * data , bool state , bool full_screen )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl )
return ;
gl - > menu_texture_enable = state ;
gl - > menu_texture_full_screen = full_screen ;
}
2021-11-23 08:45:54 +01:00
static void gl3_get_video_output_size ( void * data ,
2021-09-07 20:25:22 +02:00
unsigned * width , unsigned * height , char * desc , size_t desc_len )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2020-07-27 09:38:20 +02:00
if ( ! gl | | ! gl - > ctx_driver | | ! gl - > ctx_driver - > get_video_output_size )
return ;
gl - > ctx_driver - > get_video_output_size (
gl - > ctx_data ,
2021-09-07 20:25:22 +02:00
width , height , desc , desc_len ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static void gl3_get_video_output_prev ( void * data )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2020-07-27 09:38:20 +02:00
if ( ! gl | | ! gl - > ctx_driver | | ! gl - > ctx_driver - > get_video_output_prev )
return ;
gl - > ctx_driver - > get_video_output_prev ( gl - > ctx_data ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static void gl3_get_video_output_next ( void * data )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2020-07-27 09:38:20 +02:00
if ( ! gl | | ! gl - > ctx_driver | | ! gl - > ctx_driver - > get_video_output_next )
return ;
gl - > ctx_driver - > get_video_output_next ( gl - > ctx_data ) ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static uintptr_t gl3_get_current_framebuffer ( void * data )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-03-02 11:53:14 +01:00
if ( ! gl | | ! gl - > hw_render_enable )
return 0 ;
return gl - > hw_render_fbo ;
}
2021-11-23 08:45:54 +01:00
static retro_proc_address_t gl3_get_proc_address (
2019-08-28 21:26:43 +02:00
void * data , const char * sym )
2019-03-02 11:53:14 +01:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2019-08-28 21:26:43 +02:00
if ( gl & & gl - > ctx_driver - > get_proc_address )
return gl - > ctx_driver - > get_proc_address ( sym ) ;
return NULL ;
2019-03-02 11:53:14 +01:00
}
2021-11-23 08:45:54 +01:00
static const video_poke_interface_t gl3_poke_interface = {
gl3_get_flags ,
gl3_load_texture ,
gl3_unload_texture ,
gl3_set_video_mode ,
gl3_get_refresh_rate , /* get_refresh_rate */
2019-03-02 11:53:14 +01:00
NULL ,
2021-11-23 08:45:54 +01:00
gl3_get_video_output_size ,
gl3_get_video_output_prev ,
gl3_get_video_output_next ,
gl3_get_current_framebuffer ,
gl3_get_proc_address ,
gl3_set_aspect_ratio ,
gl3_apply_state_changes ,
gl3_set_texture_frame ,
gl3_set_texture_enable ,
2020-01-04 16:24:14 +01:00
font_driver_render_msg ,
2021-11-23 08:45:54 +01:00
gl3_show_mouse ,
2019-03-02 11:53:14 +01:00
NULL , /* grab_mouse_toggle */
2021-11-23 08:45:54 +01:00
gl3_get_current_shader ,
2019-03-02 11:53:14 +01:00
NULL ,
NULL ,
2021-09-03 06:15:25 +02:00
NULL , /* set_hdr_max_nits */
NULL , /* set_hdr_paper_white_nits */
NULL , /* set_hdr_contrast */
NULL /* set_hdr_expand_gamut */
2019-03-02 11:53:14 +01:00
} ;
2021-11-23 08:45:54 +01:00
static void gl3_get_poke_interface ( void * data ,
2019-03-02 11:53:14 +01:00
const video_poke_interface_t * * iface )
{
( void ) data ;
2021-11-23 08:45:54 +01:00
* iface = & gl3_poke_interface ;
2019-03-02 11:53:14 +01:00
}
2020-02-17 21:28:42 +01:00
# ifdef HAVE_GFX_WIDGETS
2021-11-23 08:45:54 +01:00
static bool gl3_gfx_widgets_enabled ( void * data )
2019-03-11 14:57:25 +01:00
{
( void ) data ;
return true ;
}
# endif
2021-11-23 08:45:54 +01:00
static unsigned gl3_wrap_type_to_enum ( enum gfx_wrap_type type )
2019-03-14 20:03:41 +01:00
{
switch ( type )
{
case RARCH_WRAP_BORDER :
2019-04-08 21:17:13 +02:00
# ifdef HAVE_OPENGLES3
/* GLES does not support CLAMP_TO_BORDER until GLES 3.2.
* It is a deprecated feature in general . */
return GL_CLAMP_TO_EDGE ;
# else
2019-03-14 20:03:41 +01:00
return GL_CLAMP_TO_BORDER ;
2019-04-08 21:17:13 +02:00
# endif
2019-03-14 20:03:41 +01:00
case RARCH_WRAP_EDGE :
return GL_CLAMP_TO_EDGE ;
case RARCH_WRAP_REPEAT :
return GL_REPEAT ;
case RARCH_WRAP_MIRRORED_REPEAT :
return GL_MIRRORED_REPEAT ;
default :
2019-04-08 21:17:13 +02:00
break ;
2019-03-14 20:03:41 +01:00
}
return 0 ;
}
2021-11-23 08:45:54 +01:00
static bool gl3_has_windowed ( void * data )
2020-07-27 13:39:02 +02:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2020-07-27 13:39:02 +02:00
if ( gl & & gl - > ctx_driver )
return gl - > ctx_driver - > has_windowed ;
return false ;
}
2021-11-23 08:45:54 +01:00
static bool gl3_focus ( void * data )
2020-07-27 13:46:55 +02:00
{
2021-11-23 08:45:54 +01:00
gl3_t * gl = ( gl3_t * ) data ;
2020-07-27 13:46:55 +02:00
if ( gl & & gl - > ctx_driver & & gl - > ctx_driver - > has_focus )
return gl - > ctx_driver - > has_focus ( gl - > ctx_data ) ;
return true ;
}
2021-11-23 08:45:54 +01:00
video_driver_t video_gl3 = {
gl3_init ,
gl3_frame ,
gl3_set_nonblock_state ,
gl3_alive ,
gl3_focus ,
gl3_suppress_screensaver ,
gl3_has_windowed ,
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
gl3_set_shader ,
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
gl3_free ,
2019-03-02 11:53:14 +01:00
" glcore " ,
2021-11-23 08:45:54 +01:00
gl3_set_viewport_wrapper ,
gl3_set_rotation ,
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
gl3_viewport_info ,
2019-03-02 11:53:14 +01:00
2021-11-23 08:45:54 +01:00
gl3_read_viewport ,
2019-03-02 11:53:14 +01:00
# if defined(READ_RAW_GL_FRAME_TEST)
2021-11-23 08:45:54 +01:00
gl3_read_frame_raw ,
2019-03-02 11:53:14 +01:00
# else
NULL ,
# endif
# ifdef HAVE_OVERLAY
2021-11-23 08:45:54 +01:00
gl3_get_overlay_interface ,
2019-04-01 04:00:58 +01:00
# endif
# ifdef HAVE_VIDEO_LAYOUT
NULL ,
2019-03-02 11:53:14 +01:00
# endif
2021-11-23 08:45:54 +01:00
gl3_get_poke_interface ,
gl3_wrap_type_to_enum ,
2020-02-17 21:28:42 +01:00
# ifdef HAVE_GFX_WIDGETS
2021-11-23 08:45:54 +01:00
gl3_gfx_widgets_enabled
2019-03-02 11:53:14 +01:00
# endif
} ;