2015-05-24 06:55:12 +02:00
// Copyright 2008 Dolphin Emulator Project
2015-05-18 01:08:10 +02:00
// Licensed under GPLv2+
2013-04-17 23:29:41 -04:00
// Refer to the license.txt file included.
2009-02-23 07:23:34 +00:00
2014-08-16 23:00:57 -04:00
#include <algorithm>
2014-02-17 05:18:15 -05:00
#include <cinttypes>
2009-02-23 07:23:34 +00:00
#include <cmath>
2009-03-28 21:07:16 +00:00
#include <cstdio>
2015-05-08 09:28:29 -04:00
#include <memory>
2014-03-12 15:33:41 -04:00
#include <string>
2014-02-17 05:18:15 -05:00
#include <vector>
#include "Common/Atomic.h"
#include "Common/CommonPaths.h"
#include "Common/FileUtil.h"
2015-05-07 00:37:58 +02:00
#include "Common/MathUtil.h"
2014-02-17 05:18:15 -05:00
#include "Common/StringUtil.h"
#include "Common/Thread.h"
2014-02-19 12:14:09 +01:00
#include "Common/Timer.h"
2015-09-19 04:40:00 +12:00
#include "Common/GL/GLInterfaceBase.h"
#include "Common/GL/GLUtil.h"
2014-12-28 21:46:00 +01:00
#include "Common/Logging/LogManager.h"
2014-02-17 05:18:15 -05:00
#include "Core/ConfigManager.h"
#include "Core/Core.h"
2014-11-13 23:26:49 +01:00
#include "VideoBackends/OGL/BoundingBox.h"
2014-02-17 05:18:15 -05:00
#include "VideoBackends/OGL/FramebufferManager.h"
#include "VideoBackends/OGL/main.h"
#include "VideoBackends/OGL/PostProcessing.h"
#include "VideoBackends/OGL/ProgramShaderCache.h"
#include "VideoBackends/OGL/RasterFont.h"
#include "VideoBackends/OGL/Render.h"
#include "VideoBackends/OGL/SamplerCache.h"
#include "VideoBackends/OGL/StreamBuffer.h"
#include "VideoBackends/OGL/TextureCache.h"
#include "VideoBackends/OGL/TextureConverter.h"
#include "VideoBackends/OGL/VertexManager.h"
#include "VideoCommon/BPFunctions.h"
#include "VideoCommon/BPStructs.h"
#include "VideoCommon/DriverDetails.h"
#include "VideoCommon/Fifo.h"
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PixelEngine.h"
2015-01-03 06:06:56 +13:00
#include "VideoCommon/PixelShaderManager.h"
2014-02-17 05:18:15 -05:00
#include "VideoCommon/Statistics.h"
#include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VertexShaderGen.h"
#include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/VideoConfig.h"
2009-02-23 07:23:34 +00:00
2015-01-26 02:35:29 +01:00
#if defined _WIN32 || defined HAVE_LIBAV
#include "VideoCommon/AVIDump.h"
#endif
2011-01-29 20:16:51 +00:00
void VideoConfig :: UpdateProjectionHack ()
{
2011-01-29 21:13:56 +00:00
:: UpdateProjectionHack ( g_Config . iPhackvalue , g_Config . sPhackvalue );
2011-01-29 20:16:51 +00:00
}
2014-07-08 15:58:25 +02:00
static int OSDInternalW , OSDInternalH ;
2011-01-29 20:16:51 +00:00
namespace OGL
{
2013-03-25 15:45:10 +01:00
2014-08-15 14:09:53 -04:00
enum MultisampleMode
{
2013-03-25 15:45:10 +01:00
MULTISAMPLE_OFF ,
MULTISAMPLE_2X ,
MULTISAMPLE_4X ,
MULTISAMPLE_8X ,
};
2013-03-25 15:14:24 +01:00
VideoConfig g_ogl_config ;
2011-01-29 20:16:51 +00:00
// Declarations and definitions
// ----------------------------
2012-12-07 23:34:38 +01:00
static GLuint s_ShowEFBCopyRegions_VBO = 0 ;
2012-12-09 22:49:58 +01:00
static GLuint s_ShowEFBCopyRegions_VAO = 0 ;
2013-02-13 13:12:19 +01:00
static SHADER s_ShowEFBCopyRegions ;
2011-01-29 20:16:51 +00:00
2014-03-09 21:14:26 +01:00
static RasterFont * s_pfont = nullptr ;
2009-02-23 07:23:34 +00:00
2009-03-08 19:19:51 +00:00
// 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA.
static int s_MSAASamples = 1 ;
2014-12-28 18:35:23 +01:00
static int s_last_multisample_mode = 0 ;
static bool s_last_stereo_mode = false ;
2014-12-28 18:26:25 +01:00
static bool s_last_xfb_mode = false ;
2014-10-29 15:42:37 +01:00
2009-03-08 19:19:51 +00:00
static u32 s_blendMode ;
2009-02-23 07:23:34 +00:00
2013-03-30 22:17:39 +01:00
static bool s_vsync ;
2011-08-22 06:15:02 +02:00
// EFB cache related
2012-12-07 23:34:38 +01:00
static const u32 EFB_CACHE_RECT_SIZE = 64 ; // Cache 64x64 blocks.
static const u32 EFB_CACHE_WIDTH = ( EFB_WIDTH + EFB_CACHE_RECT_SIZE - 1 ) / EFB_CACHE_RECT_SIZE ; // round up
static const u32 EFB_CACHE_HEIGHT = ( EFB_HEIGHT + EFB_CACHE_RECT_SIZE - 1 ) / EFB_CACHE_RECT_SIZE ;
2011-08-22 06:15:02 +02:00
static bool s_efbCacheValid [ 2 ][ EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT ];
2014-06-05 10:12:05 +02:00
static bool s_efbCacheIsCleared = false ;
2011-08-22 06:15:02 +02:00
static std :: vector < u32 > s_efbCache [ 2 ][ EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT ]; // 2 for PEEK_Z and PEEK_COLOR
2009-07-29 03:11:35 +00:00
2014-07-08 14:29:26 +02:00
static int GetNumMSAASamples ( int MSAAMode )
2011-05-07 22:00:05 +00:00
{
2013-03-25 15:14:24 +01:00
int samples ;
2011-05-07 22:00:05 +00:00
switch ( MSAAMode )
{
case MULTISAMPLE_OFF :
2013-03-17 10:44:57 +01:00
samples = 1 ;
break ;
2011-05-07 22:00:05 +00:00
case MULTISAMPLE_2X :
2013-03-17 10:44:57 +01:00
samples = 2 ;
break ;
2011-05-07 22:00:05 +00:00
case MULTISAMPLE_4X :
2013-03-17 10:44:57 +01:00
samples = 4 ;
break ;
2011-05-07 22:00:05 +00:00
case MULTISAMPLE_8X :
2013-03-17 10:44:57 +01:00
samples = 8 ;
break ;
2011-05-07 22:00:05 +00:00
default :
2013-03-17 10:44:57 +01:00
samples = 1 ;
2011-05-07 22:00:05 +00:00
}
2013-10-29 01:23:17 -04:00
2014-08-15 14:09:53 -04:00
if ( samples <= g_ogl_config . max_samples )
return samples ;
2013-10-29 01:23:17 -04:00
2013-08-27 13:24:23 +02:00
// TODO: move this to InitBackendInfo
OSD :: AddMessage ( StringFromFormat ( "%d Anti Aliasing samples selected, but only %d supported by your GPU." , samples , g_ogl_config . max_samples ), 10000 );
2013-03-25 15:14:24 +01:00
return g_ogl_config . max_samples ;
2011-05-07 22:00:05 +00:00
}
2014-07-08 14:29:26 +02:00
static void GLAPIENTRY ErrorCallback ( GLenum source , GLenum type , GLuint id , GLenum severity , GLsizei length , const char * message , const void * userParam )
2013-04-04 17:37:16 +02:00
{
const char * s_source ;
const char * s_type ;
2013-05-05 23:33:49 -05:00
2014-03-11 00:30:55 +13:00
switch ( source )
2013-04-04 17:37:16 +02:00
{
case GL_DEBUG_SOURCE_API_ARB : s_source = "API" ; break ;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB : s_source = "Window System" ; break ;
case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB : s_source = "Shader Compiler" ; break ;
case GL_DEBUG_SOURCE_THIRD_PARTY_ARB : s_source = "Third Party" ; break ;
case GL_DEBUG_SOURCE_APPLICATION_ARB : s_source = "Application" ; break ;
case GL_DEBUG_SOURCE_OTHER_ARB : s_source = "Other" ; break ;
default : s_source = "Unknown" ; break ;
}
2014-03-11 00:30:55 +13:00
switch ( type )
2013-04-04 17:37:16 +02:00
{
case GL_DEBUG_TYPE_ERROR_ARB : s_type = "Error" ; break ;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB : s_type = "Deprecated" ; break ;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB : s_type = "Undefined" ; break ;
case GL_DEBUG_TYPE_PORTABILITY_ARB : s_type = "Portability" ; break ;
case GL_DEBUG_TYPE_PERFORMANCE_ARB : s_type = "Performance" ; break ;
case GL_DEBUG_TYPE_OTHER_ARB : s_type = "Other" ; break ;
default : s_type = "Unknown" ; break ;
}
2014-03-11 00:30:55 +13:00
switch ( severity )
2013-04-04 17:37:16 +02:00
{
2015-09-10 18:27:31 +12:00
case GL_DEBUG_SEVERITY_HIGH_ARB : ERROR_LOG ( HOST_GPU , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
case GL_DEBUG_SEVERITY_MEDIUM_ARB : WARN_LOG ( HOST_GPU , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
case GL_DEBUG_SEVERITY_LOW_ARB : WARN_LOG ( HOST_GPU , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
case GL_DEBUG_SEVERITY_NOTIFICATION : DEBUG_LOG ( HOST_GPU , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
default : ERROR_LOG ( HOST_GPU , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
2013-04-04 17:37:16 +02:00
}
}
2013-06-25 18:14:41 +02:00
// Two small Fallbacks to avoid GL_ARB_ES2_compatibility
2014-07-08 14:29:26 +02:00
static void GLAPIENTRY DepthRangef ( GLfloat neardepth , GLfloat fardepth )
2013-06-25 18:14:41 +02:00
{
2014-01-01 16:02:18 -06:00
glDepthRange ( neardepth , fardepth );
2013-06-25 18:14:41 +02:00
}
2014-07-08 14:29:26 +02:00
static void GLAPIENTRY ClearDepthf ( GLfloat depthval )
2013-06-25 18:14:41 +02:00
{
2014-01-01 16:02:18 -06:00
glClearDepth ( depthval );
2013-06-25 18:14:41 +02:00
}
2014-07-08 14:29:26 +02:00
static void InitDriverInfo ()
2013-06-11 08:33:56 -05:00
{
std :: string svendor = std :: string ( g_ogl_config . gl_vendor );
std :: string srenderer = std :: string ( g_ogl_config . gl_renderer );
2013-08-23 10:52:29 +02:00
std :: string sversion = std :: string ( g_ogl_config . gl_version );
2013-06-11 08:33:56 -05:00
DriverDetails :: Vendor vendor = DriverDetails :: VENDOR_UNKNOWN ;
2013-08-21 05:34:42 -05:00
DriverDetails :: Driver driver = DriverDetails :: DRIVER_UNKNOWN ;
2013-06-11 08:33:56 -05:00
double version = 0.0 ;
2013-12-30 18:26:55 -06:00
u32 family = 0 ;
2013-06-11 08:33:56 -05:00
2013-08-21 05:34:42 -05:00
// Get the vendor first
2013-06-11 08:33:56 -05:00
if ( svendor == "NVIDIA Corporation" && srenderer != "NVIDIA Tegra" )
2014-08-15 14:09:53 -04:00
{
2013-10-29 01:23:17 -04:00
vendor = DriverDetails :: VENDOR_NVIDIA ;
2014-08-15 14:09:53 -04:00
}
2013-08-21 05:34:42 -05:00
else if ( svendor == "ATI Technologies Inc." || svendor == "Advanced Micro Devices, Inc." )
2014-08-15 14:09:53 -04:00
{
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_ATI ;
2014-08-15 14:09:53 -04:00
}
2013-08-23 10:52:29 +02:00
else if ( std :: string :: npos != sversion . find ( "Mesa" ))
2014-08-15 14:09:53 -04:00
{
2013-08-23 10:52:29 +02:00
vendor = DriverDetails :: VENDOR_MESA ;
2014-08-15 14:09:53 -04:00
}
2013-06-11 08:33:56 -05:00
else if ( std :: string :: npos != svendor . find ( "Intel" ))
2014-08-15 14:09:53 -04:00
{
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_INTEL ;
2014-08-15 14:09:53 -04:00
}
2013-06-11 08:33:56 -05:00
else if ( svendor == "ARM" )
2014-08-15 14:09:53 -04:00
{
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_ARM ;
2014-08-15 14:09:53 -04:00
}
2013-08-21 05:41:32 -05:00
else if ( svendor == "http://limadriver.org/" )
{
vendor = DriverDetails :: VENDOR_ARM ;
driver = DriverDetails :: DRIVER_LIMA ;
}
2013-06-11 08:33:56 -05:00
else if ( svendor == "Qualcomm" )
2014-08-15 14:09:53 -04:00
{
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_QUALCOMM ;
2014-08-15 14:09:53 -04:00
}
2013-06-11 08:33:56 -05:00
else if ( svendor == "Imagination Technologies" )
2014-08-15 14:09:53 -04:00
{
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_IMGTEC ;
2014-08-15 14:09:53 -04:00
}
2013-07-02 21:58:09 -04:00
else if ( svendor == "NVIDIA Corporation" && srenderer == "NVIDIA Tegra" )
2014-08-15 14:09:53 -04:00
{
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_TEGRA ;
2014-08-15 14:09:53 -04:00
}
2013-06-11 08:33:56 -05:00
else if ( svendor == "Vivante Corporation" )
2014-08-15 14:09:53 -04:00
{
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_VIVANTE ;
2014-08-15 14:09:53 -04:00
}
2013-10-29 01:23:17 -04:00
2013-06-11 08:33:56 -05:00
// Get device family and driver version...if we care about it
2014-03-11 00:30:55 +13:00
switch ( vendor )
2013-06-11 08:33:56 -05:00
{
case DriverDetails :: VENDOR_QUALCOMM :
{
2014-12-19 21:31:37 +00:00
driver = DriverDetails :: DRIVER_QUALCOMM ;
2013-06-11 08:33:56 -05:00
double glVersion ;
sscanf ( g_ogl_config . gl_version , "OpenGL ES %lg V@%lg" , & glVersion , & version );
}
break ;
2013-07-25 05:44:01 +00:00
case DriverDetails :: VENDOR_ARM :
2014-04-11 23:46:44 -05:00
// Currently the Mali-T line has two families in it.
// Mali-T6xx and Mali-T7xx
// These two families are similar enough that they share bugs in their drivers.
2014-12-19 21:41:12 +00:00
//
// Mali drivers provide no way to explicitly find out what video driver is running.
// This is similar to how we can't find the Nvidia driver version in Windows.
// Good thing is that ARM introduces a new video driver about once every two years so we can
// find the driver version by the features it exposes.
// r2p0 - No OpenGL ES 3.0 support (We don't support this)
// r3p0 - OpenGL ES 3.0 support
// r4p0 - Supports 'GL_EXT_shader_pixel_local_storage' extension.
2014-04-11 23:38:40 -05:00
2014-12-19 21:41:12 +00:00
driver = DriverDetails :: DRIVER_ARM ;
if ( GLExtensions :: Supports ( "GL_EXT_shader_pixel_local_storage" ))
version = 400 ;
else
version = 300 ;
2013-08-23 10:52:29 +02:00
break ;
case DriverDetails :: VENDOR_MESA :
{
2014-03-11 00:30:55 +13:00
if ( svendor == "nouveau" )
2013-08-23 10:52:29 +02:00
driver = DriverDetails :: DRIVER_NOUVEAU ;
2014-03-11 00:30:55 +13:00
else if ( svendor == "Intel Open Source Technology Center" )
2013-08-23 10:52:29 +02:00
driver = DriverDetails :: DRIVER_I965 ;
2014-03-11 00:30:55 +13:00
else if ( std :: string :: npos != srenderer . find ( "AMD" ) || std :: string :: npos != srenderer . find ( "ATI" ))
2013-08-23 10:52:29 +02:00
driver = DriverDetails :: DRIVER_R600 ;
2013-10-29 01:23:17 -04:00
2013-08-23 10:52:29 +02:00
int major = 0 ;
int minor = 0 ;
int release = 0 ;
2015-07-25 12:20:00 +02:00
sscanf ( g_ogl_config . gl_version , "%*s (Core Profile) Mesa %d.%d.%d" , & major , & minor , & release );
2013-08-23 10:52:29 +02:00
version = 100 * major + 10 * minor + release ;
}
2013-07-25 05:44:01 +00:00
break ;
2014-09-04 18:03:53 -05:00
case DriverDetails :: VENDOR_INTEL : // Happens in OS X/Windows
{
2013-12-30 18:26:55 -06:00
sscanf ( g_ogl_config . gl_renderer , "Intel HD Graphics %d" , & family );
2014-09-04 18:03:53 -05:00
#ifdef _WIN32
2013-12-30 18:26:55 -06:00
int glmajor = 0 ;
int glminor = 0 ;
int major = 0 ;
int minor = 0 ;
int release = 0 ;
2014-09-04 18:03:53 -05:00
int revision = 0 ;
// Example version string: '4.3.0 - Build 10.18.10.3907'
sscanf ( g_ogl_config . gl_version , "%d.%d.0 - Build %d.%d.%d.%d" , & glmajor , & glminor , & major , & minor , & release , & revision );
version = 100000000 * major + 1000000 * minor + 10000 * release + revision ;
version /= 10000 ;
#endif
}
2013-12-30 18:26:55 -06:00
break ;
2014-01-07 18:29:04 -06:00
case DriverDetails :: VENDOR_NVIDIA :
{
int glmajor = 0 ;
int glminor = 0 ;
int glrelease = 0 ;
int major = 0 ;
int minor = 0 ;
2015-01-11 00:17:29 -05:00
// TODO: this is known to be broken on Windows
// Nvidia seems to have removed their driver version from this string, so we can't get it.
// hopefully we'll never have to workaround Nvidia bugs
2014-01-07 19:35:06 -06:00
sscanf ( g_ogl_config . gl_version , "%d.%d.%d NVIDIA %d.%d" , & glmajor , & glminor , & glrelease , & major , & minor );
2014-01-07 18:29:04 -06:00
version = 100 * major + minor ;
}
break ;
2013-06-11 08:33:56 -05:00
// We don't care about these
default :
break ;
}
2013-12-30 18:26:55 -06:00
DriverDetails :: Init ( vendor , driver , version , family );
2013-06-11 08:33:56 -05:00
}
2009-06-07 12:06:15 +00:00
// Init functions
2010-11-18 02:21:26 +00:00
Renderer :: Renderer ()
2009-02-23 07:23:34 +00:00
{
2011-02-13 19:05:24 +00:00
OSDInternalW = 0 ;
OSDInternalH = 0 ;
2012-12-07 23:34:38 +01:00
s_ShowEFBCopyRegions_VBO = 0 ;
2011-05-07 22:00:05 +00:00
s_blendMode = 0 ;
2009-04-03 20:22:45 +00:00
2011-05-07 22:00:05 +00:00
bool bSuccess = true ;
2013-05-05 23:33:49 -05:00
// Init extension support.
2013-12-30 07:22:50 -06:00
if ( ! GLExtensions :: Init ())
2010-08-10 01:08:22 +00:00
{
2013-12-30 07:22:50 -06:00
// OpenGL 2.0 is required for all shader based drawings. There is no way to get this by extensions
PanicAlert ( "GPU: OGL ERROR: Does your video card support OpenGL 2.0?" );
bSuccess = false ;
2013-09-02 13:14:45 +02:00
}
2014-01-01 20:06:11 -06:00
g_ogl_config . gl_vendor = ( const char * ) glGetString ( GL_VENDOR );
g_ogl_config . gl_renderer = ( const char * ) glGetString ( GL_RENDERER );
g_ogl_config . gl_version = ( const char * ) glGetString ( GL_VERSION );
g_ogl_config . glsl_version = ( const char * ) glGetString ( GL_SHADING_LANGUAGE_VERSION );
InitDriverInfo ();
2014-03-29 11:05:44 +01:00
2013-09-02 13:14:45 +02:00
// check for the max vertex attributes
GLint numvertexattribs = 0 ;
glGetIntegerv ( GL_MAX_VERTEX_ATTRIBS , & numvertexattribs );
if ( numvertexattribs < 16 )
{
PanicAlert ( "GPU: OGL ERROR: Number of attributes %d not enough. \n "
"GPU: Does your video card support OpenGL 2.x?" ,
numvertexattribs );
bSuccess = false ;
}
// check the max texture width and height
GLint max_texture_size ;
glGetIntegerv ( GL_MAX_TEXTURE_SIZE , ( GLint * ) & max_texture_size );
if ( max_texture_size < 1024 )
{
PanicAlert ( "GL_MAX_TEXTURE_SIZE too small at %i - must be at least 1024." ,
max_texture_size );
bSuccess = false ;
2010-06-05 01:38:22 +00:00
}
2013-10-29 01:23:17 -04:00
2013-12-30 07:22:50 -06:00
if ( ! GLExtensions :: Supports ( "GL_ARB_framebuffer_object" ))
2013-01-03 12:06:47 +01:00
{
2013-09-02 13:14:45 +02:00
// We want the ogl3 framebuffer instead of the ogl2 one for better blitting support.
// It's also compatible with the gles3 one.
PanicAlert ( "GPU: ERROR: Need GL_ARB_framebuffer_object for multiple render targets. \n "
2013-01-03 12:06:47 +01:00
"GPU: Does your video card support OpenGL 3.0?" );
bSuccess = false ;
}
2013-12-30 07:22:50 -06:00
if ( ! GLExtensions :: Supports ( "GL_ARB_vertex_array_object" ))
2012-12-09 22:49:58 +01:00
{
2013-09-02 13:14:45 +02:00
// This extension is used to replace lots of pointer setting function.
// Also gles3 requires to use it.
PanicAlert ( "GPU: OGL ERROR: Need GL_ARB_vertex_array_object. \n "
2012-12-09 22:49:58 +01:00
"GPU: Does your video card support OpenGL 3.0?" );
bSuccess = false ;
}
2013-10-29 01:23:17 -04:00
2013-12-30 07:22:50 -06:00
if ( ! GLExtensions :: Supports ( "GL_ARB_map_buffer_range" ))
2012-12-15 14:43:01 +01:00
{
2013-09-02 13:14:45 +02:00
// ogl3 buffer mapping for better streaming support.
// The ogl2 one also isn't in gles3.
PanicAlert ( "GPU: OGL ERROR: Need GL_ARB_map_buffer_range. \n "
2012-12-15 14:43:01 +01:00
"GPU: Does your video card support OpenGL 3.0?" );
bSuccess = false ;
}
2014-01-30 16:38:11 +01:00
if ( ! GLExtensions :: Supports ( "GL_ARB_uniform_buffer_object" ))
{
// ubo allow us to keep the current constants on shader switches
// we also can stream them much nicer and pack into it whatever we want to
PanicAlert ( "GPU: OGL ERROR: Need GL_ARB_uniform_buffer_object. \n "
"GPU: Does your video card support OpenGL 3.1?" );
bSuccess = false ;
}
else if ( DriverDetails :: HasBug ( DriverDetails :: BUG_BROKENUBO ))
{
PanicAlert ( "Buggy GPU driver detected. \n "
"Please either install the closed-source GPU driver or update your Mesa 3D version." );
bSuccess = false ;
}
2014-11-26 21:33:47 +01:00
if ( ! GLExtensions :: Supports ( "GL_ARB_sampler_objects" ))
2013-02-19 21:24:08 -06:00
{
2013-09-02 13:14:45 +02:00
// Our sampler cache uses this extension. It could easyly be workaround and it's by far the
// highest requirement, but it seems that no driver lacks support for it.
2014-11-26 21:33:47 +01:00
PanicAlert ( "GPU: OGL ERROR: Need GL_ARB_sampler_objects. \n "
"GPU: Does your video card support OpenGL 3.3?" );
bSuccess = false ;
}
if ( GLExtensions :: Version () < 300 )
{
// integer vertex attributes require a gl3 only function
PanicAlert ( "GPU: OGL ERROR: Need OpenGL version 3. \n "
"GPU: Does your video card support OpenGL 3?" );
2013-02-19 21:24:08 -06:00
bSuccess = false ;
}
2013-10-29 01:23:17 -04:00
2013-06-25 18:14:41 +02:00
// OpenGL 3 doesn't provide GLES like float functions for depth.
// They are in core in OpenGL 4.1, so almost every driver should support them.
// But for the oldest ones, we provide fallbacks to the old double functions.
2013-12-30 07:22:50 -06:00
if ( ! GLExtensions :: Supports ( "GL_ARB_ES2_compatibility" ) && GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGL )
2013-06-25 18:14:41 +02:00
{
glDepthRangef = DepthRangef ;
glClearDepthf = ClearDepthf ;
}
2014-01-01 20:06:11 -06:00
2015-09-04 20:25:59 -05:00
g_Config . backend_info . bSupportsDualSourceBlend = GLExtensions :: Supports ( "GL_ARB_blend_func_extended" ) ||
GLExtensions :: Supports ( "GL_EXT_blend_func_extended" );
2014-01-17 16:10:10 +01:00
g_Config . backend_info . bSupportsPrimitiveRestart = ! DriverDetails :: HasBug ( DriverDetails :: BUG_PRIMITIVERESTART ) &&
(( GLExtensions :: Version () >= 310 ) || GLExtensions :: Supports ( "GL_NV_primitive_restart" ));
2014-11-13 23:26:49 +01:00
g_Config . backend_info . bSupportsBBox = GLExtensions :: Supports ( "GL_ARB_shader_storage_buffer_object" );
2014-10-29 14:51:12 +01:00
g_Config . backend_info . bSupportsGSInstancing = GLExtensions :: Supports ( "GL_ARB_gpu_shader5" );
2015-09-07 22:21:11 +02:00
g_Config . backend_info . bSupportsSSAA = GLExtensions :: Supports ( "GL_ARB_gpu_shader5" ) && GLExtensions :: Supports ( "GL_ARB_sample_shading" );
2014-12-21 12:52:14 +01:00
g_Config . backend_info . bSupportsGeometryShaders = GLExtensions :: Version () >= 320 ;
2015-09-01 05:23:43 -05:00
g_Config . backend_info . bSupportsPaletteConversion = GLExtensions :: Supports ( "GL_ARB_texture_buffer_object" ) ||
GLExtensions :: Supports ( "GL_OES_texture_buffer" ) ||
GLExtensions :: Supports ( "GL_EXT_texture_buffer" );
2015-05-19 01:18:04 +02:00
g_Config . backend_info . bSupportsClipControl = GLExtensions :: Supports ( "GL_ARB_clip_control" );
2015-07-25 12:20:00 +02:00
g_ogl_config . bSupportsCopySubImage = ( GLExtensions :: Supports ( "GL_ARB_copy_image" ) ||
GLExtensions :: Supports ( "GL_NV_copy_image" ) ||
GLExtensions :: Supports ( "GL_EXT_copy_image" ) ||
GLExtensions :: Supports ( "GL_OES_copy_image" )) &&
! DriverDetails :: HasBug ( DriverDetails :: BUG_BROKENCOPYIMAGE );
2014-03-30 13:58:05 -05:00
// Desktop OpenGL supports the binding layout if it supports 420pack
// OpenGL ES 3.1 supports it implicitly without an extension
g_Config . backend_info . bSupportsBindingLayout = GLExtensions :: Supports ( "GL_ARB_shading_language_420pack" );
2013-10-29 01:23:17 -04:00
2013-12-30 07:22:50 -06:00
g_ogl_config . bSupportsGLSLCache = GLExtensions :: Supports ( "GL_ARB_get_program_binary" );
g_ogl_config . bSupportsGLPinnedMemory = GLExtensions :: Supports ( "GL_AMD_pinned_memory" );
g_ogl_config . bSupportsGLSync = GLExtensions :: Supports ( "GL_ARB_sync" );
2014-11-21 18:52:39 -06:00
g_ogl_config . bSupportsGLBaseVertex = GLExtensions :: Supports ( "GL_ARB_draw_elements_base_vertex" ) ||
2015-01-18 15:23:51 -06:00
GLExtensions :: Supports ( "GL_EXT_draw_elements_base_vertex" ) ||
GLExtensions :: Supports ( "GL_OES_draw_elements_base_vertex" );
g_ogl_config . bSupportsGLBufferStorage = GLExtensions :: Supports ( "GL_ARB_buffer_storage" ) ||
2015-05-19 00:54:00 +02:00
GLExtensions :: Supports ( "GL_EXT_buffer_storage" );
2014-04-30 18:48:40 +02:00
g_ogl_config . bSupportsMSAA = GLExtensions :: Supports ( "GL_ARB_texture_multisample" );
2013-12-30 07:22:50 -06:00
g_ogl_config . bSupportOGL31 = GLExtensions :: Version () >= 310 ;
g_ogl_config . bSupportViewportFloat = GLExtensions :: Supports ( "GL_ARB_viewport_array" );
2015-08-26 00:09:32 -05:00
g_ogl_config . bSupportsDebug = GLExtensions :: Supports ( "GL_KHR_debug" ) ||
GLExtensions :: Supports ( "GL_ARB_debug_output" );
2015-09-01 00:17:24 -05:00
g_ogl_config . bSupports3DTextureStorage = GLExtensions :: Supports ( "GL_ARB_texture_storage_multisample" ) ||
GLExtensions :: Supports ( "GL_OES_texture_storage_multisample_2d_array" );
g_ogl_config . bSupports2DTextureStorage = GLExtensions :: Supports ( "GL_ARB_texture_storage_multisample" );
2015-10-18 00:43:22 +13:00
g_ogl_config . bSupportsEarlyFragmentTests = GLExtensions :: Supports ( "GL_ARB_shader_image_load_store" );
g_ogl_config . bSupportsConservativeDepth = GLExtensions :: Supports ( "GL_ARB_conservative_depth" );
2013-11-14 03:11:40 -05:00
2013-12-30 07:22:50 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGLES3 )
2014-03-30 13:58:05 -05:00
{
2015-08-26 00:09:32 -05:00
g_ogl_config . SupportedESPointSize = GLExtensions :: Supports ( "GL_OES_geometry_point_size" ) ? 1 : GLExtensions :: Supports ( "GL_EXT_geometry_point_size" ) ? 2 : 0 ;
2015-09-01 05:23:43 -05:00
g_ogl_config . SupportedESTextureBuffer = GLExtensions :: Supports ( "VERSION_GLES_3_2" ) ? ES_TEXBUF_TYPE :: TEXBUF_CORE :
GLExtensions :: Supports ( "GL_OES_texture_buffer" ) ? ES_TEXBUF_TYPE :: TEXBUF_OES :
GLExtensions :: Supports ( "GL_EXT_texture_buffer" ) ? ES_TEXBUF_TYPE :: TEXBUF_EXT : ES_TEXBUF_TYPE :: TEXBUF_NONE ;
2015-08-26 00:09:32 -05:00
2015-08-22 09:12:19 -05:00
if ( strstr ( g_ogl_config . glsl_version , "3.0" ) || DriverDetails :: HasBug ( DriverDetails :: BUG_BROKENGLES31 ))
2014-03-30 13:58:05 -05:00
{
g_ogl_config . eSupportedGLSLVersion = GLSLES_300 ;
2014-12-07 05:29:36 +00:00
g_ogl_config . bSupportsAEP = false ;
2014-12-17 00:26:03 +01:00
g_Config . backend_info . bSupportsGeometryShaders = false ;
2014-03-30 13:58:05 -05:00
}
2015-08-26 00:09:32 -05:00
else if ( strstr ( g_ogl_config . glsl_version , "3.1" ))
2014-03-30 13:58:05 -05:00
{
g_ogl_config . eSupportedGLSLVersion = GLSLES_310 ;
2014-12-07 05:29:36 +00:00
g_ogl_config . bSupportsAEP = GLExtensions :: Supports ( "GL_ANDROID_extension_pack_es31a" );
2014-03-30 13:58:05 -05:00
g_Config . backend_info . bSupportsBindingLayout = true ;
2015-10-18 00:43:22 +13:00
g_ogl_config . bSupportsEarlyFragmentTests = true ;
2014-12-17 00:26:03 +01:00
g_Config . backend_info . bSupportsGeometryShaders = g_ogl_config . bSupportsAEP ;
2015-08-26 00:09:32 -05:00
g_Config . backend_info . bSupportsGSInstancing = g_Config . backend_info . bSupportsGeometryShaders && g_ogl_config . SupportedESPointSize > 0 ;
2015-09-06 13:58:18 +02:00
g_Config . backend_info . bSupportsSSAA = g_ogl_config . bSupportsAEP ;
2015-09-07 12:07:27 -05:00
g_Config . backend_info . bSupportsBBox = true ;
2015-09-01 00:17:24 -05:00
g_ogl_config . bSupportsMSAA = true ;
g_ogl_config . bSupports2DTextureStorage = true ;
if ( g_ActiveConfig . iStereoMode > 0 && g_ActiveConfig . iMultisampleMode > 1 && ! g_ogl_config . bSupports3DTextureStorage )
{
// GLES 3.1 can't support stereo rendering and MSAA
OSD :: AddMessage ( "MSAA Stereo rendering isn't supported by your GPU." , 10000 );
g_ActiveConfig . iMultisampleMode = 1 ;
}
2014-03-30 13:58:05 -05:00
}
2015-08-26 00:09:32 -05:00
else
{
g_ogl_config . eSupportedGLSLVersion = GLSLES_320 ;
g_ogl_config . bSupportsAEP = GLExtensions :: Supports ( "GL_ANDROID_extension_pack_es31a" );
g_Config . backend_info . bSupportsBindingLayout = true ;
2015-10-18 00:43:22 +13:00
g_ogl_config . bSupportsEarlyFragmentTests = true ;
2015-08-26 00:09:32 -05:00
g_Config . backend_info . bSupportsGeometryShaders = true ;
g_Config . backend_info . bSupportsGSInstancing = g_ogl_config . SupportedESPointSize > 0 ;
2015-09-01 05:23:43 -05:00
g_Config . backend_info . bSupportsPaletteConversion = true ;
2015-09-01 00:17:24 -05:00
g_Config . backend_info . bSupportsSSAA = true ;
2015-09-07 12:07:27 -05:00
g_Config . backend_info . bSupportsBBox = true ;
2015-08-26 00:09:32 -05:00
g_ogl_config . bSupportsCopySubImage = true ;
g_ogl_config . bSupportsGLBaseVertex = true ;
g_ogl_config . bSupportsDebug = true ;
2015-09-01 00:17:24 -05:00
g_ogl_config . bSupportsMSAA = true ;
g_ogl_config . bSupports2DTextureStorage = true ;
g_ogl_config . bSupports3DTextureStorage = true ;
2015-08-26 00:09:32 -05:00
}
2014-03-30 13:58:05 -05:00
}
2013-04-08 14:50:58 +02:00
else
{
2014-03-11 00:30:55 +13:00
if ( strstr ( g_ogl_config . glsl_version , "1.00" ) || strstr ( g_ogl_config . glsl_version , "1.10" ) || strstr ( g_ogl_config . glsl_version , "1.20" ))
2013-12-30 07:22:50 -06:00
{
PanicAlert ( "GPU: OGL ERROR: Need at least GLSL 1.30 \n "
"GPU: Does your video card support OpenGL 3.0? \n "
"GPU: Your driver supports GLSL %s" , g_ogl_config . glsl_version );
bSuccess = false ;
}
2014-03-11 00:30:55 +13:00
else if ( strstr ( g_ogl_config . glsl_version , "1.30" ))
2013-12-30 07:22:50 -06:00
{
g_ogl_config . eSupportedGLSLVersion = GLSL_130 ;
2015-10-18 00:43:22 +13:00
g_ogl_config . bSupportsEarlyFragmentTests = false ; // layout keyword is only supported on glsl150+
g_ogl_config . bSupportsConservativeDepth = false ; // layout keyword is only supported on glsl150+
2014-12-17 00:26:03 +01:00
g_Config . backend_info . bSupportsGeometryShaders = false ; // geometry shaders are only supported on glsl150+
2013-12-30 07:22:50 -06:00
}
2014-03-11 00:30:55 +13:00
else if ( strstr ( g_ogl_config . glsl_version , "1.40" ))
2013-12-30 07:22:50 -06:00
{
g_ogl_config . eSupportedGLSLVersion = GLSL_140 ;
2015-10-18 00:43:22 +13:00
g_ogl_config . bSupportsEarlyFragmentTests = false ; // layout keyword is only supported on glsl150+
g_ogl_config . bSupportsConservativeDepth = false ; // layout keyword is only supported on glsl150+
2014-12-17 00:26:03 +01:00
g_Config . backend_info . bSupportsGeometryShaders = false ; // geometry shaders are only supported on glsl150+
2015-09-06 13:58:18 +02:00
}
else if ( strstr ( g_ogl_config . glsl_version , "1.50" ))
{
g_ogl_config . eSupportedGLSLVersion = GLSL_150 ;
}
else if ( strstr ( g_ogl_config . glsl_version , "3.30" ))
{
g_ogl_config . eSupportedGLSLVersion = GLSL_330 ;
2013-12-30 07:22:50 -06:00
}
else
{
2015-09-06 13:58:18 +02:00
g_ogl_config . eSupportedGLSLVersion = GLSL_400 ;
2015-09-07 22:21:11 +02:00
g_Config . backend_info . bSupportsSSAA = true ;
2013-12-30 07:22:50 -06:00
}
2014-12-07 05:29:36 +00:00
// Desktop OpenGL can't have the Android Extension Pack
g_ogl_config . bSupportsAEP = false ;
2013-12-30 07:22:50 -06:00
}
2014-11-22 15:07:52 +01:00
2015-10-18 00:43:22 +13:00
// Either method can do early-z tests. See PixelShaderGen for details.
g_Config . backend_info . bSupportsEarlyZ = g_ogl_config . bSupportsEarlyFragmentTests || g_ogl_config . bSupportsConservativeDepth ;
2014-12-28 21:46:00 +01:00
if ( g_ogl_config . bSupportsDebug )
2013-12-30 07:22:50 -06:00
{
2014-12-28 21:46:00 +01:00
if ( GLExtensions :: Supports ( "GL_KHR_debug" ))
{
glDebugMessageControl ( GL_DONT_CARE , GL_DONT_CARE , GL_DONT_CARE , 0 , nullptr , true );
glDebugMessageCallback ( ErrorCallback , nullptr );
}
else
{
glDebugMessageControlARB ( GL_DONT_CARE , GL_DONT_CARE , GL_DONT_CARE , 0 , nullptr , true );
glDebugMessageCallbackARB ( ErrorCallback , nullptr );
}
2015-09-10 18:27:31 +12:00
if ( LogManager :: GetInstance () -> IsEnabled ( LogTypes :: HOST_GPU , LogTypes :: LERROR ))
2014-12-28 21:46:00 +01:00
glEnable ( GL_DEBUG_OUTPUT );
else
glDisable ( GL_DEBUG_OUTPUT );
2013-04-08 14:50:58 +02:00
}
2014-11-22 15:07:52 +01:00
2013-11-05 17:37:32 +01:00
int samples ;
glGetIntegerv ( GL_SAMPLES , & samples );
2014-03-11 00:30:55 +13:00
if ( samples > 1 )
2013-11-05 17:37:32 +01:00
{
// MSAA on default framebuffer isn't working because of glBlitFramebuffer.
// It also isn't useful as we don't render anything to the default framebuffer.
// We also try to get a non-msaa fb, so this only happens when forced by the driver.
PanicAlert ( "MSAA on default framebuffer isn't supported. \n "
2015-01-11 00:17:29 -05:00
"Please avoid forcing Dolphin to use MSAA by the driver. \n "
2013-11-05 17:37:32 +01:00
"%d samples on default framebuffer found." , samples );
bSuccess = false ;
}
2013-09-02 13:14:45 +02:00
if ( ! bSuccess )
{
// Not all needed extensions are supported, so we have to stop here.
// Else some of the next calls might crash.
return ;
}
2013-10-29 01:23:17 -04:00
2013-03-25 15:14:24 +01:00
glGetIntegerv ( GL_MAX_SAMPLES , & g_ogl_config . max_samples );
2014-04-30 18:48:40 +02:00
if ( g_ogl_config . max_samples < 1 || ! g_ogl_config . bSupportsMSAA )
2013-04-05 07:08:32 +02:00
g_ogl_config . max_samples = 1 ;
2013-10-29 01:23:17 -04:00
2014-10-30 14:56:40 +01:00
g_Config . VerifyValidity ();
2013-01-21 18:44:33 +01:00
UpdateActiveConfig ();
2013-03-25 15:14:24 +01:00
OSD :: AddMessage ( StringFromFormat ( "Video Info: %s, %s, %s" ,
g_ogl_config . gl_vendor ,
g_ogl_config . gl_renderer ,
2013-08-24 01:44:16 +02:00
g_ogl_config . gl_version ), 5000 );
2013-10-29 01:23:17 -04:00
2015-06-24 14:43:35 +02:00
WARN_LOG ( VIDEO , "Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s%s%s%s" ,
2013-02-17 21:41:00 +01:00
g_ActiveConfig . backend_info . bSupportsDualSourceBlend ? "" : "DualSourceBlend " ,
2013-03-29 14:27:33 +01:00
g_ActiveConfig . backend_info . bSupportsPrimitiveRestart ? "" : "PrimitiveRestart " ,
2013-07-22 12:02:16 +02:00
g_ActiveConfig . backend_info . bSupportsEarlyZ ? "" : "EarlyZ " ,
2013-03-25 15:14:24 +01:00
g_ogl_config . bSupportsGLPinnedMemory ? "" : "PinnedMemory " ,
g_ogl_config . bSupportsGLSLCache ? "" : "ShaderCache " ,
g_ogl_config . bSupportsGLBaseVertex ? "" : "BaseVertex " ,
2013-12-27 10:56:03 -06:00
g_ogl_config . bSupportsGLBufferStorage ? "" : "BufferStorage " ,
2013-03-25 15:14:24 +01:00
g_ogl_config . bSupportsGLSync ? "" : "Sync " ,
2014-04-30 18:48:40 +02:00
g_ogl_config . bSupportsMSAA ? "" : "MSAA " ,
2015-09-01 00:17:24 -05:00
g_ActiveConfig . backend_info . bSupportsSSAA ? "" : "SSAA " ,
2015-05-19 00:54:00 +02:00
g_ActiveConfig . backend_info . bSupportsGSInstancing ? "" : "GSInstancing " ,
2015-06-13 15:51:58 +02:00
g_ActiveConfig . backend_info . bSupportsClipControl ? "" : "ClipControl " ,
2015-07-25 12:10:52 +02:00
g_ogl_config . bSupportsCopySubImage ? "" : "CopyImageSubData "
2013-04-04 18:55:37 +02:00
);
2013-10-29 01:23:17 -04:00
2014-12-28 18:35:23 +01:00
s_last_multisample_mode = g_ActiveConfig . iMultisampleMode ;
s_MSAASamples = GetNumMSAASamples ( s_last_multisample_mode );
2014-12-28 18:26:25 +01:00
2014-12-28 18:35:23 +01:00
s_last_stereo_mode = g_ActiveConfig . iStereoMode > 0 ;
2014-12-28 18:26:25 +01:00
s_last_xfb_mode = g_ActiveConfig . bUseRealXFB ;
2013-10-29 01:23:17 -04:00
2013-04-19 09:21:45 -04:00
// Decide framebuffer size
2012-12-17 14:54:20 -06:00
s_backbuffer_width = ( int ) GLInterface -> GetBackBufferWidth ();
s_backbuffer_height = ( int ) GLInterface -> GetBackBufferHeight ();
2010-09-28 02:15:02 +00:00
2009-03-05 23:11:13 +00:00
// Handle VSync on/off
2013-03-30 22:17:39 +01:00
s_vsync = g_ActiveConfig . IsVSync ();
2015-09-19 06:12:20 +12:00
if ( ! DriverDetails :: HasBug ( DriverDetails :: BUG_BROKENVSYNC ))
GLInterface -> SwapInterval ( s_vsync );
2009-02-23 07:23:34 +00:00
2012-09-28 23:48:18 +02:00
// TODO: Move these somewhere else?
FramebufferManagerBase :: SetLastXfbWidth ( MAX_XFB_WIDTH );
FramebufferManagerBase :: SetLastXfbHeight ( MAX_XFB_HEIGHT );
2010-05-19 03:15:36 +00:00
2012-09-29 00:04:55 +02:00
UpdateDrawRectangle ( s_backbuffer_width , s_backbuffer_height );
2010-08-10 01:08:22 +00:00
2014-12-28 18:35:23 +01:00
s_last_efb_scale = g_ActiveConfig . iEFBScale ;
2012-09-29 00:19:28 +02:00
CalculateTargetSize ( s_backbuffer_width , s_backbuffer_height );
2010-09-30 15:24:34 +00:00
2015-01-03 06:06:56 +13:00
PixelShaderManager :: SetEfbScaleChanged ();
2010-08-10 01:08:22 +00:00
// Because of the fixed framebuffer size we need to disable the resolution
// options while running
2009-10-22 20:22:50 +00:00
g_Config . bRunning = true ;
2013-10-29 01:23:17 -04:00
2013-01-25 20:39:19 +01:00
glStencilFunc ( GL_ALWAYS , 0 , 0 );
glBlendFunc ( GL_ONE , GL_ONE );
2009-02-23 07:23:34 +00:00
2013-01-25 20:39:19 +01:00
glViewport ( 0 , 0 , GetTargetWidth (), GetTargetHeight ()); // Reset The Current Viewport
2015-05-19 01:18:04 +02:00
if ( g_ActiveConfig . backend_info . bSupportsClipControl )
glClipControl ( GL_LOWER_LEFT , GL_ZERO_TO_ONE );
2013-01-25 20:39:19 +01:00
glClearColor ( 0.0f , 0.0f , 0.0f , 1.0f );
2013-05-05 23:33:49 -05:00
glClearDepthf ( 1.0f );
2013-01-25 20:39:19 +01:00
glEnable ( GL_DEPTH_TEST );
glDepthFunc ( GL_LEQUAL );
glPixelStorei ( GL_UNPACK_ALIGNMENT , 4 ); // 4-byte pixel alignment
glDisable ( GL_STENCIL_TEST );
glEnable ( GL_SCISSOR_TEST );
glScissor ( 0 , 0 , GetTargetWidth (), GetTargetHeight ());
glBlendColor ( 0 , 0 , 0 , 0.5f );
2013-05-05 23:33:49 -05:00
glClearDepthf ( 1.0f );
2014-03-11 00:30:55 +13:00
if ( g_ActiveConfig . backend_info . bSupportsPrimitiveRestart )
2013-04-10 12:58:52 +02:00
{
2013-12-30 07:22:50 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGLES3 )
2014-08-15 14:09:53 -04:00
{
2013-12-30 07:22:50 -06:00
glEnable ( GL_PRIMITIVE_RESTART_FIXED_INDEX );
2014-08-15 14:09:53 -04:00
}
2013-04-10 12:58:52 +02:00
else
2014-08-15 14:09:53 -04:00
{
2014-03-11 00:30:55 +13:00
if ( g_ogl_config . bSupportOGL31 )
2013-12-30 07:22:50 -06:00
{
glEnable ( GL_PRIMITIVE_RESTART );
glPrimitiveRestartIndex ( 65535 );
}
else
{
glEnableClientState ( GL_PRIMITIVE_RESTART_NV );
glPrimitiveRestartIndexNV ( 65535 );
}
2014-08-15 14:09:53 -04:00
}
2013-09-16 15:43:57 +00:00
}
2013-01-25 20:39:19 +01:00
UpdateActiveConfig ();
2014-06-05 10:12:05 +02:00
ClearEFBCache ();
2013-01-25 20:39:19 +01:00
}
Renderer ::~ Renderer ()
{
}
void Renderer :: Shutdown ()
{
2013-07-22 15:41:10 +02:00
delete g_framebuffer_manager ;
2013-10-29 01:23:17 -04:00
2013-01-25 20:39:19 +01:00
g_Config . bRunning = false ;
UpdateActiveConfig ();
2013-10-29 01:23:17 -04:00
2013-01-25 20:39:19 +01:00
glDeleteBuffers ( 1 , & s_ShowEFBCopyRegions_VBO );
glDeleteVertexArrays ( 1 , & s_ShowEFBCopyRegions_VAO );
s_ShowEFBCopyRegions_VBO = 0 ;
2013-10-29 01:23:17 -04:00
2013-01-25 20:39:19 +01:00
delete s_pfont ;
2014-03-09 21:14:26 +01:00
s_pfont = nullptr ;
2013-02-13 13:12:19 +01:00
s_ShowEFBCopyRegions . Destroy ();
2014-07-29 11:57:02 -05:00
delete m_post_processor ;
m_post_processor = nullptr ;
2014-12-03 23:24:58 -08:00
OpenGL_DeleteAttributelessVAO ();
2013-01-25 20:39:19 +01:00
}
void Renderer :: Init ()
{
2013-07-22 15:41:10 +02:00
// Initialize the FramebufferManager
g_framebuffer_manager = new FramebufferManager ( s_target_width , s_target_height ,
2014-04-30 18:13:31 +02:00
s_MSAASamples );
2013-10-29 01:23:17 -04:00
2014-07-29 11:57:02 -05:00
m_post_processor = new OpenGLPostProcessing ();
2010-06-05 01:38:22 +00:00
s_pfont = new RasterFont ();
2013-10-29 01:23:17 -04:00
ProgramShaderCache :: CompileShader ( s_ShowEFBCopyRegions ,
2014-02-25 15:52:22 +01:00
"in vec2 rawpos; \n "
"in vec3 color0; \n "
"out vec4 c; \n "
2013-01-09 20:08:55 +01:00
"void main(void) { \n "
2013-10-06 03:12:13 -05:00
" gl_Position = vec4(rawpos, 0.0, 1.0); \n "
" c = vec4(color0, 1.0); \n "
2013-02-13 13:12:19 +01:00
"} \n " ,
2014-02-25 15:52:22 +01:00
"in vec4 c; \n "
2013-11-25 00:06:29 +00:00
"out vec4 ocol0; \n "
2013-02-13 13:12:19 +01:00
"void main(void) { \n "
" ocol0 = c; \n "
"} \n " );
2013-10-29 01:23:17 -04:00
2014-12-11 01:00:37 -08:00
OpenGL_CreateAttributelessVAO ();
2012-12-07 23:34:38 +01:00
// creating buffers
glGenBuffers ( 1 , & s_ShowEFBCopyRegions_VBO );
2012-12-09 22:49:58 +01:00
glGenVertexArrays ( 1 , & s_ShowEFBCopyRegions_VAO );
glBindBuffer ( GL_ARRAY_BUFFER , s_ShowEFBCopyRegions_VBO );
glBindVertexArray ( s_ShowEFBCopyRegions_VAO );
2013-01-11 11:59:42 +01:00
glEnableVertexAttribArray ( SHADER_POSITION_ATTRIB );
2014-03-09 21:14:26 +01:00
glVertexAttribPointer ( SHADER_POSITION_ATTRIB , 2 , GL_FLOAT , 0 , sizeof ( GLfloat ) * 5 , nullptr );
2013-01-11 11:59:42 +01:00
glEnableVertexAttribArray ( SHADER_COLOR0_ATTRIB );
2014-03-09 21:14:26 +01:00
glVertexAttribPointer ( SHADER_COLOR0_ATTRIB , 3 , GL_FLOAT , 0 , sizeof ( GLfloat ) * 5 , ( GLfloat * ) nullptr + 2 );
2009-09-03 19:24:16 +00:00
}
2009-02-23 07:23:34 +00:00
2010-09-28 02:15:02 +00:00
// Create On-Screen-Messages
2014-12-20 13:02:00 +01:00
void Renderer :: ShowEfbCopyRegions ()
2010-09-28 02:15:02 +00:00
{
2013-12-30 07:22:50 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGL && g_ActiveConfig . bShowEFBCopyRegions )
2010-09-28 02:15:02 +00:00
{
// Set Line Size
glLineWidth ( 3.0f );
2012-12-07 22:49:18 +01:00
// 2*Coords + 3*Color
2014-08-19 20:18:02 -07:00
GLsizeiptr length = stats . efb_regions . size () * sizeof ( GLfloat ) * ( 2 + 3 ) * 2 * 6 ;
2012-12-07 23:34:38 +01:00
glBindBuffer ( GL_ARRAY_BUFFER , s_ShowEFBCopyRegions_VBO );
2014-03-09 21:14:26 +01:00
glBufferData ( GL_ARRAY_BUFFER , length , nullptr , GL_STREAM_DRAW );
2013-08-19 19:20:10 +02:00
GLfloat * Vertices = ( GLfloat * ) glMapBufferRange ( GL_ARRAY_BUFFER , 0 , length , GL_MAP_WRITE_BIT );
2010-09-28 02:15:02 +00:00
// Draw EFB copy regions rectangles
2012-10-27 18:10:31 -05:00
int a = 0 ;
2012-12-07 22:49:18 +01:00
GLfloat color [ 3 ] = { 0.0f , 1.0f , 1.0f };
2013-10-29 01:23:17 -04:00
2014-02-12 16:00:34 +01:00
for ( const EFBRectangle & rect : stats . efb_regions )
2010-09-28 02:15:02 +00:00
{
GLfloat halfWidth = EFB_WIDTH / 2.0f ;
GLfloat halfHeight = EFB_HEIGHT / 2.0f ;
2014-02-12 16:00:34 +01:00
GLfloat x = ( GLfloat ) - 1.0f + (( GLfloat ) rect . left / halfWidth );
GLfloat y = ( GLfloat ) 1.0f - (( GLfloat ) rect . top / halfHeight );
GLfloat x2 = ( GLfloat ) - 1.0f + (( GLfloat ) rect . right / halfWidth );
GLfloat y2 = ( GLfloat ) 1.0f - (( GLfloat ) rect . bottom / halfHeight );
2010-09-28 02:15:02 +00:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x ;
Vertices [ a ++ ] = y ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x2 ;
Vertices [ a ++ ] = y ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x2 ;
Vertices [ a ++ ] = y ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x2 ;
Vertices [ a ++ ] = y2 ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x2 ;
Vertices [ a ++ ] = y2 ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x ;
Vertices [ a ++ ] = y2 ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x ;
Vertices [ a ++ ] = y2 ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x ;
Vertices [ a ++ ] = y ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x ;
Vertices [ a ++ ] = y ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x2 ;
Vertices [ a ++ ] = y2 ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x2 ;
Vertices [ a ++ ] = y ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
Vertices [ a ++ ] = x ;
Vertices [ a ++ ] = y2 ;
Vertices [ a ++ ] = color [ 0 ];
Vertices [ a ++ ] = color [ 1 ];
Vertices [ a ++ ] = color [ 2 ];
2013-10-29 01:23:17 -04:00
2012-12-07 22:49:18 +01:00
// TO DO: build something nicer here
GLfloat temp = color [ 0 ];
color [ 0 ] = color [ 1 ];
color [ 1 ] = color [ 2 ];
color [ 2 ] = temp ;
2010-09-28 02:15:02 +00:00
}
2012-12-07 23:34:38 +01:00
glUnmapBuffer ( GL_ARRAY_BUFFER );
2013-10-29 01:23:17 -04:00
2013-02-13 13:12:19 +01:00
s_ShowEFBCopyRegions . Bind ();
2014-08-19 20:18:02 -07:00
glBindVertexArray ( s_ShowEFBCopyRegions_VAO );
GLsizei count = static_cast < GLsizei > ( stats . efb_regions . size () * 2 * 6 );
glDrawArrays ( GL_LINES , 0 , count );
2010-09-28 02:15:02 +00:00
// Clear stored regions
stats . efb_regions . clear ();
}
}
2014-03-12 15:33:41 -04:00
void Renderer :: RenderText ( const std :: string & text , int left , int top , u32 color )
2010-09-28 02:15:02 +00:00
{
2012-12-17 14:54:20 -06:00
const int nBackbufferWidth = ( int ) GLInterface -> GetBackBufferWidth ();
const int nBackbufferHeight = ( int ) GLInterface -> GetBackBufferHeight ();
2011-11-29 21:50:31 -06:00
2010-09-28 02:15:02 +00:00
s_pfont -> printMultilineText ( text ,
left * 2.0f / ( float ) nBackbufferWidth - 1 ,
1 - top * 2.0f / ( float ) nBackbufferHeight ,
2012-12-11 12:37:08 +01:00
0 , nBackbufferWidth , nBackbufferHeight , color );
2010-09-28 02:15:02 +00:00
}
2009-07-15 00:51:24 +00:00
TargetRectangle Renderer :: ConvertEFBRectangle ( const EFBRectangle & rc )
{
2010-09-30 15:24:34 +00:00
TargetRectangle result ;
2011-05-12 02:14:45 +00:00
result . left = EFBToScaledX ( rc . left );
result . top = EFBToScaledY ( EFB_HEIGHT - rc . top );
result . right = EFBToScaledX ( rc . right );
result . bottom = EFBToScaledY ( EFB_HEIGHT - rc . bottom );
2010-09-30 15:24:34 +00:00
return result ;
2009-02-23 07:23:34 +00:00
}
2009-06-07 12:06:15 +00:00
// Function: This function handles the OpenGL glScissor() function
2009-09-08 16:07:13 +00:00
// ----------------------------
2009-02-23 07:23:34 +00:00
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
// case 0x52 > SetScissorRect()
2009-09-08 16:07:13 +00:00
// ----------------------------
2009-06-22 09:31:30 +00:00
// bpmem.scissorTL.x, y = 342x342
// bpmem.scissorBR.x, y = 981x821
2009-02-23 07:23:34 +00:00
// Renderer::GetTargetHeight() = the fixed ini file setting
// donkopunchstania - it appears scissorBR is the bottom right pixel inside the scissor box
// therefore the width and height are (scissorBR + 1) - scissorTL
2014-02-04 21:17:38 +01:00
void Renderer :: SetScissorRect ( const EFBRectangle & rc )
2009-02-23 07:23:34 +00:00
{
2014-02-04 21:17:38 +01:00
TargetRectangle trc = g_renderer -> ConvertEFBRectangle ( rc );
glScissor ( trc . left , trc . bottom , trc . GetWidth (), trc . GetHeight ());
2009-02-23 07:23:34 +00:00
}
2010-09-28 02:15:02 +00:00
void Renderer :: SetColorMask ()
{
2010-12-20 16:57:29 +00:00
// Only enable alpha channel if it's supported by the current EFB format
2010-12-21 22:18:40 +00:00
GLenum ColorMask = GL_FALSE , AlphaMask = GL_FALSE ;
2013-01-08 17:23:01 +01:00
if ( bpmem . alpha_test . TestResult () != AlphaTest :: FAIL )
{
if ( bpmem . blendmode . colorupdate )
ColorMask = GL_TRUE ;
2014-03-23 21:44:23 +01:00
if ( bpmem . blendmode . alphaupdate && ( bpmem . zcontrol . pixel_format == PEControl :: RGBA6_Z24 ))
2013-01-08 17:23:01 +01:00
AlphaMask = GL_TRUE ;
}
2010-09-28 02:15:02 +00:00
glColorMask ( ColorMask , ColorMask , ColorMask , AlphaMask );
}
2011-08-22 06:15:02 +02:00
void ClearEFBCache ()
{
2014-06-05 10:12:05 +02:00
if ( ! s_efbCacheIsCleared )
{
s_efbCacheIsCleared = true ;
memset ( s_efbCacheValid , 0 , sizeof ( s_efbCacheValid ));
}
2011-08-22 06:15:02 +02:00
}
2015-06-06 20:09:22 +02:00
void Renderer :: UpdateEFBCache ( EFBAccessType type , u32 cacheRectIdx , const EFBRectangle & efbPixelRc , const TargetRectangle & targetPixelRc , const void * data )
2011-08-22 06:15:02 +02:00
{
u32 cacheType = ( type == PEEK_Z ? 0 : 1 );
if ( ! s_efbCache [ cacheType ][ cacheRectIdx ]. size ())
s_efbCache [ cacheType ][ cacheRectIdx ]. resize ( EFB_CACHE_RECT_SIZE * EFB_CACHE_RECT_SIZE );
u32 targetPixelRcWidth = targetPixelRc . right - targetPixelRc . left ;
2012-07-23 19:56:57 +02:00
u32 efbPixelRcHeight = efbPixelRc . bottom - efbPixelRc . top ;
u32 efbPixelRcWidth = efbPixelRc . right - efbPixelRc . left ;
2011-08-22 06:15:02 +02:00
2012-07-23 19:56:57 +02:00
for ( u32 yCache = 0 ; yCache < efbPixelRcHeight ; ++ yCache )
2011-08-22 06:15:02 +02:00
{
u32 yEFB = efbPixelRc . top + yCache ;
u32 yPixel = ( EFBToScaledY ( EFB_HEIGHT - yEFB ) + EFBToScaledY ( EFB_HEIGHT - yEFB - 1 )) / 2 ;
u32 yData = yPixel - targetPixelRc . bottom ;
2012-07-23 19:56:57 +02:00
for ( u32 xCache = 0 ; xCache < efbPixelRcWidth ; ++ xCache )
2011-08-22 06:15:02 +02:00
{
u32 xEFB = efbPixelRc . left + xCache ;
u32 xPixel = ( EFBToScaledX ( xEFB ) + EFBToScaledX ( xEFB + 1 )) / 2 ;
u32 xData = xPixel - targetPixelRc . left ;
2015-06-06 20:09:22 +02:00
u32 value ;
if ( type == PEEK_Z )
{
float * ptr = ( float * ) data ;
value = MathUtil :: Clamp < u32 > (( u32 )( ptr [ yData * targetPixelRcWidth + xData ] * 16777216.0f ), 0 , 0xFFFFFF );
}
else
{
u32 * ptr = ( u32 * ) data ;
value = ptr [ yData * targetPixelRcWidth + xData ];
}
s_efbCache [ cacheType ][ cacheRectIdx ][ yCache * EFB_CACHE_RECT_SIZE + xCache ] = value ;
2011-08-22 06:15:02 +02:00
}
}
s_efbCacheValid [ cacheType ][ cacheRectIdx ] = true ;
2014-06-05 10:12:05 +02:00
s_efbCacheIsCleared = false ;
2011-08-22 06:15:02 +02:00
}
2010-10-24 19:52:52 +00:00
// This function allows the CPU to directly access the EFB.
// There are EFB peeks (which will read the color or depth of a pixel)
// and EFB pokes (which will change the color or depth of a pixel).
//
// The behavior of EFB peeks can only be modified by:
2014-02-16 23:51:41 -05:00
// - GX_PokeAlphaRead
2010-10-24 19:52:52 +00:00
// The behavior of EFB pokes can be modified by:
2014-02-16 23:51:41 -05:00
// - GX_PokeAlphaMode (TODO)
// - GX_PokeAlphaUpdate (TODO)
// - GX_PokeBlendMode (TODO)
// - GX_PokeColorUpdate (TODO)
// - GX_PokeDither (TODO)
// - GX_PokeDstAlpha (TODO)
// - GX_PokeZMode (TODO)
2010-10-22 19:40:05 +00:00
u32 Renderer :: AccessEFB ( EFBAccessType type , u32 x , u32 y , u32 poke_data )
2010-09-28 02:15:02 +00:00
{
2011-12-01 02:40:58 +01:00
u32 cacheRectIdx = ( y / EFB_CACHE_RECT_SIZE ) * EFB_CACHE_WIDTH
+ ( x / EFB_CACHE_RECT_SIZE );
2011-08-22 06:15:02 +02:00
2010-09-28 02:15:02 +00:00
EFBRectangle efbPixelRc ;
2014-12-12 16:11:17 +01:00
if ( type == PEEK_COLOR || type == PEEK_Z )
{
// Get the rectangular target region containing the EFB pixel
efbPixelRc . left = ( x / EFB_CACHE_RECT_SIZE ) * EFB_CACHE_RECT_SIZE ;
efbPixelRc . top = ( y / EFB_CACHE_RECT_SIZE ) * EFB_CACHE_RECT_SIZE ;
efbPixelRc . right = std :: min ( efbPixelRc . left + EFB_CACHE_RECT_SIZE , ( u32 ) EFB_WIDTH );
efbPixelRc . bottom = std :: min ( efbPixelRc . top + EFB_CACHE_RECT_SIZE , ( u32 ) EFB_HEIGHT );
}
else
{
efbPixelRc . left = x ;
efbPixelRc . top = y ;
efbPixelRc . right = x + 1 ;
efbPixelRc . bottom = y + 1 ;
}
2010-09-28 02:15:02 +00:00
2010-09-30 15:24:34 +00:00
TargetRectangle targetPixelRc = ConvertEFBRectangle ( efbPixelRc );
2011-08-22 06:15:02 +02:00
u32 targetPixelRcWidth = targetPixelRc . right - targetPixelRc . left ;
u32 targetPixelRcHeight = targetPixelRc . top - targetPixelRc . bottom ;
2010-09-28 02:15:02 +00:00
// TODO (FIX) : currently, AA path is broken/offset and doesn't return the correct pixel
switch ( type )
{
case PEEK_Z :
{
2011-08-22 06:15:02 +02:00
if ( ! s_efbCacheValid [ 0 ][ cacheRectIdx ])
2010-09-28 02:15:02 +00:00
{
2011-08-22 06:15:02 +02:00
if ( s_MSAASamples > 1 )
{
2013-08-26 22:18:00 +02:00
g_renderer -> ResetAPIState ();
2013-10-29 01:23:17 -04:00
2011-08-22 06:15:02 +02:00
// Resolve our rectangle.
FramebufferManager :: GetEFBDepthTexture ( efbPixelRc );
2013-01-03 12:06:47 +01:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , FramebufferManager :: GetResolvedFramebuffer ());
2013-10-29 01:23:17 -04:00
2013-08-26 22:18:00 +02:00
g_renderer -> RestoreAPIState ();
2011-08-22 06:15:02 +02:00
}
2015-06-06 20:09:22 +02:00
std :: unique_ptr < float > depthMap ( new float [ targetPixelRcWidth * targetPixelRcHeight ]);
2011-08-22 06:15:02 +02:00
glReadPixels ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRcWidth , targetPixelRcHeight ,
2015-06-06 20:09:22 +02:00
GL_DEPTH_COMPONENT , GL_FLOAT , depthMap . get ());
2011-08-22 06:15:02 +02:00
2015-05-08 09:28:29 -04:00
UpdateEFBCache ( type , cacheRectIdx , efbPixelRc , targetPixelRc , depthMap . get ());
2010-09-28 02:15:02 +00:00
}
2011-08-22 06:15:02 +02:00
u32 xRect = x % EFB_CACHE_RECT_SIZE ;
u32 yRect = y % EFB_CACHE_RECT_SIZE ;
2015-05-08 09:29:36 -04:00
u32 z = s_efbCache [ 0 ][ cacheRectIdx ][ yRect * EFB_CACHE_RECT_SIZE + xRect ];
2010-09-28 02:15:02 +00:00
2015-06-06 20:09:22 +02:00
// if Z is in 16 bit format you must return a 16 bit integer
2014-03-23 21:44:23 +01:00
if ( bpmem . zcontrol . pixel_format == PEControl :: RGB565_Z16 )
2015-06-06 20:07:49 +02:00
z = z >> 8 ;
2015-06-06 20:09:22 +02:00
2015-06-06 20:07:49 +02:00
return z ;
2010-09-28 02:15:02 +00:00
}
case PEEK_COLOR : // GXPeekARGB
{
// Although it may sound strange, this really is A8R8G8B8 and not RGBA or 24-bit...
// Tested in Killer 7, the first 8bits represent the alpha value which is used to
// determine if we're aiming at an enemy (0x80 / 0x88) or not (0x70)
// Wind Waker is also using it for the pictograph to determine the color of each pixel
2011-08-22 06:15:02 +02:00
if ( ! s_efbCacheValid [ 1 ][ cacheRectIdx ])
2010-09-28 02:15:02 +00:00
{
2011-08-22 06:15:02 +02:00
if ( s_MSAASamples > 1 )
{
2013-08-26 22:18:00 +02:00
g_renderer -> ResetAPIState ();
2013-10-29 01:23:17 -04:00
2011-08-22 06:15:02 +02:00
// Resolve our rectangle.
FramebufferManager :: GetEFBColorTexture ( efbPixelRc );
2013-01-03 12:06:47 +01:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , FramebufferManager :: GetResolvedFramebuffer ());
2013-10-29 01:23:17 -04:00
2013-08-26 22:18:00 +02:00
g_renderer -> RestoreAPIState ();
2011-08-22 06:15:02 +02:00
}
2015-05-08 09:28:29 -04:00
std :: unique_ptr < u32 > colorMap ( new u32 [ targetPixelRcWidth * targetPixelRcHeight ]);
2011-08-22 06:15:02 +02:00
2013-12-30 07:22:50 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGLES3 )
2013-05-05 23:33:49 -05:00
// XXX: Swap colours
2013-12-30 07:22:50 -06:00
glReadPixels ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRcWidth , targetPixelRcHeight ,
2015-05-08 09:28:29 -04:00
GL_RGBA , GL_UNSIGNED_BYTE , colorMap . get ());
2013-12-30 07:22:50 -06:00
else
glReadPixels ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRcWidth , targetPixelRcHeight ,
2015-05-08 09:28:29 -04:00
GL_BGRA , GL_UNSIGNED_INT_8_8_8_8_REV , colorMap . get ());
2011-08-22 06:15:02 +02:00
2015-05-08 09:28:29 -04:00
UpdateEFBCache ( type , cacheRectIdx , efbPixelRc , targetPixelRc , colorMap . get ());
2010-09-28 02:15:02 +00:00
}
2011-08-22 06:15:02 +02:00
u32 xRect = x % EFB_CACHE_RECT_SIZE ;
u32 yRect = y % EFB_CACHE_RECT_SIZE ;
2015-05-08 09:29:36 -04:00
u32 color = s_efbCache [ 1 ][ cacheRectIdx ][ yRect * EFB_CACHE_RECT_SIZE + xRect ];
2010-09-28 02:15:02 +00:00
2010-10-24 19:52:52 +00:00
// check what to do with the alpha channel (GX_PokeAlphaRead)
2014-02-15 03:23:35 +01:00
PixelEngine :: UPEAlphaReadReg alpha_read_mode = PixelEngine :: GetAlphaReadMode ();
2010-12-27 03:18:01 +00:00
2014-03-23 21:44:23 +01:00
if ( bpmem . zcontrol . pixel_format == PEControl :: RGBA6_Z24 )
2010-12-27 03:18:01 +00:00
{
color = RGBA8ToRGBA6ToRGBA8 ( color );
}
2014-03-23 21:44:23 +01:00
else if ( bpmem . zcontrol . pixel_format == PEControl :: RGB565_Z16 )
2010-12-27 03:18:01 +00:00
{
2010-12-27 18:09:03 +00:00
color = RGBA8ToRGB565ToRGBA8 ( color );
}
2014-03-23 21:44:23 +01:00
if ( bpmem . zcontrol . pixel_format != PEControl :: RGBA6_Z24 )
2010-12-27 03:18:01 +00:00
{
color |= 0xFF000000 ;
}
2014-08-15 14:09:53 -04:00
if ( alpha_read_mode . ReadMode == 2 )
{
// GX_READ_NONE
return color ;
}
else if ( alpha_read_mode . ReadMode == 1 )
{
// GX_READ_FF
return ( color | 0xFF000000 );
}
else /*if(alpha_read_mode.ReadMode == 0)*/
{
// GX_READ_00
return ( color & 0x00FFFFFF );
}
2010-09-28 02:15:02 +00:00
}
case POKE_COLOR :
2015-05-29 09:55:45 +02:00
case POKE_Z :
2013-11-23 12:33:03 +01:00
{
2015-05-01 17:47:52 +02:00
std :: vector < EfbPokeData > vector ;
EfbPokeData d ;
d . x = x ;
d . y = y ;
d . data = poke_data ;
vector . push_back ( d );
PokeEFB ( type , vector );
2010-09-28 02:15:02 +00:00
break ;
2013-11-23 12:33:03 +01:00
}
2010-09-28 02:15:02 +00:00
default :
break ;
}
return 0 ;
}
2015-05-01 17:47:52 +02:00
void Renderer :: PokeEFB ( EFBAccessType type , const std :: vector < EfbPokeData >& data )
{
2015-05-29 09:55:45 +02:00
FramebufferManager :: PokeEFB ( type , data );
2015-05-01 17:47:52 +02:00
}
2014-11-13 23:26:49 +01:00
u16 Renderer :: BBoxRead ( int index )
{
int swapped_index = index ;
if ( index >= 2 )
swapped_index ^= 1 ; // swap 2 and 3 for top/bottom
// Here we get the min/max value of the truncated position of the upscaled and swapped framebuffer.
// So we have to correct them to the unscaled EFB sizes.
int value = BoundingBox :: Get ( swapped_index );
if ( index < 2 )
{
// left/right
value = value * EFB_WIDTH / s_target_width ;
}
else
{
// up/down -- we have to swap up and down
value = value * EFB_HEIGHT / s_target_height ;
value = EFB_HEIGHT - value - 1 ;
}
if ( index & 1 )
value ++ ; // fix max values to describe the outer border
return value ;
}
void Renderer :: BBoxWrite ( int index , u16 _value )
{
int value = _value ; // u16 isn't enough to multiply by the efb width
if ( index & 1 )
value -- ;
if ( index < 2 )
{
value = value * s_target_width / EFB_WIDTH ;
}
else
{
index ^= 1 ; // swap 2 and 3 for top/bottom
value = EFB_HEIGHT - value - 1 ;
value = value * s_target_height / EFB_HEIGHT ;
}
BoundingBox :: Set ( index , value );
}
2014-02-04 10:45:38 +01:00
void Renderer :: SetViewport ()
2010-09-28 02:15:02 +00:00
{
// reversed gxsetviewport(xorig, yorig, width, height, nearz, farz)
// [0] = width/2
// [1] = height/2
// [2] = 16777215 * (farz - nearz)
// [3] = xorig + width/2 + 342
// [4] = yorig + height/2 + 342
// [5] = 16777215 * farz
2010-09-30 15:24:34 +00:00
2011-05-12 02:14:45 +00:00
int scissorXOff = bpmem . scissorOffset . x * 2 ;
int scissorYOff = bpmem . scissorOffset . y * 2 ;
2010-09-30 15:24:34 +00:00
2010-12-10 15:54:14 +00:00
// TODO: ceil, floor or just cast to int?
2014-04-27 11:59:04 -07:00
float X = EFBToScaledXf ( xfmem . viewport . xOrig - xfmem . viewport . wd - ( float ) scissorXOff );
float Y = EFBToScaledYf (( float ) EFB_HEIGHT - xfmem . viewport . yOrig + xfmem . viewport . ht + ( float ) scissorYOff );
float Width = EFBToScaledXf ( 2.0f * xfmem . viewport . wd );
float Height = EFBToScaledYf ( - 2.0f * xfmem . viewport . ht );
2015-06-24 22:28:36 +02:00
float GLNear = MathUtil :: Clamp < float > ( xfmem . viewport . farZ - MathUtil :: Clamp < float > ( xfmem . viewport . zRange , - 16777215.0f , 16777215.0f ), 0.0f , 16777215.0f ) / 16777216.0f ;
2015-05-16 21:25:52 +02:00
float GLFar = MathUtil :: Clamp < float > ( xfmem . viewport . farZ , 0.0f , 16777215.0f ) / 16777216.0f ;
2010-09-30 15:24:34 +00:00
if ( Width < 0 )
2010-09-28 02:15:02 +00:00
{
2010-09-30 15:24:34 +00:00
X += Width ;
2010-12-10 15:54:14 +00:00
Width *= - 1 ;
2010-09-28 02:15:02 +00:00
}
2010-09-30 15:24:34 +00:00
if ( Height < 0 )
2010-09-28 02:15:02 +00:00
{
2010-09-30 15:24:34 +00:00
Y += Height ;
Height *= - 1 ;
2010-09-28 02:15:02 +00:00
}
2011-05-12 02:14:45 +00:00
2010-09-28 02:15:02 +00:00
// Update the view port
2014-03-11 00:30:55 +13:00
if ( g_ogl_config . bSupportViewportFloat )
2013-10-29 15:29:06 +01:00
{
glViewportIndexedf ( 0 , X , Y , Width , Height );
}
else
{
2014-08-19 20:18:02 -07:00
auto iceilf = []( float f )
{
return static_cast < GLint > ( ceilf ( f ));
};
glViewport ( iceilf ( X ), iceilf ( Y ), iceilf ( Width ), iceilf ( Height ));
2013-10-29 15:29:06 +01:00
}
2015-05-22 22:42:25 +02:00
glDepthRangef ( GLFar , GLNear );
2010-09-28 02:15:02 +00:00
}
2010-12-19 22:00:25 +00:00
void Renderer :: ClearScreen ( const EFBRectangle & rc , bool colorEnable , bool alphaEnable , bool zEnable , u32 color , u32 z )
2009-07-15 00:51:24 +00:00
{
2010-12-28 16:16:27 +00:00
ResetAPIState ();
2010-09-30 15:24:34 +00:00
2011-11-29 21:50:31 -06:00
// color
GLboolean const
color_mask = colorEnable ? GL_TRUE : GL_FALSE ,
alpha_mask = alphaEnable ? GL_TRUE : GL_FALSE ;
glColorMask ( color_mask , color_mask , color_mask , alpha_mask );
2009-07-15 00:51:24 +00:00
2011-11-29 21:50:31 -06:00
glClearColor (
float (( color >> 16 ) & 0xFF ) / 255.0f ,
float (( color >> 8 ) & 0xFF ) / 255.0f ,
float (( color >> 0 ) & 0xFF ) / 255.0f ,
float (( color >> 24 ) & 0xFF ) / 255.0f );
2009-07-15 00:51:24 +00:00
2011-11-29 21:50:31 -06:00
// depth
glDepthMask ( zEnable ? GL_TRUE : GL_FALSE );
2010-12-28 16:16:27 +00:00
2015-05-07 23:29:16 +02:00
glClearDepthf ( float ( z & 0xFFFFFF ) / 16777216.0f );
2011-11-29 21:50:31 -06:00
// Update rect for clearing the picture
glEnable ( GL_SCISSOR_TEST );
TargetRectangle const targetRc = ConvertEFBRectangle ( rc );
glScissor ( targetRc . left , targetRc . bottom , targetRc . GetWidth (), targetRc . GetHeight ());
// glColorMask/glDepthMask/glScissor affect glClear (glViewport does not)
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2010-12-28 16:16:27 +00:00
RestoreAPIState ();
2011-08-22 06:15:02 +02:00
ClearEFBCache ();
2009-07-15 00:51:24 +00:00
}
2010-08-10 01:08:22 +00:00
2014-12-20 17:10:58 +01:00
void Renderer :: BlitScreen ( TargetRectangle src , TargetRectangle dst , GLuint src_texture , int src_width , int src_height )
{
if ( g_ActiveConfig . iStereoMode == STEREO_SBS || g_ActiveConfig . iStereoMode == STEREO_TAB )
{
TargetRectangle leftRc , rightRc ;
2015-09-10 22:22:26 +02:00
// Top-and-Bottom mode needs to compensate for inverted vertical screen coordinates.
if ( g_ActiveConfig . iStereoMode == STEREO_TAB )
ConvertStereoRectangle ( dst , rightRc , leftRc );
else
ConvertStereoRectangle ( dst , leftRc , rightRc );
2014-12-20 17:10:58 +01:00
m_post_processor -> BlitFromTexture ( src , leftRc , src_texture , src_width , src_height , 0 );
m_post_processor -> BlitFromTexture ( src , rightRc , src_texture , src_width , src_height , 1 );
}
else
{
m_post_processor -> BlitFromTexture ( src , dst , src_texture , src_width , src_height );
}
}
2010-12-27 21:56:20 +00:00
void Renderer :: ReinterpretPixelData ( unsigned int convtype )
{
2013-07-22 15:41:10 +02:00
if ( convtype == 0 || convtype == 2 )
{
FramebufferManager :: ReinterpretPixelData ( convtype );
}
else
{
ERROR_LOG ( VIDEO , "Trying to reinterpret pixel data with unsupported conversion type %d" , convtype );
}
2010-12-27 21:56:20 +00:00
}
2010-09-28 02:15:02 +00:00
void Renderer :: SetBlendMode ( bool forceUpdate )
2009-06-26 08:57:53 +00:00
{
2013-01-13 23:35:07 +01:00
// Our render target always uses an alpha channel, so we need to override the blend functions to assume a destination alpha of 1 if the render target isn't supposed to have an alpha channel
// Example: D3DBLEND_DESTALPHA needs to be D3DBLEND_ONE since the result without an alpha channel is assumed to always be 1.
2014-03-23 21:44:23 +01:00
bool target_has_alpha = bpmem . zcontrol . pixel_format == PEControl :: RGBA6_Z24 ;
2013-10-29 01:23:17 -04:00
2015-09-09 21:20:46 +02:00
bool useDstAlpha = bpmem . dstalpha . enable && bpmem . blendmode . alphaupdate && target_has_alpha ;
2013-03-17 00:36:13 +01:00
bool useDualSource = useDstAlpha && g_ActiveConfig . backend_info . bSupportsDualSourceBlend ;
2013-10-29 01:23:17 -04:00
2013-01-13 23:35:07 +01:00
const GLenum glSrcFactors [ 8 ] =
{
GL_ZERO ,
GL_ONE ,
GL_DST_COLOR ,
GL_ONE_MINUS_DST_COLOR ,
2013-03-28 18:32:59 -03:00
( useDualSource ) ? GL_SRC1_ALPHA : ( GLenum ) GL_SRC_ALPHA ,
( useDualSource ) ? GL_ONE_MINUS_SRC1_ALPHA : ( GLenum ) GL_ONE_MINUS_SRC_ALPHA ,
2013-01-13 23:35:07 +01:00
( target_has_alpha ) ? GL_DST_ALPHA : ( GLenum ) GL_ONE ,
( target_has_alpha ) ? GL_ONE_MINUS_DST_ALPHA : ( GLenum ) GL_ZERO
};
const GLenum glDestFactors [ 8 ] =
{
GL_ZERO ,
GL_ONE ,
GL_SRC_COLOR ,
GL_ONE_MINUS_SRC_COLOR ,
2013-03-28 18:32:59 -03:00
( useDualSource ) ? GL_SRC1_ALPHA : ( GLenum ) GL_SRC_ALPHA ,
( useDualSource ) ? GL_ONE_MINUS_SRC1_ALPHA : ( GLenum ) GL_ONE_MINUS_SRC_ALPHA ,
2013-01-13 23:35:07 +01:00
( target_has_alpha ) ? GL_DST_ALPHA : ( GLenum ) GL_ONE ,
( target_has_alpha ) ? GL_ONE_MINUS_DST_ALPHA : ( GLenum ) GL_ZERO
};
2010-09-28 02:15:02 +00:00
// blend mode bit mask
// 0 - blend enable
2013-01-18 00:44:35 +01:00
// 1 - dst alpha enabled
2010-09-28 02:15:02 +00:00
// 2 - reverse subtract enable (else add)
// 3-5 - srcRGB function
// 6-8 - dstRGB function
2013-01-18 00:44:35 +01:00
u32 newval = useDualSource << 1 ;
newval |= bpmem . blendmode . subtract << 2 ;
2010-09-28 02:15:02 +00:00
if ( bpmem . blendmode . subtract )
2014-08-15 14:09:53 -04:00
{
2010-09-28 02:15:02 +00:00
newval |= 0x0049 ; // enable blending src 1 dst 1
2014-08-15 14:09:53 -04:00
}
2010-09-28 02:15:02 +00:00
else if ( bpmem . blendmode . blendenable )
2010-07-02 17:09:53 +00:00
{
2010-09-28 02:15:02 +00:00
newval |= 1 ; // enable blending
newval |= bpmem . blendmode . srcfactor << 3 ;
newval |= bpmem . blendmode . dstfactor << 6 ;
2009-08-31 17:47:17 +00:00
}
2010-09-28 02:15:02 +00:00
u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode ;
if ( changes & 1 )
2014-08-15 14:09:53 -04:00
{
2010-09-28 02:15:02 +00:00
// blend enable change
( newval & 1 ) ? glEnable ( GL_BLEND ) : glDisable ( GL_BLEND );
2014-08-15 14:09:53 -04:00
}
2010-09-28 02:15:02 +00:00
if ( changes & 4 )
2010-10-23 19:55:19 +00:00
{
2010-09-28 02:15:02 +00:00
// subtract enable change
2010-10-23 19:55:19 +00:00
GLenum equation = newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD ;
GLenum equationAlpha = useDualSource ? GL_FUNC_ADD : equation ;
2013-10-29 01:23:17 -04:00
2010-10-23 19:55:19 +00:00
glBlendEquationSeparate ( equation , equationAlpha );
}
2010-09-28 02:15:02 +00:00
2013-01-18 00:44:35 +01:00
if ( changes & 0x1FA )
2010-10-23 19:55:19 +00:00
{
2013-03-28 18:04:33 -03:00
u32 srcidx = ( newval >> 3 ) & 7 ;
u32 dstidx = ( newval >> 6 ) & 7 ;
GLenum srcFactor = glSrcFactors [ srcidx ];
GLenum dstFactor = glDestFactors [ dstidx ];
2013-03-28 22:59:42 +01:00
// adjust alpha factors
2010-10-23 19:55:19 +00:00
if ( useDualSource )
{
2014-03-24 20:21:34 +01:00
srcidx = BlendMode :: ONE ;
dstidx = BlendMode :: ZERO ;
2013-10-29 01:23:17 -04:00
}
2013-03-28 18:04:33 -03:00
else
{
// we can't use GL_DST_COLOR or GL_ONE_MINUS_DST_COLOR for source in alpha channel so use their alpha equivalent instead
2014-08-15 14:09:53 -04:00
if ( srcidx == BlendMode :: DSTCLR )
srcidx = BlendMode :: DSTALPHA ;
else if ( srcidx == BlendMode :: INVDSTCLR )
srcidx = BlendMode :: INVDSTALPHA ;
2013-03-28 22:59:42 +01:00
2013-03-28 18:04:33 -03:00
// we can't use GL_SRC_COLOR or GL_ONE_MINUS_SRC_COLOR for destination in alpha channel so use their alpha equivalent instead
2014-08-15 14:09:53 -04:00
if ( dstidx == BlendMode :: SRCCLR )
dstidx = BlendMode :: SRCALPHA ;
else if ( dstidx == BlendMode :: INVSRCCLR )
dstidx = BlendMode :: INVSRCALPHA ;
2013-10-29 01:23:17 -04:00
}
2013-03-28 18:04:33 -03:00
GLenum srcFactorAlpha = glSrcFactors [ srcidx ];
GLenum dstFactorAlpha = glDestFactors [ dstidx ];
2010-09-28 02:15:02 +00:00
// blend RGB change
2010-10-23 19:55:19 +00:00
glBlendFuncSeparate ( srcFactor , dstFactor , srcFactorAlpha , dstFactorAlpha );
}
2010-09-28 02:15:02 +00:00
s_blendMode = newval ;
2009-06-26 08:57:53 +00:00
}
2009-06-07 12:06:15 +00:00
2014-07-08 14:29:26 +02:00
static void DumpFrame ( const std :: vector < u8 >& data , int w , int h )
2013-02-26 20:47:48 -05:00
{
2015-01-26 02:35:29 +01:00
#if defined(HAVE_LIBAV) || defined(_WIN32)
2014-10-12 23:53:10 -04:00
if ( SConfig :: GetInstance (). m_DumpFrames && ! data . empty ())
{
AVIDump :: AddFrame ( & data [ 0 ], w , h );
}
2013-02-26 20:47:48 -05:00
#endif
}
2009-06-29 07:30:48 +00:00
// This function has the final picture. We adjust the aspect ratio here.
2014-05-02 00:08:44 -07:00
void Renderer :: SwapImpl ( u32 xfbAddr , u32 fbWidth , u32 fbStride , u32 fbHeight , const EFBRectangle & rc , float Gamma )
2009-02-28 16:33:59 +00:00
{
2014-12-28 21:46:00 +01:00
if ( g_ogl_config . bSupportsDebug )
{
2015-09-10 18:27:31 +12:00
if ( LogManager :: GetInstance () -> IsEnabled ( LogTypes :: HOST_GPU , LogTypes :: LERROR ))
2014-12-28 21:46:00 +01:00
glEnable ( GL_DEBUG_OUTPUT );
else
glDisable ( GL_DEBUG_OUTPUT );
}
2011-02-11 18:59:42 +00:00
static int w = 0 , h = 0 ;
2012-09-28 23:19:50 +02:00
if ( g_bSkipCurrentFrame || ( ! XFBWrited && ! g_ActiveConfig . RealXFBEnabled ()) || ! fbWidth || ! fbHeight )
2010-01-28 14:37:03 +00:00
{
2013-02-26 20:47:48 -05:00
DumpFrame ( frame_data , w , h );
2011-01-31 01:28:32 +00:00
Core :: Callback_VideoCopiedToXFB ( false );
2009-08-08 01:39:56 +00:00
return ;
2010-06-05 01:38:22 +00:00
}
2010-09-28 02:15:02 +00:00
2010-03-10 06:45:13 +00:00
u32 xfbCount = 0 ;
2014-10-02 02:20:46 -04:00
const XFBSourceBase * const * xfbSourceList = FramebufferManager :: GetXFBSource ( xfbAddr , fbStride , fbHeight , & xfbCount );
2012-09-28 23:19:50 +02:00
if ( g_ActiveConfig . VirtualXFBEnabled () && ( ! xfbSourceList || xfbCount == 0 ))
2009-06-29 07:30:48 +00:00
{
2013-02-26 20:47:48 -05:00
DumpFrame ( frame_data , w , h );
2011-01-31 01:28:32 +00:00
Core :: Callback_VideoCopiedToXFB ( false );
2009-06-29 07:30:48 +00:00
return ;
}
2010-06-05 01:38:22 +00:00
ResetAPIState ();
2010-11-18 02:21:26 +00:00
2012-09-29 00:04:55 +02:00
UpdateDrawRectangle ( s_backbuffer_width , s_backbuffer_height );
TargetRectangle flipped_trc = GetTargetRectangle ();
2014-10-29 19:50:39 -05:00
// Flip top and bottom for some reason; TODO: Fix the code to suck less?
std :: swap ( flipped_trc . top , flipped_trc . bottom );
2013-10-29 01:23:17 -04:00
2013-01-03 20:44:47 +01:00
// Copy the framebuffer to screen.
2014-10-18 09:32:24 +02:00
const XFBSource * xfbSource = nullptr ;
2010-03-10 06:45:13 +00:00
2014-03-11 00:30:55 +13:00
if ( g_ActiveConfig . bUseXFB )
2009-03-08 19:19:51 +00:00
{
2010-07-02 17:09:53 +00:00
// draw each xfb source
for ( u32 i = 0 ; i < xfbCount ; ++ i )
2010-03-10 06:45:13 +00:00
{
2014-10-18 09:32:24 +02:00
xfbSource = ( const XFBSource * ) xfbSourceList [ i ];
2010-03-10 06:45:13 +00:00
2014-10-18 09:32:24 +02:00
TargetRectangle drawRc ;
2015-07-25 01:46:41 +08:00
TargetRectangle sourceRc ;
sourceRc . left = xfbSource -> sourceRc . left ;
sourceRc . right = xfbSource -> sourceRc . right ;
sourceRc . top = xfbSource -> sourceRc . top ;
sourceRc . bottom = xfbSource -> sourceRc . bottom ;
2010-03-10 06:45:13 +00:00
2012-09-28 23:21:09 +02:00
if ( g_ActiveConfig . bUseRealXFB )
{
2014-10-18 09:32:24 +02:00
drawRc = flipped_trc ;
2015-07-25 01:46:41 +08:00
sourceRc . right -= fbStride - fbWidth ;
2012-09-28 23:21:09 +02:00
}
else
2010-07-02 17:09:53 +00:00
{
// use virtual xfb with offset
int xfbHeight = xfbSource -> srcHeight ;
int xfbWidth = xfbSource -> srcWidth ;
2014-05-02 00:08:44 -07:00
int hOffset = (( s32 ) xfbSource -> srcAddr - ( s32 ) xfbAddr ) / (( s32 ) fbStride * 2 );
2013-10-29 01:23:17 -04:00
2015-01-20 02:31:32 +10:30
drawRc . top = flipped_trc . top - hOffset * flipped_trc . GetHeight () / ( s32 ) fbHeight ;
drawRc . bottom = flipped_trc . top - ( hOffset + xfbHeight ) * flipped_trc . GetHeight () / ( s32 ) fbHeight ;
drawRc . left = flipped_trc . left + ( flipped_trc . GetWidth () - xfbWidth * flipped_trc . GetWidth () / ( s32 ) fbStride ) / 2 ;
drawRc . right = flipped_trc . left + ( flipped_trc . GetWidth () + xfbWidth * flipped_trc . GetWidth () / ( s32 ) fbStride ) / 2 ;
2013-10-29 01:23:17 -04:00
2010-09-30 15:24:34 +00:00
// The following code disables auto stretch. Kept for reference.
// scale draw area for a 1 to 1 pixel mapping with the draw target
2012-09-29 00:04:55 +02:00
//float vScale = (float)fbHeight / (float)flipped_trc.GetHeight();
//float hScale = (float)fbWidth / (float)flipped_trc.GetWidth();
2010-09-30 15:24:34 +00:00
//drawRc.top *= vScale;
//drawRc.bottom *= vScale;
//drawRc.left *= hScale;
//drawRc.right *= hScale;
2015-07-25 01:46:41 +08:00
sourceRc . right -= Renderer :: EFBToScaledX ( fbStride - fbWidth );
2010-07-02 17:09:53 +00:00
}
// Tell the OSD Menu about the current internal resolution
OSDInternalW = xfbSource -> sourceRc . GetWidth (); OSDInternalH = xfbSource -> sourceRc . GetHeight ();
2014-12-20 17:10:58 +01:00
BlitScreen ( sourceRc , drawRc , xfbSource -> texture , xfbSource -> texWidth , xfbSource -> texHeight );
2010-07-02 17:09:53 +00:00
}
}
else
{
2010-09-30 15:24:34 +00:00
TargetRectangle targetRc = ConvertEFBRectangle ( rc );
2013-10-29 01:23:17 -04:00
2013-01-09 01:50:52 +01:00
// for msaa mode, we must resolve the efb content to non-msaa
2014-10-18 09:32:24 +02:00
GLuint tex = FramebufferManager :: ResolveAndGetRenderTarget ( rc );
2014-12-20 17:10:58 +01:00
BlitScreen ( targetRc , flipped_trc , tex , s_target_width , s_target_height );
2009-03-08 19:19:51 +00:00
}
2013-10-29 01:23:17 -04:00
2013-01-03 21:40:54 +01:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 );
2010-09-30 15:24:34 +00:00
2009-06-08 00:44:48 +00:00
// Save screenshot
if ( s_bScreenshot )
2009-06-07 11:51:53 +00:00
{
2013-11-10 00:10:20 +01:00
std :: lock_guard < std :: mutex > lk ( s_criticalScreenshot );
2015-06-24 21:44:38 -04:00
if ( SaveScreenshot ( s_sScreenshotName , flipped_trc ))
OSD :: AddMessage ( "Screenshot saved to " + s_sScreenshotName );
2009-06-07 11:51:53 +00:00
// Reset settings
2013-11-10 00:10:20 +01:00
s_sScreenshotName . clear ();
s_bScreenshot = false ;
2015-06-18 21:28:40 -04:00
s_screenshotCompleted . Set ();
2009-06-07 11:51:53 +00:00
}
2009-03-28 21:07:16 +00:00
// Frame dumps are handled a little differently in Windows
2013-05-06 06:43:04 -05:00
// Frame dumping disabled entirely on GLES3
2014-01-01 20:06:11 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGL )
2009-06-08 10:16:08 +00:00
{
2015-01-26 02:35:29 +01:00
#if defined _WIN32 || defined HAVE_LIBAV
2014-10-12 23:53:10 -04:00
if ( SConfig :: GetInstance (). m_DumpFrames )
2011-02-11 18:59:42 +00:00
{
2014-01-01 20:06:11 -06:00
std :: lock_guard < std :: mutex > lk ( s_criticalScreenshot );
if ( frame_data . empty () || w != flipped_trc . GetWidth () ||
h != flipped_trc . GetHeight ())
2009-04-03 14:35:49 +00:00
{
2014-01-01 20:06:11 -06:00
w = flipped_trc . GetWidth ();
h = flipped_trc . GetHeight ();
frame_data . resize ( 3 * w * h );
}
glPixelStorei ( GL_PACK_ALIGNMENT , 1 );
glReadPixels ( flipped_trc . left , flipped_trc . bottom , w , h , GL_BGR , GL_UNSIGNED_BYTE , & frame_data [ 0 ]);
2014-10-26 04:53:22 -04:00
if ( w > 0 && h > 0 )
2014-01-01 20:06:11 -06:00
{
if ( ! bLastFrameDumped )
2009-04-03 14:35:49 +00:00
{
2015-01-26 02:35:29 +01:00
#ifdef _WIN32
bAVIDumping = AVIDump :: Start ( nullptr , w , h );
#else
bAVIDumping = AVIDump :: Start ( w , h );
#endif
2014-01-01 20:06:11 -06:00
if ( ! bAVIDumping )
2014-08-15 14:09:53 -04:00
{
2014-01-01 20:06:11 -06:00
OSD :: AddMessage ( "AVIDump Start failed" , 2000 );
2014-08-15 14:09:53 -04:00
}
2014-01-01 20:06:11 -06:00
else
{
OSD :: AddMessage ( StringFromFormat (
"Dumping Frames to \" %sframedump0.avi \" (%dx%d RGB24)" ,
2014-03-12 15:33:41 -04:00
File :: GetUserPath ( D_DUMPFRAMES_IDX ). c_str (), w , h ), 2000 );
2014-01-01 20:06:11 -06:00
}
2009-03-28 21:07:16 +00:00
}
2014-01-01 20:06:11 -06:00
if ( bAVIDumping )
{
2015-01-26 02:35:29 +01:00
#ifndef _WIN32
FlipImageData ( & frame_data [ 0 ], w , h );
#endif
AVIDump :: AddFrame ( & frame_data [ 0 ], w , h );
2014-01-01 20:06:11 -06:00
}
2009-04-03 14:35:49 +00:00
2014-01-01 20:06:11 -06:00
bLastFrameDumped = true ;
}
else
2014-08-15 14:09:53 -04:00
{
2014-01-01 20:06:11 -06:00
NOTICE_LOG ( VIDEO , "Error reading framebuffer" );
2014-08-15 14:09:53 -04:00
}
2009-03-28 21:07:16 +00:00
}
2009-06-08 10:16:08 +00:00
else
2009-04-03 14:35:49 +00:00
{
2014-01-01 20:06:11 -06:00
if ( bLastFrameDumped && bAVIDumping )
{
std :: vector < u8 > (). swap ( frame_data );
w = h = 0 ;
AVIDump :: Stop ();
bAVIDumping = false ;
OSD :: AddMessage ( "Stop dumping frames" , 2000 );
}
bLastFrameDumped = false ;
2015-01-26 02:35:29 +01:00
}
#else
if ( SConfig :: GetInstance (). m_DumpFrames )
{
std :: lock_guard < std :: mutex > lk ( s_criticalScreenshot );
std :: string movie_file_name ;
w = GetTargetRectangle (). GetWidth ();
h = GetTargetRectangle (). GetHeight ();
frame_data . resize ( 3 * w * h );
glPixelStorei ( GL_PACK_ALIGNMENT , 1 );
glReadPixels ( GetTargetRectangle (). left , GetTargetRectangle (). bottom , w , h , GL_BGR , GL_UNSIGNED_BYTE , & frame_data [ 0 ]);
if ( ! bLastFrameDumped )
{
movie_file_name = File :: GetUserPath ( D_DUMPFRAMES_IDX ) + "framedump.raw" ;
File :: CreateFullPath ( movie_file_name );
pFrameDump . Open ( movie_file_name , "wb" );
if ( ! pFrameDump )
{
OSD :: AddMessage ( "Error opening framedump.raw for writing." , 2000 );
}
else
{
OSD :: AddMessage ( StringFromFormat ( "Dumping Frames to \" %s \" (%dx%d RGB24)" , movie_file_name . c_str (), w , h ), 2000 );
}
}
if ( pFrameDump )
{
FlipImageData ( & frame_data [ 0 ], w , h );
pFrameDump . WriteBytes ( & frame_data [ 0 ], w * 3 * h );
pFrameDump . Flush ();
}
bLastFrameDumped = true ;
}
else
{
if ( bLastFrameDumped )
pFrameDump . Close ();
bLastFrameDumped = false ;
2014-01-01 20:06:11 -06:00
}
2013-05-06 06:43:04 -05:00
#endif
2014-01-01 20:06:11 -06:00
}
2010-09-28 02:15:02 +00:00
// Finish up the current frame, print some stats
2011-01-07 04:57:59 +00:00
2014-05-02 00:08:44 -07:00
SetWindowSize ( fbStride , fbHeight );
2011-01-07 04:57:59 +00:00
2012-12-17 14:54:20 -06:00
GLInterface -> Update (); // just updates the render window position and the backbuffer size
2011-05-07 22:00:05 +00:00
2014-12-28 18:26:25 +01:00
bool xfbchanged = s_last_xfb_mode != g_ActiveConfig . bUseRealXFB ;
2010-09-28 02:15:02 +00:00
2014-05-02 00:08:44 -07:00
if ( FramebufferManagerBase :: LastXfbWidth () != fbStride || FramebufferManagerBase :: LastXfbHeight () != fbHeight )
2010-05-19 03:15:36 +00:00
{
xfbchanged = true ;
2014-05-02 00:08:44 -07:00
unsigned int const last_w = ( fbStride < 1 || fbStride > MAX_XFB_WIDTH ) ? MAX_XFB_WIDTH : fbStride ;
2013-01-29 16:40:15 -06:00
unsigned int const last_h = ( fbHeight < 1 || fbHeight > MAX_XFB_HEIGHT ) ? MAX_XFB_HEIGHT : fbHeight ;
FramebufferManagerBase :: SetLastXfbWidth ( last_w );
FramebufferManagerBase :: SetLastXfbHeight ( last_h );
2010-05-19 03:15:36 +00:00
}
2010-09-28 02:15:02 +00:00
2010-05-19 03:15:36 +00:00
bool WindowResized = false ;
2012-12-17 14:54:20 -06:00
int W = ( int ) GLInterface -> GetBackBufferWidth ();
int H = ( int ) GLInterface -> GetBackBufferHeight ();
2014-12-28 18:35:23 +01:00
if ( W != s_backbuffer_width || H != s_backbuffer_height || s_last_efb_scale != g_ActiveConfig . iEFBScale )
2010-05-19 03:15:36 +00:00
{
WindowResized = true ;
2010-09-28 02:15:02 +00:00
s_backbuffer_width = W ;
s_backbuffer_height = H ;
2014-12-28 18:35:23 +01:00
s_last_efb_scale = g_ActiveConfig . iEFBScale ;
2010-05-19 03:15:36 +00:00
}
2015-07-20 20:12:29 -04:00
bool TargetSizeChanged = false ;
if ( CalculateTargetSize ( s_backbuffer_width , s_backbuffer_height ))
{
TargetSizeChanged = true ;
}
2015-09-06 13:58:18 +02:00
if ( TargetSizeChanged || xfbchanged || WindowResized ||
2015-09-01 00:17:24 -05:00
( s_last_multisample_mode != g_ActiveConfig . iMultisampleMode ) || ( s_last_stereo_mode != ( g_ActiveConfig . iStereoMode > 0 )))
2010-06-05 01:38:22 +00:00
{
2014-12-28 18:26:25 +01:00
s_last_xfb_mode = g_ActiveConfig . bUseRealXFB ;
2012-09-29 00:04:55 +02:00
UpdateDrawRectangle ( s_backbuffer_width , s_backbuffer_height );
2010-05-19 03:15:36 +00:00
2015-09-06 13:58:18 +02:00
if ( TargetSizeChanged ||
2015-09-01 00:17:24 -05:00
s_last_multisample_mode != g_ActiveConfig . iMultisampleMode || s_last_stereo_mode != ( g_ActiveConfig . iStereoMode > 0 ))
2010-09-30 15:24:34 +00:00
{
2015-01-03 01:35:39 +01:00
s_last_stereo_mode = g_ActiveConfig . iStereoMode > 0 ;
2014-12-28 18:35:23 +01:00
s_last_multisample_mode = g_ActiveConfig . iMultisampleMode ;
s_MSAASamples = GetNumMSAASamples ( s_last_multisample_mode );
2013-10-29 01:23:17 -04:00
2010-11-14 23:31:53 +00:00
delete g_framebuffer_manager ;
g_framebuffer_manager = new FramebufferManager ( s_target_width , s_target_height ,
2014-04-30 18:13:31 +02:00
s_MSAASamples );
2015-01-03 06:06:56 +13:00
PixelShaderManager :: SetEfbScaleChanged ();
2010-05-19 03:15:36 +00:00
}
}
2009-06-07 12:06:15 +00:00
// ---------------------------------------------------------------------
2015-09-04 23:45:35 -05:00
glEnable ( GL_BLEND );
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
2009-06-07 12:06:15 +00:00
2015-09-04 23:45:35 -05:00
// Reset viewport for drawing text
glViewport ( 0 , 0 , GLInterface -> GetBackBufferWidth (), GLInterface -> GetBackBufferHeight ());
2014-12-20 13:02:00 +01:00
2015-09-04 23:45:35 -05:00
ShowEfbCopyRegions ();
DrawDebugText ();
// Do our OSD callbacks
OSD :: DoCallbacks ( OSD :: OSD_ONFRAME );
OSD :: DrawMessages ();
2010-08-10 01:08:22 +00:00
2010-06-05 01:38:22 +00:00
// Copy the rendered frame to the real window
2012-12-26 00:34:09 -06:00
GLInterface -> Swap ();
2010-08-10 01:08:22 +00:00
2009-06-08 20:07:20 +00:00
// Clear framebuffer
2015-09-04 23:45:35 -05:00
glClearColor ( 0 , 0 , 0 , 0 );
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2009-06-07 12:06:15 +00:00
2014-03-11 00:30:55 +13:00
if ( s_vsync != g_ActiveConfig . IsVSync ())
2013-03-30 22:17:39 +01:00
{
s_vsync = g_ActiveConfig . IsVSync ();
2015-09-19 06:12:20 +12:00
if ( ! DriverDetails :: HasBug ( DriverDetails :: BUG_BROKENVSYNC ))
GLInterface -> SwapInterval ( s_vsync );
2013-03-30 22:17:39 +01:00
}
2013-03-18 20:41:45 -04:00
2010-01-17 17:44:09 +00:00
// Clean out old stuff from caches. It's not worth it to clean out the shader caches.
2014-05-19 18:31:38 +02:00
TextureCache :: Cleanup ( frameCount );
2009-06-07 12:06:15 +00:00
// Render to the framebuffer.
2010-11-14 23:31:53 +00:00
FramebufferManager :: SetFramebuffer ( 0 );
2009-06-07 12:06:15 +00:00
2010-06-05 01:38:22 +00:00
RestoreAPIState ();
2009-09-13 17:46:33 +00:00
2010-06-05 01:38:22 +00:00
g_Config . iSaveTargetId = 0 ;
2009-09-13 17:46:33 +00:00
2010-12-28 12:46:00 +00:00
UpdateActiveConfig ();
2012-05-28 11:31:37 +02:00
TextureCache :: OnConfigChanged ( g_ActiveConfig );
2010-12-28 12:46:00 +00:00
2009-09-13 17:46:33 +00:00
// For testing zbuffer targets.
2010-06-05 01:38:22 +00:00
// Renderer::SetZBufferRender();
2013-11-16 15:59:59 +13:00
// SaveTexture("tex.png", GL_TEXTURE_2D, s_FakeZTarget,
2010-11-18 02:21:26 +00:00
// GetTargetWidth(), GetTargetHeight());
2011-08-22 06:15:02 +02:00
// Invalidate EFB cache
ClearEFBCache ();
2009-09-13 17:46:33 +00:00
}
2009-06-07 12:06:15 +00:00
2010-09-28 02:15:02 +00:00
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
void Renderer :: ResetAPIState ()
2009-02-23 07:23:34 +00:00
{
2010-09-28 02:15:02 +00:00
// Gets us to a reasonably sane state where it's possible to do things like
// image copies with textured quads, etc.
glDisable ( GL_SCISSOR_TEST );
glDisable ( GL_DEPTH_TEST );
glDisable ( GL_CULL_FACE );
glDisable ( GL_BLEND );
2013-12-30 07:22:50 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGL )
glDisable ( GL_COLOR_LOGIC_OP );
2010-09-28 02:15:02 +00:00
glDepthMask ( GL_FALSE );
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE );
}
2009-04-03 14:35:49 +00:00
2010-09-28 02:15:02 +00:00
void Renderer :: RestoreAPIState ()
{
// Gets us back into a more game-like state.
glEnable ( GL_SCISSOR_TEST );
2010-12-28 16:16:27 +00:00
SetGenerationMode ();
2011-09-05 22:04:28 +02:00
BPFunctions :: SetScissor ();
2010-09-28 02:15:02 +00:00
SetColorMask ();
2010-12-28 16:16:27 +00:00
SetDepthMode ();
2010-09-28 02:15:02 +00:00
SetBlendMode ( true );
2013-09-09 23:21:56 +02:00
SetLogicOpMode ();
2014-02-04 10:45:38 +01:00
SetViewport ();
2010-09-28 02:15:02 +00:00
2012-12-15 14:43:01 +01:00
VertexManager * vm = ( OGL :: VertexManager * ) g_vertex_manager ;
2012-12-15 17:28:58 +01:00
glBindBuffer ( GL_ARRAY_BUFFER , vm -> m_vertex_buffers );
2014-09-03 00:08:50 -04:00
if ( vm -> m_last_vao )
glBindVertexArray ( vm -> m_last_vao );
2013-10-29 01:23:17 -04:00
2013-01-19 00:39:31 +01:00
TextureCache :: SetStage ();
2010-09-28 02:15:02 +00:00
}
void Renderer :: SetGenerationMode ()
{
// none, ccw, cw, ccw
if ( bpmem . genMode . cullmode > 0 )
2009-03-16 07:54:44 +00:00
{
2013-12-30 20:37:59 +01:00
// TODO: GX_CULL_ALL not supported, yet!
2010-09-28 02:15:02 +00:00
glEnable ( GL_CULL_FACE );
glFrontFace ( bpmem . genMode . cullmode == 2 ? GL_CCW : GL_CW );
2009-03-16 07:54:44 +00:00
}
2010-09-28 02:15:02 +00:00
else
2014-08-15 14:09:53 -04:00
{
2010-09-28 02:15:02 +00:00
glDisable ( GL_CULL_FACE );
2014-08-15 14:09:53 -04:00
}
2010-09-28 02:15:02 +00:00
}
2009-03-16 07:54:44 +00:00
2010-09-28 02:15:02 +00:00
void Renderer :: SetDepthMode ()
{
2013-01-13 23:35:07 +01:00
const GLenum glCmpFuncs [ 8 ] =
{
GL_NEVER ,
GL_LESS ,
GL_EQUAL ,
GL_LEQUAL ,
GL_GREATER ,
GL_NOTEQUAL ,
GL_GEQUAL ,
GL_ALWAYS
};
2010-09-28 02:15:02 +00:00
if ( bpmem . zmode . testenable )
2009-09-13 08:21:35 +00:00
{
2010-09-28 02:15:02 +00:00
glEnable ( GL_DEPTH_TEST );
glDepthMask ( bpmem . zmode . updateenable ? GL_TRUE : GL_FALSE );
glDepthFunc ( glCmpFuncs [ bpmem . zmode . func ]);
2009-09-13 08:21:35 +00:00
}
2010-09-28 02:15:02 +00:00
else
2009-06-08 02:48:38 +00:00
{
2010-09-28 02:15:02 +00:00
// if the test is disabled write is disabled too
2013-02-18 14:54:24 +00:00
// TODO: When PE performance metrics are being emulated via occlusion queries, we should (probably?) enable depth test with depth function ALWAYS here
2010-09-28 02:15:02 +00:00
glDisable ( GL_DEPTH_TEST );
glDepthMask ( GL_FALSE );
2009-06-08 02:48:38 +00:00
}
2009-02-28 16:33:59 +00:00
}
2010-08-10 01:08:22 +00:00
2010-09-28 02:15:02 +00:00
void Renderer :: SetLogicOpMode ()
2009-02-23 07:23:34 +00:00
{
2014-01-01 20:06:11 -06:00
if ( GLInterface -> GetMode () != GLInterfaceMode :: MODE_OPENGL )
return ;
2013-05-05 23:33:49 -05:00
// Logic ops aren't available in GLES3/GLES2
2013-01-13 23:35:07 +01:00
const GLenum glLogicOpCodes [ 16 ] =
{
GL_CLEAR ,
GL_AND ,
GL_AND_REVERSE ,
GL_COPY ,
GL_AND_INVERTED ,
GL_NOOP ,
GL_XOR ,
GL_OR ,
GL_NOR ,
GL_EQUIV ,
GL_INVERT ,
GL_OR_REVERSE ,
GL_COPY_INVERTED ,
GL_OR_INVERTED ,
GL_NAND ,
GL_SET
};
2014-12-15 20:08:54 -08:00
if ( bpmem . blendmode . logicopenable && ! bpmem . blendmode . blendenable )
2010-09-28 02:15:02 +00:00
{
glEnable ( GL_COLOR_LOGIC_OP );
glLogicOp ( glLogicOpCodes [ bpmem . blendmode . logicmode ]);
}
else
{
glDisable ( GL_COLOR_LOGIC_OP );
}
}
void Renderer :: SetDitherMode ()
{
if ( bpmem . blendmode . dither )
glEnable ( GL_DITHER );
else
glDisable ( GL_DITHER );
}
2015-03-01 13:04:48 +01:00
void Renderer :: SetSamplerState ( int stage , int texindex , bool custom_tex )
2010-09-28 02:15:02 +00:00
{
2013-02-19 18:22:38 -06:00
auto const & tex = bpmem . tex [ texindex ];
auto const & tm0 = tex . texMode0 [ stage ];
auto const & tm1 = tex . texMode1 [ stage ];
2013-10-29 01:23:17 -04:00
2015-03-01 13:04:48 +01:00
g_sampler_cache -> SetSamplerState (( texindex * 4 ) + stage , tm0 , tm1 , custom_tex );
2010-09-28 02:15:02 +00:00
}
void Renderer :: SetInterlacingMode ()
{
// TODO
2009-02-23 07:23:34 +00:00
}
2013-11-15 13:00:38 +13:00
void Renderer :: FlipImageData ( u8 * data , int w , int h , int pixel_width )
2010-09-28 02:15:02 +00:00
{
2013-11-10 00:10:20 +01:00
// Flip image upside down. Damn OpenGL.
2013-11-15 13:00:38 +13:00
for ( int y = 0 ; y < h / 2 ; ++ y )
2010-09-28 02:15:02 +00:00
{
2014-03-11 00:30:55 +13:00
for ( int x = 0 ; x < w ; ++ x )
2010-09-28 02:15:02 +00:00
{
2014-02-12 16:00:34 +01:00
for ( int delta = 0 ; delta < pixel_width ; ++ delta )
2013-11-15 13:00:38 +13:00
std :: swap ( data [( y * w + x ) * pixel_width + delta ], data [(( h - 1 - y ) * w + x ) * pixel_width + delta ]);
2010-09-28 02:15:02 +00:00
}
}
}
2013-11-10 00:10:20 +01:00
}
namespace OGL
{
bool Renderer :: SaveScreenshot ( const std :: string & filename , const TargetRectangle & back_rc )
2010-07-09 20:56:16 +00:00
{
u32 W = back_rc . GetWidth ();
u32 H = back_rc . GetHeight ();
2014-09-25 22:47:34 -05:00
std :: unique_ptr < u8 [] > data ( new u8 [ W * 4 * H ]);
2009-02-27 03:56:34 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 1 );
2010-08-10 01:08:22 +00:00
2014-09-25 22:47:34 -05:00
glReadPixels ( back_rc . left , back_rc . bottom , W , H , GL_RGBA , GL_UNSIGNED_BYTE , data . get ());
2010-08-10 01:08:22 +00:00
2009-06-06 13:36:33 +00:00
// Turn image upside down
2014-09-25 22:47:34 -05:00
FlipImageData ( data . get (), W , H , 4 );
2013-11-17 10:34:34 +13:00
2014-09-25 22:47:34 -05:00
return TextureToPng ( data . get (), W * 4 , filename , W , H , false );
2013-11-17 10:34:34 +13:00
2009-03-28 21:07:16 +00:00
}
2010-11-18 02:21:26 +00:00
2014-09-25 19:50:25 -04:00
int Renderer :: GetMaxTextureSize ()
{
int max_size ;
glGetIntegerv ( GL_MAX_TEXTURE_SIZE , & max_size );
return max_size ;
}
2010-12-20 17:06:39 +00:00
}