2013-04-17 23:29:41 -04:00
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
2009-02-23 07:23:34 +00:00
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>
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"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
2014-02-19 12:14:09 +01:00
#include "Common/Timer.h"
2014-02-17 05:18:15 -05:00
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/Movie.h"
#include "VideoBackends/OGL/FramebufferManager.h"
#include "VideoBackends/OGL/GLUtil.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/FPSCounter.h"
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PixelEngine.h"
#include "VideoCommon/Statistics.h"
#include "VideoCommon/VertexLoader.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
2013-11-10 00:10:20 +01:00
#if defined(HAVE_WX) && HAVE_WX
2014-02-17 05:18:15 -05:00
#include "DolphinWX/WxUtils.h"
2013-11-10 00:10:20 +01:00
#endif
2009-02-23 07:23:34 +00:00
#ifdef _WIN32
#include <mmsystem.h>
#endif
#ifdef _WIN32
2010-11-14 21:14:26 +00:00
#endif
2011-02-11 21:30:53 +00:00
#if defined _WIN32 || defined HAVE_LIBAV
2014-02-17 05:18:15 -05:00
#include "VideoCommon/AVIDump.h"
2011-02-11 21:30:53 +00:00
#endif
2009-02-23 07:23:34 +00:00
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
enum MultisampleMode {
MULTISAMPLE_OFF ,
MULTISAMPLE_2X ,
MULTISAMPLE_4X ,
MULTISAMPLE_8X ,
MULTISAMPLE_SSAA_4X ,
};
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 ;
2011-05-07 22:00:05 +00:00
static int s_LastMultisampleMode = 0 ;
2009-03-05 23:11:13 +00: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 ;
2013-11-10 00:10:20 +01:00
#if defined(HAVE_WX) && HAVE_WX
static std :: thread scrshotThread ;
#endif
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-25 15:45:10 +01:00
case MULTISAMPLE_SSAA_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-03-11 00:30:55 +13: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 ApplySSAASettings () {
2013-05-05 23:33:49 -05:00
// GLES3 doesn't support SSAA
2013-12-30 07:22:50 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGL )
{
2014-03-11 00:30:55 +13:00
if ( g_ActiveConfig . iMultisampleMode == MULTISAMPLE_SSAA_4X ) {
if ( g_ogl_config . bSupportSampleShading ) {
2013-12-30 07:22:50 -06:00
glEnable ( GL_SAMPLE_SHADING_ARB );
glMinSampleShadingARB ( s_MSAASamples );
} else {
// TODO: move this to InitBackendInfo
OSD :: AddMessage ( "SSAA Anti Aliasing isn't supported by your GPU." , 10000 );
}
2014-03-11 00:30:55 +13:00
} else if ( g_ogl_config . bSupportSampleShading ) {
2013-12-30 07:22:50 -06:00
glDisable ( GL_SAMPLE_SHADING_ARB );
2013-03-25 15:45:10 +01:00
}
}
}
2014-07-13 02:16:51 +02:00
#if defined(_DEBUG) || defined(DEBUGFAST)
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
{
case GL_DEBUG_SEVERITY_HIGH_ARB : ERROR_LOG ( VIDEO , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
case GL_DEBUG_SEVERITY_MEDIUM_ARB : WARN_LOG ( VIDEO , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
case GL_DEBUG_SEVERITY_LOW_ARB : WARN_LOG ( VIDEO , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
default : ERROR_LOG ( VIDEO , "id: %x, source: %s, type: %s - %s" , id , s_source , s_type , message ); break ;
}
}
2014-07-13 02:16:51 +02:00
#endif
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" )
2013-10-29 01:23:17 -04:00
vendor = DriverDetails :: VENDOR_NVIDIA ;
2013-08-21 05:34:42 -05:00
else if ( svendor == "ATI Technologies Inc." || svendor == "Advanced Micro Devices, Inc." )
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_ATI ;
2013-08-23 10:52:29 +02:00
else if ( std :: string :: npos != sversion . find ( "Mesa" ))
vendor = DriverDetails :: VENDOR_MESA ;
2013-06-11 08:33:56 -05:00
else if ( std :: string :: npos != svendor . find ( "Intel" ))
vendor = DriverDetails :: VENDOR_INTEL ;
else if ( svendor == "ARM" )
vendor = DriverDetails :: VENDOR_ARM ;
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" )
vendor = DriverDetails :: VENDOR_QUALCOMM ;
else if ( svendor == "Imagination Technologies" )
vendor = DriverDetails :: VENDOR_IMGTEC ;
2013-07-02 21:58:09 -04:00
else if ( svendor == "NVIDIA Corporation" && srenderer == "NVIDIA Tegra" )
2013-06-11 08:33:56 -05:00
vendor = DriverDetails :: VENDOR_TEGRA ;
else if ( svendor == "Vivante Corporation" )
vendor = DriverDetails :: VENDOR_VIVANTE ;
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 :
{
if ( std :: string :: npos != srenderer . find ( "Adreno (TM) 3" ))
2013-08-23 10:52:29 +02:00
driver = DriverDetails :: DRIVER_QUALCOMM_3XX ;
2013-06-11 08:33:56 -05:00
else
2013-08-23 10:52:29 +02:00
driver = DriverDetails :: DRIVER_QUALCOMM_2XX ;
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.
if ( std :: string :: npos != srenderer . find ( "Mali-T" ))
2014-04-11 23:38:40 -05:00
{
2014-04-18 21:06:32 -05:00
driver = DriverDetails :: DRIVER_ARM_MIDGARD ;
2014-04-11 23:38:40 -05: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.
if ( GLExtensions :: Supports ( "GL_EXT_shader_pixel_local_storage" ))
version = 400 ;
else
version = 300 ;
}
2014-04-18 21:06:32 -05:00
else if ( std :: string :: npos != srenderer . find ( "Mali-4" ) ||
std :: string :: npos != srenderer . find ( "Mali-3" ) ||
std :: string :: npos != srenderer . find ( "Mali-2" ))
2014-04-11 23:38:40 -05:00
{
2014-04-18 21:06:32 -05:00
driver = DriverDetails :: DRIVER_ARM_UTGARD ;
2014-04-11 23:38:40 -05:00
}
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 ;
sscanf ( g_ogl_config . gl_version , "%*s Mesa %d.%d.%d" , & major , & minor , & release );
version = 100 * major + 10 * minor + release ;
}
2013-07-25 05:44:01 +00:00
break ;
2013-12-30 18:26:55 -06:00
case DriverDetails :: VENDOR_INTEL : // Happens in OS X
sscanf ( g_ogl_config . gl_renderer , "Intel HD Graphics %d" , & family );
/*
int glmajor = 0;
int glminor = 0;
int major = 0;
int minor = 0;
int release = 0;
sscanf(g_ogl_config.gl_version, "%d.%d INTEL-%d.%d.%d", &glmajor, &glminor, &major, &minor, &release);
version = 10000*major + 1000*minor + release;
*/
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 ;
2014-01-28 15:15:26 +01: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 ;
}
2013-12-30 07:22:50 -06:00
if ( ! GLExtensions :: Supports ( "GL_ARB_sampler_objects" ) && bSuccess )
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.
PanicAlert ( "GPU: OGL ERROR: Need GL_ARB_sampler_objects."
"GPU: Does your video card support OpenGL 3.3?"
2013-03-17 10:06:16 +01:00
"Please report this issue, then there will be a workaround" );
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
2013-12-30 07:22:50 -06:00
g_Config . backend_info . bSupportsDualSourceBlend = GLExtensions :: Supports ( "GL_ARB_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" ));
2013-12-30 07:22:50 -06:00
g_Config . backend_info . bSupportsEarlyZ = GLExtensions :: Supports ( "GL_ARB_shader_image_load_store" );
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" );
g_ogl_config . bSupportsGLBaseVertex = GLExtensions :: Supports ( "GL_ARB_draw_elements_base_vertex" );
g_ogl_config . bSupportsGLBufferStorage = GLExtensions :: Supports ( "GL_ARB_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 . bSupportSampleShading = GLExtensions :: Supports ( "GL_ARB_sample_shading" );
g_ogl_config . bSupportOGL31 = GLExtensions :: Version () >= 310 ;
g_ogl_config . bSupportViewportFloat = GLExtensions :: Supports ( "GL_ARB_viewport_array" );
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
{
2014-06-06 21:21:35 -05:00
if ( strstr ( g_ogl_config . glsl_version , "3.0" ))
2014-03-30 13:58:05 -05:00
{
g_ogl_config . eSupportedGLSLVersion = GLSLES_300 ;
}
else
{
g_ogl_config . eSupportedGLSLVersion = GLSLES_310 ;
g_Config . backend_info . bSupportsBindingLayout = true ;
}
}
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 ;
g_Config . backend_info . bSupportsEarlyZ = false ; // layout keyword is only supported on glsl150+
}
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 ;
g_Config . backend_info . bSupportsEarlyZ = false ; // layout keyword is only supported on glsl150+
}
else
{
g_ogl_config . eSupportedGLSLVersion = GLSL_150 ;
}
}
#if defined(_DEBUG) || defined(DEBUGFAST)
if ( GLExtensions :: Supports ( "GL_KHR_debug" ))
{
2014-03-09 21:14:26 +01:00
glDebugMessageControl ( GL_DONT_CARE , GL_DONT_CARE , GL_DONT_CARE , 0 , nullptr , true );
glDebugMessageCallback ( ErrorCallback , nullptr );
2013-12-30 15:33:53 -06:00
glEnable ( GL_DEBUG_OUTPUT );
2013-12-30 07:22:50 -06:00
}
else if ( GLExtensions :: Supports ( "GL_ARB_debug_output" ))
{
2014-03-09 21:14:26 +01:00
glDebugMessageControlARB ( GL_DONT_CARE , GL_DONT_CARE , GL_DONT_CARE , 0 , nullptr , true );
glDebugMessageCallbackARB ( ErrorCallback , nullptr );
2013-12-30 07:22:50 -06:00
glEnable ( GL_DEBUG_OUTPUT );
2013-04-08 14:50:58 +02:00
}
2013-09-02 13:14:45 +02:00
#endif
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 "
"Please avoid forcing dolphin to use MSAA by the driver. \n "
"%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
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
2014-04-30 18:48:40 +02:00
WARN_LOG ( VIDEO , "Missing OGL Extensions: %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 " ,
2013-03-25 15:45:10 +01:00
g_ogl_config . bSupportSampleShading ? "" : "SSAA "
2013-04-04 18:55:37 +02:00
);
2013-10-29 01:23:17 -04:00
2011-05-07 22:00:05 +00:00
s_LastMultisampleMode = g_ActiveConfig . iMultisampleMode ;
s_MSAASamples = GetNumMSAASamples ( s_LastMultisampleMode );
2013-03-25 15:45:10 +01:00
ApplySSAASettings ();
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 ();
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
2010-09-30 15:24:34 +00:00
s_LastEFBScale = 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
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
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 )
glEnable ( GL_PRIMITIVE_RESTART_FIXED_INDEX );
2013-04-10 12:58:52 +02:00
else
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 );
}
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 ()
{
2013-11-10 00:10:20 +01:00
#if defined(HAVE_WX) && HAVE_WX
if ( scrshotThread . joinable ())
scrshotThread . join ();
#endif
2013-01-25 20:39:19 +01:00
}
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 ();
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
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
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
2010-11-18 02:21:26 +00:00
void Renderer :: DrawDebugInfo ()
2010-09-28 02:15:02 +00:00
{
// Reset viewport for drawing text
2012-12-17 14:54:20 -06:00
glViewport ( 0 , 0 , GLInterface -> GetBackBufferWidth (), GLInterface -> GetBackBufferHeight ());
2014-05-25 20:52:58 -04:00
2010-09-28 02:15:02 +00:00
// Draw various messages on the screen, like FPS, statistics, etc.
2014-05-25 20:52:58 -04:00
std :: string debug_info ;
2010-09-28 02:15:02 +00:00
if ( g_ActiveConfig . bShowFPS )
2014-07-13 13:04:25 +02:00
debug_info += StringFromFormat ( "FPS: %d \n " , m_fps_counter . m_fps );
2010-09-28 02:15:02 +00:00
2012-11-14 16:21:54 -05:00
if ( SConfig :: GetInstance (). m_ShowLag )
2014-05-25 20:52:58 -04:00
debug_info += StringFromFormat ( "Lag: %" PRIu64 " \n " , Movie :: g_currentLagCount );
2012-11-11 17:57:06 -05:00
2011-02-17 09:12:36 +00:00
if ( g_ActiveConfig . bShowInputDisplay )
2014-05-25 20:52:58 -04:00
debug_info += Movie :: GetInputDisplay ();
2011-02-17 09:12:36 +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
2013-08-19 19:20:10 +02:00
u32 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 ();
2012-12-09 22:49:58 +01:00
glBindVertexArray ( s_ShowEFBCopyRegions_VAO );
2012-12-07 22:49:18 +01:00
glDrawArrays ( GL_LINES , 0 , stats . efb_regions . size () * 2 * 6 );
2010-09-28 02:15:02 +00:00
// Restore Line Size
2013-03-25 15:14:24 +01:00
SetLineWidth ();
2010-09-28 02:15:02 +00:00
// Clear stored regions
stats . efb_regions . clear ();
}
if ( g_ActiveConfig . bOverlayStats )
2014-05-25 20:52:58 -04:00
debug_info += Statistics :: ToString ();
2010-09-28 02:15:02 +00:00
if ( g_ActiveConfig . bOverlayProjStats )
2014-05-25 20:52:58 -04:00
debug_info += Statistics :: ToStringProj ();
2010-09-28 02:15:02 +00:00
2014-05-25 20:52:58 -04:00
if ( ! debug_info . empty ())
2010-09-28 02:15:02 +00:00
{
2014-05-25 20:52:58 -04:00
// Render a shadow, and then the text.
Renderer :: RenderText ( debug_info , 21 , 21 , 0xDD000000 );
Renderer :: RenderText ( debug_info , 20 , 20 , 0xFF00FFFF );
2010-09-28 02:15:02 +00:00
}
}
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-11-18 02:21:26 +00:00
2010-09-28 02:15:02 +00:00
GL_REPORT_ERRORD ();
}
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
}
void Renderer :: UpdateEFBCache ( EFBAccessType type , u32 cacheRectIdx , const EFBRectangle & efbPixelRc , const TargetRectangle & targetPixelRc , const u32 * data )
{
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 ;
s_efbCache [ cacheType ][ cacheRectIdx ][ yCache * EFB_CACHE_RECT_SIZE + xCache ] = data [ yData * targetPixelRcWidth + xData ];
}
}
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
// Get the rectangular target region containing the EFB pixel
2010-09-28 02:15:02 +00:00
EFBRectangle efbPixelRc ;
2011-08-22 06:15:02 +02:00
efbPixelRc . left = ( x / EFB_CACHE_RECT_SIZE ) * EFB_CACHE_RECT_SIZE ;
efbPixelRc . top = ( y / EFB_CACHE_RECT_SIZE ) * EFB_CACHE_RECT_SIZE ;
2012-07-23 19:56:57 +02:00
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 );
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
u32 z ;
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
}
u32 * depthMap = new u32 [ targetPixelRcWidth * targetPixelRcHeight ];
glReadPixels ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRcWidth , targetPixelRcHeight ,
GL_DEPTH_COMPONENT , GL_UNSIGNED_INT , depthMap );
GL_REPORT_ERRORD ();
UpdateEFBCache ( type , cacheRectIdx , efbPixelRc , targetPixelRc , depthMap );
delete [] depthMap ;
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 ;
z = s_efbCache [ 0 ][ cacheRectIdx ][ yRect * EFB_CACHE_RECT_SIZE + xRect ];
2010-09-28 02:15:02 +00:00
// Scale the 32-bit value returned by glReadPixels to a 24-bit
// value (GC uses a 24-bit Z-buffer).
// TODO: in RE0 this value is often off by one, which causes lighting to disappear
2014-03-23 21:44:23 +01:00
if ( bpmem . zcontrol . pixel_format == PEControl :: RGB565_Z16 )
2010-12-27 03:18:01 +00:00
{
2010-12-27 18:09:03 +00:00
// if Z is in 16 bit format you must return a 16 bit integer
2010-12-27 03:18:01 +00:00
z = z >> 16 ;
}
else
{
z = z >> 8 ;
}
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
u32 color ;
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
}
u32 * colorMap = new u32 [ targetPixelRcWidth * targetPixelRcHeight ];
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 ,
GL_RGBA , GL_UNSIGNED_BYTE , colorMap );
else
glReadPixels ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRcWidth , targetPixelRcHeight ,
GL_BGRA , GL_UNSIGNED_INT_8_8_8_8_REV , colorMap );
2011-08-22 06:15:02 +02:00
GL_REPORT_ERRORD ();
UpdateEFBCache ( type , cacheRectIdx , efbPixelRc , targetPixelRc , colorMap );
delete [] colorMap ;
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 ;
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-03-11 00:30:55 +13:00
if ( alpha_read_mode . ReadMode == 2 ) return color ; // GX_READ_NONE
else if ( alpha_read_mode . ReadMode == 1 ) return ( color | 0xFF000000 ); // GX_READ_FF
2010-10-24 19:52:52 +00:00
else /*if(alpha_read_mode.ReadMode == 0)*/ return ( color & 0x00FFFFFF ); // GX_READ_00
2010-09-28 02:15:02 +00:00
}
case POKE_COLOR :
2013-11-23 12:33:03 +01:00
{
ResetAPIState ();
glClearColor ( float (( poke_data >> 16 ) & 0xFF ) / 255.0f ,
float (( poke_data >> 8 ) & 0xFF ) / 255.0f ,
float (( poke_data >> 0 ) & 0xFF ) / 255.0f ,
float (( poke_data >> 24 ) & 0xFF ) / 255.0f );
glEnable ( GL_SCISSOR_TEST );
glScissor ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRc . GetWidth (), targetPixelRc . GetHeight ());
glClear ( GL_COLOR_BUFFER_BIT );
RestoreAPIState ();
// TODO: Could just update the EFB cache with the new value
ClearEFBCache ();
2010-09-28 02:15:02 +00:00
break ;
2013-11-23 12:33:03 +01:00
}
case POKE_Z :
{
ResetAPIState ();
glDepthMask ( GL_TRUE );
glClearDepthf ( float ( poke_data & 0xFFFFFF ) / float ( 0xFFFFFF ));
glEnable ( GL_SCISSOR_TEST );
glScissor ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRc . GetWidth (), targetPixelRc . GetHeight ());
glClear ( GL_DEPTH_BUFFER_BIT );
RestoreAPIState ();
// TODO: Could just update the EFB cache with the new value
ClearEFBCache ();
break ;
}
2010-09-28 02:15:02 +00:00
default :
break ;
}
return 0 ;
}
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 );
float GLNear = ( xfmem . viewport . farZ - xfmem . viewport . zRange ) / 16777216.0f ;
float GLFar = xfmem . viewport . farZ / 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
{
2013-10-29 21:42:40 +01:00
glViewport ( ceil ( X ), ceil ( Y ), ceil ( Width ), ceil ( Height ));
2013-10-29 15:29:06 +01:00
}
2013-05-05 23:33:49 -05:00
glDepthRangef ( GLNear , GLFar );
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
2013-05-05 23:33:49 -05:00
glClearDepthf ( float ( z & 0xFFFFFF ) / float ( 0xFFFFFF ));
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
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
2013-03-17 00:36:13 +01:00
bool useDstAlpha = ! g_ActiveConfig . bDstAlphaPass && bpmem . dstalpha . enable && bpmem . blendmode . alphaupdate && target_has_alpha ;
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 )
newval |= 0x0049 ; // enable blending src 1 dst 1
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 )
// blend enable change
( newval & 1 ) ? glEnable ( GL_BLEND ) : glDisable ( GL_BLEND );
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-03-24 20:21:34 +01:00
if ( srcidx == BlendMode :: DSTCLR ) srcidx = BlendMode :: DSTALPHA ;
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-03-24 20:21:34 +01:00
if ( dstidx == BlendMode :: SRCCLR ) dstidx = BlendMode :: SRCALPHA ;
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
{
#if defined(HAVE_LIBAV) || defined(_WIN32)
if ( g_ActiveConfig . bDumpFrames && ! data . empty ())
{
AVIDump :: AddFrame ( & data [ 0 ], w , h );
}
#endif
}
2009-06-29 07:30:48 +00:00
// This function has the final picture. We adjust the aspect ratio here.
2014-02-05 11:48:45 +01:00
void Renderer :: SwapImpl ( u32 xfbAddr , u32 fbWidth , u32 fbHeight , const EFBRectangle & rc , float Gamma )
2009-02-28 16:33:59 +00:00
{
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 ;
2010-11-14 23:31:53 +00:00
const XFBSourceBase * const * xfbSourceList = FramebufferManager :: GetXFBSource ( xfbAddr , fbWidth , 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
2013-03-07 17:00:11 +01:00
PostProcessing :: Update ( s_backbuffer_width , s_backbuffer_height );
2012-09-29 00:04:55 +02:00
UpdateDrawRectangle ( s_backbuffer_width , s_backbuffer_height );
TargetRectangle flipped_trc = GetTargetRectangle ();
2014-01-29 16:15:10 -06:00
if ( DriverDetails :: HasBug ( DriverDetails :: BUG_ROTATEDFRAMEBUFFER ))
{
std :: swap ( flipped_trc . left , flipped_trc . right );
}
else
{
// 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
2009-06-26 08:57:53 +00:00
GL_REPORT_ERRORD ();
2013-01-03 20:44:47 +01:00
// Copy the framebuffer to screen.
2010-03-10 06:45:13 +00:00
2014-03-09 21:14:26 +01:00
const XFBSourceBase * 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
{
2013-03-07 17:00:11 +01:00
// Render to the real/postprocessing buffer now.
PostProcessing :: BindTargetFramebuffer ();
2013-10-29 01:23:17 -04:00
2010-07-02 17:09:53 +00:00
// draw each xfb source
2013-01-03 20:44:47 +01:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , FramebufferManager :: GetXFBFramebuffer ());
2010-07-12 19:30:25 +00:00
2010-07-02 17:09:53 +00:00
for ( u32 i = 0 ; i < xfbCount ; ++ i )
2010-03-10 06:45:13 +00:00
{
2010-07-02 17:09:53 +00:00
xfbSource = xfbSourceList [ i ];
2010-03-10 06:45:13 +00:00
2010-07-02 17:09:53 +00:00
MathUtil :: Rectangle < float > drawRc ;
2010-03-10 06:45:13 +00:00
2012-09-28 23:21:09 +02:00
if ( g_ActiveConfig . bUseRealXFB )
{
2013-01-03 20:44:47 +01:00
drawRc . top = flipped_trc . top ;
drawRc . bottom = flipped_trc . bottom ;
drawRc . left = flipped_trc . left ;
drawRc . right = flipped_trc . right ;
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 ;
int hOffset = (( s32 ) xfbSource -> srcAddr - ( s32 ) xfbAddr ) / (( s32 ) fbWidth * 2 );
2013-10-29 01:23:17 -04:00
2013-03-25 23:23:32 +01:00
drawRc . top = flipped_trc . top - hOffset * flipped_trc . GetHeight () / fbHeight ;
drawRc . bottom = flipped_trc . top - ( hOffset + xfbHeight ) * flipped_trc . GetHeight () / fbHeight ;
2013-01-03 20:44:47 +01:00
drawRc . left = flipped_trc . left + ( flipped_trc . GetWidth () - xfbWidth * flipped_trc . GetWidth () / fbWidth ) / 2 ;
drawRc . right = flipped_trc . left + ( flipped_trc . GetWidth () + xfbWidth * flipped_trc . GetWidth () / fbWidth ) / 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;
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-01-26 13:21:22 +00:00
MathUtil :: Rectangle < int > sourceRc ;
2010-11-14 23:31:53 +00:00
sourceRc . left = xfbSource -> sourceRc . left ;
sourceRc . right = xfbSource -> sourceRc . right ;
sourceRc . top = xfbSource -> sourceRc . top ;
sourceRc . bottom = xfbSource -> sourceRc . bottom ;
2013-11-24 11:33:43 +13:00
xfbSource -> Draw ( sourceRc , drawRc );
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
FramebufferManager :: ResolveAndGetRenderTarget ( rc );
2013-10-29 01:23:17 -04:00
2013-03-07 17:00:11 +01:00
// Render to the real/postprocessing buffer now. (resolve have changed this in msaa mode)
PostProcessing :: BindTargetFramebuffer ();
2013-10-29 01:23:17 -04:00
2013-01-09 01:50:52 +01:00
// always the non-msaa fbo
GLuint fb = s_MSAASamples > 1 ? FramebufferManager :: GetResolvedFramebuffer () : FramebufferManager :: GetEFBFramebuffer ();
2013-10-29 01:23:17 -04:00
2013-01-09 01:50:52 +01:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , fb );
2013-01-03 20:44:47 +01:00
glBlitFramebuffer ( targetRc . left , targetRc . bottom , targetRc . right , targetRc . top ,
flipped_trc . left , flipped_trc . bottom , flipped_trc . right , flipped_trc . top ,
GL_COLOR_BUFFER_BIT , GL_LINEAR );
2009-03-08 19:19:51 +00:00
}
2013-10-29 01:23:17 -04:00
2013-03-07 17:00:11 +01:00
PostProcessing :: BlitToScreen ();
2010-09-30 15:24:34 +00: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 );
SaveScreenshot ( s_sScreenshotName , flipped_trc );
2009-06-07 11:51:53 +00:00
// Reset settings
2013-11-10 00:10:20 +01:00
s_sScreenshotName . clear ();
s_bScreenshot = false ;
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
{
2014-01-01 20:06:11 -06:00
#if defined _WIN32 || defined HAVE_LIBAV
if ( g_ActiveConfig . bDumpFrames )
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 ]);
if ( GL_REPORT_ERROR () == GL_NO_ERROR && w > 0 && h > 0 )
{
if ( ! bLastFrameDumped )
2009-04-03 14:35:49 +00:00
{
2014-01-01 20:06:11 -06:00
#ifdef _WIN32
2014-06-15 00:23:43 +02:00
bAVIDumping = AVIDump :: Start (( HWND )(( cInterfaceWGL * ) GLInterface ) -> m_window_handle , w , h );
2014-01-01 20:06:11 -06:00
#else
bAVIDumping = AVIDump :: Start ( w , h );
#endif
if ( ! bAVIDumping )
OSD :: AddMessage ( "AVIDump Start failed" , 2000 );
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 )
{
#ifndef _WIN32
FlipImageData ( & frame_data [ 0 ], w , h );
#endif
2013-10-29 01:23:17 -04:00
2014-01-01 20:06:11 -06:00
AVIDump :: AddFrame ( & frame_data [ 0 ], w , h );
}
2009-04-03 14:35:49 +00:00
2014-01-01 20:06:11 -06:00
bLastFrameDumped = true ;
}
else
NOTICE_LOG ( VIDEO , "Error reading framebuffer" );
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 ;
2009-03-28 21:07:16 +00:00
}
#else
2014-01-01 20:06:11 -06:00
if ( g_ActiveConfig . bDumpFrames )
2010-08-10 01:08:22 +00:00
{
2014-01-01 20:06:11 -06:00
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 ( GL_REPORT_ERROR () == GL_NO_ERROR )
2010-08-10 01:08:22 +00:00
{
2014-01-01 20:06:11 -06:00
if ( ! bLastFrameDumped )
2010-08-10 01:08:22 +00:00
{
2014-01-01 20:06:11 -06:00
movie_file_name = File :: GetUserPath ( D_DUMPFRAMES_IDX ) + "framedump.raw" ;
pFrameDump . Open ( movie_file_name , "wb" );
if ( ! pFrameDump )
OSD :: AddMessage ( "Error opening framedump.raw for writing." , 2000 );
else
{
2014-03-12 15:33:41 -04:00
OSD :: AddMessage ( StringFromFormat ( "Dumping Frames to \" %s \" (%dx%d RGB24)" , movie_file_name . 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 ( pFrameDump )
{
FlipImageData ( & frame_data [ 0 ], w , h );
pFrameDump . WriteBytes ( & frame_data [ 0 ], w * 3 * h );
pFrameDump . Flush ();
}
bLastFrameDumped = true ;
2009-03-28 21:07:16 +00:00
}
}
2014-01-01 20:06:11 -06:00
else
{
if ( bLastFrameDumped )
pFrameDump . Close ();
bLastFrameDumped = false ;
}
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
2011-01-25 03:30:12 +00:00
SetWindowSize ( fbWidth , 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
2010-05-19 03:15:36 +00:00
bool xfbchanged = false ;
2010-09-28 02:15:02 +00:00
2012-09-28 23:48:18 +02:00
if ( FramebufferManagerBase :: LastXfbWidth () != fbWidth || FramebufferManagerBase :: LastXfbHeight () != fbHeight )
2010-05-19 03:15:36 +00:00
{
xfbchanged = true ;
2013-01-29 16:40:15 -06:00
unsigned int const last_w = ( fbWidth < 1 || fbWidth > MAX_XFB_WIDTH ) ? MAX_XFB_WIDTH : fbWidth ;
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 ();
2010-09-30 15:24:34 +00:00
if ( W != s_backbuffer_width || H != s_backbuffer_height || s_LastEFBScale != 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 ;
2010-09-30 15:24:34 +00:00
s_LastEFBScale = g_ActiveConfig . iEFBScale ;
2010-05-19 03:15:36 +00:00
}
2011-05-07 22:00:05 +00:00
if ( xfbchanged || WindowResized || ( s_LastMultisampleMode != g_ActiveConfig . iMultisampleMode ))
2010-06-05 01:38:22 +00:00
{
2012-09-29 00:04:55 +02:00
UpdateDrawRectangle ( s_backbuffer_width , s_backbuffer_height );
2010-05-19 03:15:36 +00:00
2012-09-29 00:19:28 +02:00
if ( CalculateTargetSize ( s_backbuffer_width , s_backbuffer_height ) || s_LastMultisampleMode != g_ActiveConfig . iMultisampleMode )
2010-09-30 15:24:34 +00:00
{
2011-05-07 22:00:05 +00:00
s_LastMultisampleMode = g_ActiveConfig . iMultisampleMode ;
s_MSAASamples = GetNumMSAASamples ( s_LastMultisampleMode );
2013-03-25 15:45:10 +01:00
ApplySSAASettings ();
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 );
2010-05-19 03:15:36 +00:00
}
}
2009-06-07 12:06:15 +00:00
// ---------------------------------------------------------------------
2013-09-18 02:36:48 -05:00
if ( ! DriverDetails :: HasBug ( DriverDetails :: BUG_BROKENSWAP ))
{
GL_REPORT_ERRORD ();
2010-08-10 01:08:22 +00:00
2013-09-18 02:36:48 -05:00
glEnable ( GL_BLEND );
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
2009-06-07 12:06:15 +00:00
2013-09-18 02:36:48 -05:00
DrawDebugInfo ();
DrawDebugText ();
2010-08-10 01:08:22 +00:00
2013-09-18 02:36:48 -05:00
GL_REPORT_ERRORD ();
// Do our OSD callbacks
OSD :: DoCallbacks ( OSD :: OSD_ONFRAME );
OSD :: DrawMessages ();
GL_REPORT_ERRORD ();
}
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-13 22:08:01 +00:00
GL_REPORT_ERRORD ();
2009-08-08 01:39:56 +00:00
2009-06-08 20:07:20 +00:00
// Clear framebuffer
2013-09-18 02:36:48 -05:00
if ( ! DriverDetails :: HasBug ( DriverDetails :: BUG_BROKENSWAP ))
{
glClearColor ( 0 , 0 , 0 , 0 );
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
GL_REPORT_ERRORD ();
}
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 ();
GLInterface -> SwapInterval ( s_vsync );
}
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.
2010-09-28 02:15:02 +00:00
TextureCache :: Cleanup ();
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
GL_REPORT_ERRORD ();
2010-06-05 01:38:22 +00:00
RestoreAPIState ();
2009-09-13 17:46:33 +00:00
GL_REPORT_ERRORD ();
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
2013-12-30 07:22:50 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGL )
glPolygonMode ( GL_FRONT_AND_BACK , g_ActiveConfig . bWireFrame ? GL_LINE : GL_FILL );
2013-10-29 01:23:17 -04: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-01-23 13:41:53 +01:00
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
glDisable ( GL_CULL_FACE );
}
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
};
if ( bpmem . blendmode . logicopenable )
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 );
}
void Renderer :: SetLineWidth ()
{
2014-04-27 11:59:04 -07:00
float fratio = xfmem . viewport . wd != 0 ?
2010-09-28 02:15:02 +00:00
(( float ) Renderer :: GetTargetWidth () / EFB_WIDTH ) : 1.0f ;
if ( bpmem . lineptwidth . linesize > 0 )
// scale by ratio of widths
glLineWidth (( float ) bpmem . lineptwidth . linesize * fratio / 6.0f );
2013-12-30 07:22:50 -06:00
if ( GLInterface -> GetMode () == GLInterfaceMode :: MODE_OPENGL && bpmem . lineptwidth . pointsize > 0 )
2010-09-28 02:15:02 +00:00
glPointSize (( float ) bpmem . lineptwidth . pointsize * fratio / 6.0f );
}
void Renderer :: SetSamplerState ( int stage , int texindex )
{
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
2013-02-26 18:30:13 +01:00
g_sampler_cache -> SetSamplerState (( texindex * 4 ) + stage , tm0 , tm1 );
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 ();
2013-11-15 22:07:08 -06:00
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
2013-11-15 13:00:38 +13:00
glReadPixels ( back_rc . left , back_rc . bottom , W , H , GL_RGBA , GL_UNSIGNED_BYTE , data );
2010-08-10 01:08:22 +00:00
2009-06-06 17:34:54 +00:00
// Show failure message
2010-08-03 10:48:49 +00:00
if ( GL_REPORT_ERROR () != GL_NO_ERROR )
2009-06-06 17:34:54 +00:00
{
2013-11-16 17:05:51 -05:00
delete [] data ;
2009-06-06 13:36:33 +00:00
OSD :: AddMessage ( "Error capturing or saving screenshot." , 2000 );
2013-11-10 00:10:20 +01:00
return false ;
2009-06-06 17:34:54 +00:00
}
2010-08-10 01:08:22 +00:00
2009-06-06 13:36:33 +00:00
// Turn image upside down
2013-11-15 13:00:38 +13:00
FlipImageData ( data , W , H , 4 );
2013-11-17 11:12:07 +13:00
bool success = TextureToPng ( data , W * 4 , filename , W , H , false );
2013-11-17 10:34:34 +13:00
delete [] data ;
return success ;
2009-03-28 21:07:16 +00:00
}
2010-11-18 02:21:26 +00:00
2010-12-20 17:06:39 +00:00
}