2013-04-18 03:29:41 +00: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-08-17 03:00:57 +00:00
# include <algorithm>
2014-02-17 10:18:15 +00: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 13:28:29 +00:00
# include <memory>
2014-03-12 19:33:41 +00:00
# include <string>
2014-02-17 10:18:15 +00:00
# include <vector>
# include "Common/Atomic.h"
# include "Common/CommonPaths.h"
# include "Common/FileUtil.h"
2015-05-06 22:37:58 +00:00
# include "Common/MathUtil.h"
2014-02-17 10:18:15 +00:00
# include "Common/StringUtil.h"
# include "Common/Thread.h"
2014-02-19 11:14:09 +00:00
# include "Common/Timer.h"
2014-12-28 20:46:00 +00:00
# include "Common/Logging/LogManager.h"
2014-02-17 10:18:15 +00:00
# include "Core/ConfigManager.h"
# include "Core/Core.h"
2014-11-13 22:26:49 +00:00
# include "VideoBackends/OGL/BoundingBox.h"
2014-02-17 10:18:15 +00:00
# include "VideoBackends/OGL/FramebufferManager.h"
2014-08-02 06:21:03 +00:00
# include "VideoBackends/OGL/GLInterfaceBase.h"
2014-02-17 10:18:15 +00:00
# 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"
2015-01-02 17:06:56 +00:00
# include "VideoCommon/PixelShaderManager.h"
2014-02-17 10:18:15 +00: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 01:35:29 +00: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 13:58:25 +00:00
static int OSDInternalW , OSDInternalH ;
2011-01-29 20:16:51 +00:00
namespace OGL
{
2013-03-25 14:45:10 +00:00
2014-08-15 18:09:53 +00:00
enum MultisampleMode
{
2013-03-25 14:45:10 +00:00
MULTISAMPLE_OFF ,
MULTISAMPLE_2X ,
MULTISAMPLE_4X ,
MULTISAMPLE_8X ,
MULTISAMPLE_SSAA_4X ,
} ;
2013-03-25 14:14:24 +00:00
VideoConfig g_ogl_config ;
2011-01-29 20:16:51 +00:00
// Declarations and definitions
// ----------------------------
2012-12-07 22:34:38 +00:00
static GLuint s_ShowEFBCopyRegions_VBO = 0 ;
2012-12-09 21:49:58 +00:00
static GLuint s_ShowEFBCopyRegions_VAO = 0 ;
2013-02-13 12:12:19 +00:00
static SHADER s_ShowEFBCopyRegions ;
2011-01-29 20:16:51 +00:00
2014-03-09 20:14:26 +00: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 17:35:23 +00:00
static int s_last_multisample_mode = 0 ;
static bool s_last_stereo_mode = false ;
2014-12-28 17:26:25 +00:00
static bool s_last_xfb_mode = false ;
2014-10-29 14:42:37 +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 21:17:39 +00:00
static bool s_vsync ;
2011-08-22 04:15:02 +00:00
// EFB cache related
2012-12-07 22:34:38 +00: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 04:15:02 +00:00
static bool s_efbCacheValid [ 2 ] [ EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT ] ;
2014-06-05 08:12:05 +00:00
static bool s_efbCacheIsCleared = false ;
2011-08-22 04:15:02 +00: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 12:29:26 +00:00
static int GetNumMSAASamples ( int MSAAMode )
2009-02-23 07:23:34 +00:00
{
2013-03-25 14:14:24 +00:00
int samples ;
2011-05-07 22:00:05 +00:00
switch ( MSAAMode )
2009-03-08 20:04:40 +00:00
{
2010-11-18 02:21:26 +00:00
case MULTISAMPLE_OFF :
2013-03-17 09:44:57 +00:00
samples = 1 ;
break ;
2011-05-07 22:00:05 +00:00
2010-11-18 02:21:26 +00:00
case MULTISAMPLE_2X :
2013-03-17 09:44:57 +00:00
samples = 2 ;
break ;
2011-05-07 22:00:05 +00:00
2010-11-18 02:21:26 +00:00
case MULTISAMPLE_4X :
2013-03-25 14:45:10 +00:00
case MULTISAMPLE_SSAA_4X :
2013-03-17 09:44:57 +00:00
samples = 4 ;
break ;
2011-05-07 22:00:05 +00:00
2010-11-18 02:21:26 +00:00
case MULTISAMPLE_8X :
2013-03-17 09:44:57 +00:00
samples = 8 ;
break ;
2011-05-07 22:00:05 +00:00
default :
2013-03-17 09:44:57 +00:00
samples = 1 ;
2011-05-07 22:00:05 +00:00
}
2013-10-29 05:23:17 +00:00
2014-08-15 18:09:53 +00:00
if ( samples < = g_ogl_config . max_samples )
return samples ;
2013-10-29 05:23:17 +00:00
2013-08-27 11:24:23 +00: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 14:14:24 +00:00
return g_ogl_config . max_samples ;
2011-05-07 22:00:05 +00:00
}
2014-08-15 18:09:53 +00:00
static void ApplySSAASettings ( )
{
2013-05-06 04:33:49 +00:00
// GLES3 doesn't support SSAA
2013-12-30 13:22:50 +00:00
if ( GLInterface - > GetMode ( ) = = GLInterfaceMode : : MODE_OPENGL )
{
2014-08-15 18:09:53 +00:00
if ( g_ActiveConfig . iMultisampleMode = = MULTISAMPLE_SSAA_4X )
{
if ( g_ogl_config . bSupportSampleShading )
{
2013-12-30 13:22:50 +00:00
glEnable ( GL_SAMPLE_SHADING_ARB ) ;
2014-08-20 03:18:02 +00:00
GLfloat min_sample_shading_value = static_cast < GLfloat > ( s_MSAASamples ) ;
glMinSampleShadingARB ( min_sample_shading_value ) ;
2014-08-15 18:09:53 +00:00
}
else
{
2013-12-30 13:22:50 +00:00
// TODO: move this to InitBackendInfo
OSD : : AddMessage ( " SSAA Anti Aliasing isn't supported by your GPU. " , 10000 ) ;
}
2014-08-15 18:09:53 +00:00
}
else if ( g_ogl_config . bSupportSampleShading )
{
2013-12-30 13:22:50 +00:00
glDisable ( GL_SAMPLE_SHADING_ARB ) ;
2013-03-25 14:45:10 +00:00
}
}
}
2014-07-08 12:29:26 +00:00
static void GLAPIENTRY ErrorCallback ( GLenum source , GLenum type , GLuint id , GLenum severity , GLsizei length , const char * message , const void * userParam )
2013-04-04 15:37:16 +00:00
{
const char * s_source ;
const char * s_type ;
2013-05-06 04:33:49 +00:00
2014-03-10 11:30:55 +00:00
switch ( source )
2013-04-04 15:37:16 +00: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-10 11:30:55 +00:00
switch ( type )
2013-04-04 15:37:16 +00: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-10 11:30:55 +00:00
switch ( severity )
2013-04-04 15:37:16 +00: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 ;
}
}
2013-06-25 16:14:41 +00:00
// Two small Fallbacks to avoid GL_ARB_ES2_compatibility
2014-07-08 12:29:26 +00:00
static void GLAPIENTRY DepthRangef ( GLfloat neardepth , GLfloat fardepth )
2013-06-25 16:14:41 +00:00
{
2014-01-01 22:02:18 +00:00
glDepthRange ( neardepth , fardepth ) ;
2013-06-25 16:14:41 +00:00
}
2014-07-08 12:29:26 +00:00
static void GLAPIENTRY ClearDepthf ( GLfloat depthval )
2013-06-25 16:14:41 +00:00
{
2014-01-01 22:02:18 +00:00
glClearDepth ( depthval ) ;
2013-06-25 16:14:41 +00:00
}
2014-07-08 12:29:26 +00:00
static void InitDriverInfo ( )
2013-06-11 13:33:56 +00:00
{
std : : string svendor = std : : string ( g_ogl_config . gl_vendor ) ;
std : : string srenderer = std : : string ( g_ogl_config . gl_renderer ) ;
2013-08-23 08:52:29 +00:00
std : : string sversion = std : : string ( g_ogl_config . gl_version ) ;
2013-06-11 13:33:56 +00:00
DriverDetails : : Vendor vendor = DriverDetails : : VENDOR_UNKNOWN ;
2013-08-21 10:34:42 +00:00
DriverDetails : : Driver driver = DriverDetails : : DRIVER_UNKNOWN ;
2013-06-11 13:33:56 +00:00
double version = 0.0 ;
2013-12-31 00:26:55 +00:00
u32 family = 0 ;
2013-06-11 13:33:56 +00:00
2013-08-21 10:34:42 +00:00
// Get the vendor first
2013-06-11 13:33:56 +00:00
if ( svendor = = " NVIDIA Corporation " & & srenderer ! = " NVIDIA Tegra " )
2014-08-15 18:09:53 +00:00
{
2013-10-29 05:23:17 +00:00
vendor = DriverDetails : : VENDOR_NVIDIA ;
2014-08-15 18:09:53 +00:00
}
2013-08-21 10:34:42 +00:00
else if ( svendor = = " ATI Technologies Inc. " | | svendor = = " Advanced Micro Devices, Inc. " )
2014-08-15 18:09:53 +00:00
{
2013-06-11 13:33:56 +00:00
vendor = DriverDetails : : VENDOR_ATI ;
2014-08-15 18:09:53 +00:00
}
2013-08-23 08:52:29 +00:00
else if ( std : : string : : npos ! = sversion . find ( " Mesa " ) )
2014-08-15 18:09:53 +00:00
{
2013-08-23 08:52:29 +00:00
vendor = DriverDetails : : VENDOR_MESA ;
2014-08-15 18:09:53 +00:00
}
2013-06-11 13:33:56 +00:00
else if ( std : : string : : npos ! = svendor . find ( " Intel " ) )
2014-08-15 18:09:53 +00:00
{
2013-06-11 13:33:56 +00:00
vendor = DriverDetails : : VENDOR_INTEL ;
2014-08-15 18:09:53 +00:00
}
2013-06-11 13:33:56 +00:00
else if ( svendor = = " ARM " )
2014-08-15 18:09:53 +00:00
{
2013-06-11 13:33:56 +00:00
vendor = DriverDetails : : VENDOR_ARM ;
2014-08-15 18:09:53 +00:00
}
2013-08-21 10:41:32 +00:00
else if ( svendor = = " http://limadriver.org/ " )
{
vendor = DriverDetails : : VENDOR_ARM ;
driver = DriverDetails : : DRIVER_LIMA ;
}
2013-06-11 13:33:56 +00:00
else if ( svendor = = " Qualcomm " )
2014-08-15 18:09:53 +00:00
{
2013-06-11 13:33:56 +00:00
vendor = DriverDetails : : VENDOR_QUALCOMM ;
2014-08-15 18:09:53 +00:00
}
2013-06-11 13:33:56 +00:00
else if ( svendor = = " Imagination Technologies " )
2014-08-15 18:09:53 +00:00
{
2013-06-11 13:33:56 +00:00
vendor = DriverDetails : : VENDOR_IMGTEC ;
2014-08-15 18:09:53 +00:00
}
2013-07-03 01:58:09 +00:00
else if ( svendor = = " NVIDIA Corporation " & & srenderer = = " NVIDIA Tegra " )
2014-08-15 18:09:53 +00:00
{
2013-06-11 13:33:56 +00:00
vendor = DriverDetails : : VENDOR_TEGRA ;
2014-08-15 18:09:53 +00:00
}
2013-06-11 13:33:56 +00:00
else if ( svendor = = " Vivante Corporation " )
2014-08-15 18:09:53 +00:00
{
2013-06-11 13:33:56 +00:00
vendor = DriverDetails : : VENDOR_VIVANTE ;
2014-08-15 18:09:53 +00:00
}
2013-10-29 05:23:17 +00:00
2013-06-11 13:33:56 +00:00
// Get device family and driver version...if we care about it
2014-03-10 11:30:55 +00:00
switch ( vendor )
2013-06-11 13:33:56 +00:00
{
case DriverDetails : : VENDOR_QUALCOMM :
{
2014-12-19 21:31:37 +00:00
driver = DriverDetails : : DRIVER_QUALCOMM ;
2013-06-11 13:33:56 +00: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-12 04:46:44 +00: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.
driver = DriverDetails : : DRIVER_ARM ;
if ( GLExtensions : : Supports ( " GL_EXT_shader_pixel_local_storage " ) )
version = 400 ;
else
version = 300 ;
2013-08-23 08:52:29 +00:00
break ;
case DriverDetails : : VENDOR_MESA :
{
2014-03-10 11:30:55 +00:00
if ( svendor = = " nouveau " )
2013-08-23 08:52:29 +00:00
driver = DriverDetails : : DRIVER_NOUVEAU ;
2014-03-10 11:30:55 +00:00
else if ( svendor = = " Intel Open Source Technology Center " )
2013-08-23 08:52:29 +00:00
driver = DriverDetails : : DRIVER_I965 ;
2014-03-10 11:30:55 +00:00
else if ( std : : string : : npos ! = srenderer . find ( " AMD " ) | | std : : string : : npos ! = srenderer . find ( " ATI " ) )
2013-08-23 08:52:29 +00:00
driver = DriverDetails : : DRIVER_R600 ;
2013-10-29 05:23:17 +00:00
2013-08-23 08:52:29 +00: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 ;
2014-09-04 23:03:53 +00:00
case DriverDetails : : VENDOR_INTEL : // Happens in OS X/Windows
{
2013-12-31 00:26:55 +00:00
sscanf ( g_ogl_config . gl_renderer , " Intel HD Graphics %d " , & family ) ;
2014-09-04 23:03:53 +00:00
# ifdef _WIN32
2013-12-31 00:26:55 +00:00
int glmajor = 0 ;
int glminor = 0 ;
int major = 0 ;
int minor = 0 ;
int release = 0 ;
2014-09-04 23:03:53 +00: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-31 00:26:55 +00:00
break ;
2014-01-08 00:29:04 +00:00
case DriverDetails : : VENDOR_NVIDIA :
{
int glmajor = 0 ;
int glminor = 0 ;
int glrelease = 0 ;
int major = 0 ;
int minor = 0 ;
2015-01-11 05:17:29 +00: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-08 01:35:06 +00:00
sscanf ( g_ogl_config . gl_version , " %d.%d.%d NVIDIA %d.%d " , & glmajor , & glminor , & glrelease , & major , & minor ) ;
2014-01-08 00:29:04 +00:00
version = 100 * major + minor ;
}
break ;
2013-06-11 13:33:56 +00:00
// We don't care about these
default :
break ;
}
2013-12-31 00:26:55 +00:00
DriverDetails : : Init ( vendor , driver , version , family ) ;
2013-06-11 13:33:56 +00:00
}
2011-05-07 22:00:05 +00:00
// Init functions
Renderer : : Renderer ( )
{
OSDInternalW = 0 ;
OSDInternalH = 0 ;
2012-12-07 22:34:38 +00: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-06 04:33:49 +00:00
// Init extension support.
2013-12-30 13:22:50 +00:00
if ( ! GLExtensions : : Init ( ) )
2013-09-02 11:14:45 +00:00
{
2013-12-30 13:22:50 +00: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 11:14:45 +00:00
}
2014-01-02 02:06:11 +00: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 10:05:44 +00:00
2013-09-02 11:14:45 +00:00
// check for the max vertex attributes
2011-05-07 22:00:05 +00:00
GLint numvertexattribs = 0 ;
2009-02-28 16:33:59 +00:00
glGetIntegerv ( GL_MAX_VERTEX_ATTRIBS , & numvertexattribs ) ;
2013-01-21 09:37:16 +00:00
if ( numvertexattribs < 16 )
2010-08-10 01:08:22 +00:00
{
2013-09-02 11:14:45 +00:00
PanicAlert ( " GPU: OGL ERROR: Number of attributes %d not enough. \n "
2010-08-10 01:08:22 +00:00
" GPU: Does your video card support OpenGL 2.x? " ,
numvertexattribs ) ;
2010-06-05 01:38:22 +00:00
bSuccess = false ;
}
2013-09-02 11:14:45 +00:00
// 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 )
2010-08-10 01:08:22 +00:00
{
2013-09-02 11:14:45 +00:00
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 05:23:17 +00:00
2013-12-30 13:22:50 +00:00
if ( ! GLExtensions : : Supports ( " GL_ARB_framebuffer_object " ) )
2010-08-10 01:08:22 +00:00
{
2013-09-02 11:14:45 +00: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 11:06:47 +00:00
" GPU: Does your video card support OpenGL 3.0? " ) ;
2010-06-05 01:38:22 +00:00
bSuccess = false ;
}
2010-08-10 01:08:22 +00:00
2013-12-30 13:22:50 +00:00
if ( ! GLExtensions : : Supports ( " GL_ARB_vertex_array_object " ) )
2012-12-09 21:49:58 +00:00
{
2013-09-02 11:14:45 +00: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 21:49:58 +00:00
" GPU: Does your video card support OpenGL 3.0? " ) ;
bSuccess = false ;
}
2013-10-29 05:23:17 +00:00
2013-12-30 13:22:50 +00:00
if ( ! GLExtensions : : Supports ( " GL_ARB_map_buffer_range " ) )
2012-12-15 13:43:01 +00:00
{
2013-09-02 11:14:45 +00: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 13:43:01 +00:00
" GPU: Does your video card support OpenGL 3.0? " ) ;
bSuccess = false ;
}
2014-01-30 15:38:11 +00: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 20:33:47 +00:00
if ( ! GLExtensions : : Supports ( " GL_ARB_sampler_objects " ) )
2013-02-20 03:24:08 +00:00
{
2013-09-02 11:14:45 +00: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 20:33:47 +00: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-20 03:24:08 +00:00
bSuccess = false ;
}
2013-10-29 05:23:17 +00:00
2013-06-25 16:14:41 +00: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 13:22:50 +00:00
if ( ! GLExtensions : : Supports ( " GL_ARB_ES2_compatibility " ) & & GLInterface - > GetMode ( ) = = GLInterfaceMode : : MODE_OPENGL )
2013-06-25 16:14:41 +00:00
{
glDepthRangef = DepthRangef ;
glClearDepthf = ClearDepthf ;
}
2014-01-02 02:06:11 +00:00
2013-12-30 13:22:50 +00:00
g_Config . backend_info . bSupportsDualSourceBlend = GLExtensions : : Supports ( " GL_ARB_blend_func_extended " ) ;
2014-01-17 15:10:10 +00:00
g_Config . backend_info . bSupportsPrimitiveRestart = ! DriverDetails : : HasBug ( DriverDetails : : BUG_PRIMITIVERESTART ) & &
( ( GLExtensions : : Version ( ) > = 310 ) | | GLExtensions : : Supports ( " GL_NV_primitive_restart " ) ) ;
2013-12-30 13:22:50 +00:00
g_Config . backend_info . bSupportsEarlyZ = GLExtensions : : Supports ( " GL_ARB_shader_image_load_store " ) ;
2014-11-13 22:26:49 +00:00
g_Config . backend_info . bSupportsBBox = GLExtensions : : Supports ( " GL_ARB_shader_storage_buffer_object " ) ;
2014-10-29 13:51:12 +00:00
g_Config . backend_info . bSupportsGSInstancing = GLExtensions : : Supports ( " GL_ARB_gpu_shader5 " ) ;
2014-12-21 11:52:14 +00:00
g_Config . backend_info . bSupportsGeometryShaders = GLExtensions : : Version ( ) > = 320 ;
2015-02-20 23:12:27 +00:00
g_Config . backend_info . bSupportsPaletteConversion = GLExtensions : : Supports ( " GL_ARB_texture_buffer_object " ) ;
2014-03-30 18:58:05 +00: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-12-30 13:22:50 +00: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-22 00:52:39 +00:00
g_ogl_config . bSupportsGLBaseVertex = GLExtensions : : Supports ( " GL_ARB_draw_elements_base_vertex " ) | |
2015-01-18 21:23:51 +00: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 " ) | |
GLExtensions : : Supports ( " GL_EXT_buffer_storage " ) ; ;
2014-04-30 16:48:40 +00:00
g_ogl_config . bSupportsMSAA = GLExtensions : : Supports ( " GL_ARB_texture_multisample " ) ;
2013-12-30 13:22:50 +00: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 " ) ;
2014-12-28 20:46:00 +00:00
g_ogl_config . bSupportsDebug = GLExtensions : : Supports ( " GL_KHR_debug " ) | | GLExtensions : : Supports ( " GL_ARB_debug_output " ) ;
2013-12-30 13:22:50 +00:00
if ( GLInterface - > GetMode ( ) = = GLInterfaceMode : : MODE_OPENGLES3 )
2014-03-30 18:58:05 +00:00
{
2014-06-07 02:21:35 +00:00
if ( strstr ( g_ogl_config . glsl_version , " 3.0 " ) )
2014-03-30 18:58:05 +00:00
{
g_ogl_config . eSupportedGLSLVersion = GLSLES_300 ;
2014-12-07 05:29:36 +00:00
g_ogl_config . bSupportsAEP = false ;
2014-12-16 23:26:03 +00:00
g_Config . backend_info . bSupportsGeometryShaders = false ;
2014-03-30 18:58:05 +00:00
}
else
{
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 18:58:05 +00:00
g_Config . backend_info . bSupportsBindingLayout = true ;
2014-10-23 12:34:07 +00:00
g_Config . backend_info . bSupportsEarlyZ = true ;
2014-12-16 23:26:03 +00:00
g_Config . backend_info . bSupportsGeometryShaders = g_ogl_config . bSupportsAEP ;
2015-02-20 23:12:27 +00:00
//g_Config.backend_info.bSupportsPaletteConversion = GLExtensions::Supports("GL_EXT_texture_buffer");
2014-03-30 18:58:05 +00:00
}
}
2013-12-30 13:22:50 +00:00
else
2013-04-08 12:50:58 +00:00
{
2014-03-10 11:30:55 +00: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 13:22:50 +00: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-10 11:30:55 +00:00
else if ( strstr ( g_ogl_config . glsl_version , " 1.30 " ) )
2013-12-30 13:22:50 +00:00
{
g_ogl_config . eSupportedGLSLVersion = GLSL_130 ;
g_Config . backend_info . bSupportsEarlyZ = false ; // layout keyword is only supported on glsl150+
2014-12-16 23:26:03 +00:00
g_Config . backend_info . bSupportsGeometryShaders = false ; // geometry shaders are only supported on glsl150+
2013-12-30 13:22:50 +00:00
}
2014-03-10 11:30:55 +00:00
else if ( strstr ( g_ogl_config . glsl_version , " 1.40 " ) )
2013-12-30 13:22:50 +00:00
{
g_ogl_config . eSupportedGLSLVersion = GLSL_140 ;
g_Config . backend_info . bSupportsEarlyZ = false ; // layout keyword is only supported on glsl150+
2014-12-16 23:26:03 +00:00
g_Config . backend_info . bSupportsGeometryShaders = false ; // geometry shaders are only supported on glsl150+
2013-12-30 13:22:50 +00:00
}
else
{
g_ogl_config . eSupportedGLSLVersion = GLSL_150 ;
}
2014-12-07 05:29:36 +00:00
// Desktop OpenGL can't have the Android Extension Pack
g_ogl_config . bSupportsAEP = false ;
2013-04-08 12:50:58 +00:00
}
2014-11-22 14:07:52 +00:00
2014-12-28 20:46:00 +00:00
if ( g_ogl_config . bSupportsDebug )
2013-04-08 12:50:58 +00:00
{
2014-12-28 20:46:00 +00: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 ) ;
}
if ( LogManager : : GetInstance ( ) - > IsEnabled ( LogTypes : : VIDEO , LogTypes : : LERROR ) )
glEnable ( GL_DEBUG_OUTPUT ) ;
else
glDisable ( GL_DEBUG_OUTPUT ) ;
2013-04-08 12:50:58 +00:00
}
2014-11-22 14:07:52 +00:00
2013-11-05 16:37:32 +00:00
int samples ;
glGetIntegerv ( GL_SAMPLES , & samples ) ;
2014-03-10 11:30:55 +00:00
if ( samples > 1 )
2013-11-05 16:37:32 +00: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 05:17:29 +00:00
" Please avoid forcing Dolphin to use MSAA by the driver. \n "
2013-11-05 16:37:32 +00:00
" %d samples on default framebuffer found. " , samples ) ;
bSuccess = false ;
}
2013-09-02 11:14:45 +00: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 05:23:17 +00:00
2013-03-25 14:14:24 +00:00
glGetIntegerv ( GL_MAX_SAMPLES , & g_ogl_config . max_samples ) ;
2014-04-30 16:48:40 +00:00
if ( g_ogl_config . max_samples < 1 | | ! g_ogl_config . bSupportsMSAA )
2013-04-05 05:08:32 +00:00
g_ogl_config . max_samples = 1 ;
2013-10-29 05:23:17 +00:00
2014-10-30 13:56:40 +00:00
g_Config . VerifyValidity ( ) ;
2013-01-21 17:44:33 +00:00
UpdateActiveConfig ( ) ;
2013-03-25 14:14:24 +00:00
OSD : : AddMessage ( StringFromFormat ( " Video Info: %s, %s, %s " ,
g_ogl_config . gl_vendor ,
g_ogl_config . gl_renderer ,
2013-08-23 23:44:16 +00:00
g_ogl_config . gl_version ) , 5000 ) ;
2013-10-29 05:23:17 +00:00
2014-11-03 23:44:30 +00:00
WARN_LOG ( VIDEO , " Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s%s " ,
2013-02-17 20:41:00 +00:00
g_ActiveConfig . backend_info . bSupportsDualSourceBlend ? " " : " DualSourceBlend " ,
2013-03-29 13:27:33 +00:00
g_ActiveConfig . backend_info . bSupportsPrimitiveRestart ? " " : " PrimitiveRestart " ,
2013-07-22 10:02:16 +00:00
g_ActiveConfig . backend_info . bSupportsEarlyZ ? " " : " EarlyZ " ,
2013-03-25 14:14:24 +00:00
g_ogl_config . bSupportsGLPinnedMemory ? " " : " PinnedMemory " ,
g_ogl_config . bSupportsGLSLCache ? " " : " ShaderCache " ,
g_ogl_config . bSupportsGLBaseVertex ? " " : " BaseVertex " ,
2013-12-27 16:56:03 +00:00
g_ogl_config . bSupportsGLBufferStorage ? " " : " BufferStorage " ,
2013-03-25 14:14:24 +00:00
g_ogl_config . bSupportsGLSync ? " " : " Sync " ,
2014-04-30 16:48:40 +00:00
g_ogl_config . bSupportsMSAA ? " " : " MSAA " ,
2014-10-30 13:56:40 +00:00
g_ogl_config . bSupportSampleShading ? " " : " SSAA " ,
g_ActiveConfig . backend_info . bSupportsGSInstancing ? " " : " GSInstancing "
2013-04-04 16:55:37 +00:00
) ;
2013-10-29 05:23:17 +00:00
2014-12-28 17:35:23 +00:00
s_last_multisample_mode = g_ActiveConfig . iMultisampleMode ;
s_MSAASamples = GetNumMSAASamples ( s_last_multisample_mode ) ;
2013-03-25 14:45:10 +00:00
ApplySSAASettings ( ) ;
2014-12-28 17:26:25 +00:00
2014-12-28 17:35:23 +00:00
s_last_stereo_mode = g_ActiveConfig . iStereoMode > 0 ;
2014-12-28 17:26:25 +00:00
s_last_xfb_mode = g_ActiveConfig . bUseRealXFB ;
2013-10-29 05:23:17 +00:00
2013-04-19 13:21:45 +00:00
// Decide framebuffer size
2012-12-17 20:54:20 +00: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 21:17:39 +00:00
s_vsync = g_ActiveConfig . IsVSync ( ) ;
GLInterface - > SwapInterval ( s_vsync ) ;
2009-02-23 07:23:34 +00:00
2012-09-28 21:48:18 +00: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-28 22:04:55 +00:00
UpdateDrawRectangle ( s_backbuffer_width , s_backbuffer_height ) ;
2010-08-10 01:08:22 +00:00
2014-12-28 17:35:23 +00:00
s_last_efb_scale = g_ActiveConfig . iEFBScale ;
2012-09-28 22:19:28 +00:00
CalculateTargetSize ( s_backbuffer_width , s_backbuffer_height ) ;
2010-09-30 15:24:34 +00:00
2015-01-02 17:06:56 +00: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 05:23:17 +00:00
2010-06-05 01:38:22 +00:00
glStencilFunc ( GL_ALWAYS , 0 , 0 ) ;
glBlendFunc ( GL_ONE , GL_ONE ) ;
glViewport ( 0 , 0 , GetTargetWidth ( ) , GetTargetHeight ( ) ) ; // Reset The Current Viewport
2010-12-27 03:18:01 +00:00
glClearColor ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
2013-05-06 04:33:49 +00:00
glClearDepthf ( 1.0f ) ;
2010-06-05 01:38:22 +00:00
glEnable ( GL_DEPTH_TEST ) ;
glDepthFunc ( GL_LEQUAL ) ;
2010-08-10 01:08:22 +00:00
2010-06-05 01:38:22 +00:00
glPixelStorei ( GL_UNPACK_ALIGNMENT , 4 ) ; // 4-byte pixel alignment
2010-08-10 01:08:22 +00:00
2010-06-05 01:38:22 +00:00
glDisable ( GL_STENCIL_TEST ) ;
glEnable ( GL_SCISSOR_TEST ) ;
glScissor ( 0 , 0 , GetTargetWidth ( ) , GetTargetHeight ( ) ) ;
2013-01-03 11:06:47 +00:00
glBlendColor ( 0 , 0 , 0 , 0.5f ) ;
2013-05-06 04:33:49 +00:00
glClearDepthf ( 1.0f ) ;
2014-03-10 11:30:55 +00:00
if ( g_ActiveConfig . backend_info . bSupportsPrimitiveRestart )
2013-04-10 10:58:52 +00:00
{
2013-12-30 13:22:50 +00:00
if ( GLInterface - > GetMode ( ) = = GLInterfaceMode : : MODE_OPENGLES3 )
2014-08-15 18:09:53 +00:00
{
2013-12-30 13:22:50 +00:00
glEnable ( GL_PRIMITIVE_RESTART_FIXED_INDEX ) ;
2014-08-15 18:09:53 +00:00
}
2013-04-10 10:58:52 +00:00
else
2014-08-15 18:09:53 +00:00
{
2014-03-10 11:30:55 +00:00
if ( g_ogl_config . bSupportOGL31 )
2013-12-30 13:22:50 +00:00
{
glEnable ( GL_PRIMITIVE_RESTART ) ;
glPrimitiveRestartIndex ( 65535 ) ;
}
else
{
glEnableClientState ( GL_PRIMITIVE_RESTART_NV ) ;
glPrimitiveRestartIndexNV ( 65535 ) ;
}
2014-08-15 18:09:53 +00:00
}
2013-09-16 15:43:57 +00:00
}
2009-09-13 08:21:35 +00:00
UpdateActiveConfig ( ) ;
2014-06-05 08:12:05 +00:00
ClearEFBCache ( ) ;
2009-02-23 07:23:34 +00:00
}
2010-11-18 02:21:26 +00:00
Renderer : : ~ Renderer ( )
2013-01-25 19:39:19 +00:00
{
}
void Renderer : : Shutdown ( )
2010-06-05 01:38:22 +00:00
{
2013-07-22 13:41:10 +00:00
delete g_framebuffer_manager ;
2013-10-29 05:23:17 +00:00
2009-09-06 15:11:21 +00:00
g_Config . bRunning = false ;
2009-09-13 08:21:35 +00:00
UpdateActiveConfig ( ) ;
2013-10-29 05:23:17 +00:00
2012-12-07 22:34:38 +00:00
glDeleteBuffers ( 1 , & s_ShowEFBCopyRegions_VBO ) ;
2012-12-09 21:49:58 +00:00
glDeleteVertexArrays ( 1 , & s_ShowEFBCopyRegions_VAO ) ;
2012-12-07 22:34:38 +00:00
s_ShowEFBCopyRegions_VBO = 0 ;
2013-10-29 05:23:17 +00:00
2010-06-05 01:38:22 +00:00
delete s_pfont ;
2014-03-09 20:14:26 +00:00
s_pfont = nullptr ;
2013-02-13 12:12:19 +00:00
s_ShowEFBCopyRegions . Destroy ( ) ;
2014-07-29 16:57:02 +00:00
delete m_post_processor ;
m_post_processor = nullptr ;
2014-12-04 07:24:58 +00:00
OpenGL_DeleteAttributelessVAO ( ) ;
2013-01-25 19:39:19 +00:00
}
2009-09-03 19:24:16 +00:00
2013-01-25 19:39:19 +00:00
void Renderer : : Init ( )
{
2013-07-22 13:41:10 +00:00
// Initialize the FramebufferManager
g_framebuffer_manager = new FramebufferManager ( s_target_width , s_target_height ,
2014-04-30 16:13:31 +00:00
s_MSAASamples ) ;
2013-10-29 05:23:17 +00:00
2014-07-29 16:57:02 +00:00
m_post_processor = new OpenGLPostProcessing ( ) ;
2013-01-25 19:39:19 +00:00
s_pfont = new RasterFont ( ) ;
2013-10-29 05:23:17 +00:00
ProgramShaderCache : : CompileShader ( s_ShowEFBCopyRegions ,
2014-02-25 14:52:22 +00:00
" in vec2 rawpos; \n "
" in vec3 color0; \n "
" out vec4 c; \n "
2013-01-25 19:39:19 +00:00
" void main(void) { \n "
2013-10-06 08:12:13 +00:00
" gl_Position = vec4(rawpos, 0.0, 1.0); \n "
" c = vec4(color0, 1.0); \n "
2013-02-13 12:12:19 +00:00
" } \n " ,
2014-02-25 14:52:22 +00:00
" in vec4 c; \n "
2013-11-25 00:06:29 +00:00
" out vec4 ocol0; \n "
2013-02-13 12:12:19 +00:00
" void main(void) { \n "
" ocol0 = c; \n "
" } \n " ) ;
2013-10-29 05:23:17 +00:00
2014-12-11 09:00:37 +00:00
OpenGL_CreateAttributelessVAO ( ) ;
2013-01-25 19:39:19 +00:00
// creating buffers
glGenBuffers ( 1 , & s_ShowEFBCopyRegions_VBO ) ;
glGenVertexArrays ( 1 , & s_ShowEFBCopyRegions_VAO ) ;
glBindBuffer ( GL_ARRAY_BUFFER , s_ShowEFBCopyRegions_VBO ) ;
glBindVertexArray ( s_ShowEFBCopyRegions_VAO ) ;
glEnableVertexAttribArray ( SHADER_POSITION_ATTRIB ) ;
2014-03-09 20:14:26 +00:00
glVertexAttribPointer ( SHADER_POSITION_ATTRIB , 2 , GL_FLOAT , 0 , sizeof ( GLfloat ) * 5 , nullptr ) ;
2013-01-25 19:39:19 +00:00
glEnableVertexAttribArray ( SHADER_COLOR0_ATTRIB ) ;
2014-03-09 20:14:26 +00: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 12:02:00 +00:00
void Renderer : : ShowEfbCopyRegions ( )
2009-02-23 07:23:34 +00:00
{
2013-12-30 13:22:50 +00:00
if ( GLInterface - > GetMode ( ) = = GLInterfaceMode : : MODE_OPENGL & & g_ActiveConfig . bShowEFBCopyRegions )
2010-09-28 02:15:02 +00:00
{
// Set Line Size
glLineWidth ( 3.0f ) ;
2009-02-23 07:23:34 +00:00
2012-12-07 21:49:18 +00:00
// 2*Coords + 3*Color
2014-08-20 03:18:02 +00:00
GLsizeiptr length = stats . efb_regions . size ( ) * sizeof ( GLfloat ) * ( 2 + 3 ) * 2 * 6 ;
2012-12-07 22:34:38 +00:00
glBindBuffer ( GL_ARRAY_BUFFER , s_ShowEFBCopyRegions_VBO ) ;
2014-03-09 20:14:26 +00:00
glBufferData ( GL_ARRAY_BUFFER , length , nullptr , GL_STREAM_DRAW ) ;
2013-08-19 17:20:10 +00:00
GLfloat * Vertices = ( GLfloat * ) glMapBufferRange ( GL_ARRAY_BUFFER , 0 , length , GL_MAP_WRITE_BIT ) ;
2010-08-10 01:08:22 +00:00
2010-09-28 02:15:02 +00:00
// Draw EFB copy regions rectangles
2012-10-27 23:10:31 +00:00
int a = 0 ;
2012-12-07 21:49:18 +00:00
GLfloat color [ 3 ] = { 0.0f , 1.0f , 1.0f } ;
2013-10-29 05:23:17 +00:00
2014-02-12 15:00:34 +00: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 15:00:34 +00: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 ) ;
2009-02-23 07:23:34 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x ;
Vertices [ a + + ] = y ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x2 ;
Vertices [ a + + ] = y ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x2 ;
Vertices [ a + + ] = y ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x2 ;
Vertices [ a + + ] = y2 ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x2 ;
Vertices [ a + + ] = y2 ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x ;
Vertices [ a + + ] = y2 ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x ;
Vertices [ a + + ] = y2 ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x ;
Vertices [ a + + ] = y ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x ;
Vertices [ a + + ] = y ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x2 ;
Vertices [ a + + ] = y2 ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x2 ;
Vertices [ a + + ] = y ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00:00
Vertices [ a + + ] = x ;
Vertices [ a + + ] = y2 ;
Vertices [ a + + ] = color [ 0 ] ;
Vertices [ a + + ] = color [ 1 ] ;
Vertices [ a + + ] = color [ 2 ] ;
2013-10-29 05:23:17 +00:00
2012-12-07 21:49:18 +00: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 22:34:38 +00:00
glUnmapBuffer ( GL_ARRAY_BUFFER ) ;
2013-10-29 05:23:17 +00:00
2013-02-13 12:12:19 +00:00
s_ShowEFBCopyRegions . Bind ( ) ;
2014-08-20 03:18:02 +00:00
glBindVertexArray ( s_ShowEFBCopyRegions_VAO ) ;
GLsizei count = static_cast < GLsizei > ( stats . efb_regions . size ( ) * 2 * 6 ) ;
glDrawArrays ( GL_LINES , 0 , count ) ;
2009-02-23 07:23:34 +00:00
2010-09-28 02:15:02 +00:00
// Clear stored regions
stats . efb_regions . clear ( ) ;
}
}
2014-03-12 19:33:41 +00: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 20:54:20 +00:00
const int nBackbufferWidth = ( int ) GLInterface - > GetBackBufferWidth ( ) ;
const int nBackbufferHeight = ( int ) GLInterface - > GetBackBufferHeight ( ) ;
2011-11-30 03:50:31 +00: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 11:37:08 +00:00
0 , nBackbufferWidth , nBackbufferHeight , color ) ;
2010-09-28 02:15:02 +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 ;
2010-09-28 02:15:02 +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 20:17:38 +00:00
void Renderer : : SetScissorRect ( const EFBRectangle & rc )
2009-02-23 07:23:34 +00:00
{
2014-02-04 20:17:38 +00: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 16:23:01 +00:00
if ( bpmem . alpha_test . TestResult ( ) ! = AlphaTest : : FAIL )
{
if ( bpmem . blendmode . colorupdate )
ColorMask = GL_TRUE ;
2014-03-23 20:44:23 +00:00
if ( bpmem . blendmode . alphaupdate & & ( bpmem . zcontrol . pixel_format = = PEControl : : RGBA6_Z24 ) )
2013-01-08 16:23:01 +00:00
AlphaMask = GL_TRUE ;
}
2010-09-28 02:15:02 +00:00
glColorMask ( ColorMask , ColorMask , ColorMask , AlphaMask ) ;
}
2011-08-22 04:15:02 +00:00
void ClearEFBCache ( )
{
2014-06-05 08:12:05 +00:00
if ( ! s_efbCacheIsCleared )
{
s_efbCacheIsCleared = true ;
memset ( s_efbCacheValid , 0 , sizeof ( s_efbCacheValid ) ) ;
}
2011-08-22 04:15:02 +00:00
}
2015-05-06 19:31:05 +00:00
void Renderer : : UpdateEFBCache ( EFBAccessType type , u32 cacheRectIdx , const EFBRectangle & efbPixelRc , const TargetRectangle & targetPixelRc , const void * data )
2011-08-22 04:15:02 +00: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 17:56:57 +00:00
u32 efbPixelRcHeight = efbPixelRc . bottom - efbPixelRc . top ;
u32 efbPixelRcWidth = efbPixelRc . right - efbPixelRc . left ;
2011-08-22 04:15:02 +00:00
2012-07-23 17:56:57 +00:00
for ( u32 yCache = 0 ; yCache < efbPixelRcHeight ; + + yCache )
2011-08-22 04:15:02 +00: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 17:56:57 +00:00
for ( u32 xCache = 0 ; xCache < efbPixelRcWidth ; + + xCache )
2011-08-22 04:15:02 +00:00
{
u32 xEFB = efbPixelRc . left + xCache ;
u32 xPixel = ( EFBToScaledX ( xEFB ) + EFBToScaledX ( xEFB + 1 ) ) / 2 ;
u32 xData = xPixel - targetPixelRc . left ;
2015-05-06 19:31:05 +00:00
u32 value ;
if ( type = = PEEK_Z )
{
float * ptr = ( float * ) data ;
2015-05-06 22:37:58 +00:00
value = MathUtil : : Clamp < u32 > ( ( u32 ) ( ptr [ yData * targetPixelRcWidth + xData ] * 16777216.0f ) , 0 , 0xFFFFFF ) ;
2015-05-06 19:31:05 +00:00
}
else
{
u32 * ptr = ( u32 * ) data ;
value = ptr [ yData * targetPixelRcWidth + xData ] ;
}
s_efbCache [ cacheType ] [ cacheRectIdx ] [ yCache * EFB_CACHE_RECT_SIZE + xCache ] = value ;
2011-08-22 04:15:02 +00:00
}
}
s_efbCacheValid [ cacheType ] [ cacheRectIdx ] = true ;
2014-06-05 08:12:05 +00:00
s_efbCacheIsCleared = false ;
2011-08-22 04:15:02 +00: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-17 04:51:41 +00:00
// - GX_PokeAlphaRead
2010-10-24 19:52:52 +00:00
// The behavior of EFB pokes can be modified by:
2014-02-17 04:51:41 +00: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 01:40:58 +00:00
u32 cacheRectIdx = ( y / EFB_CACHE_RECT_SIZE ) * EFB_CACHE_WIDTH
+ ( x / EFB_CACHE_RECT_SIZE ) ;
2011-08-22 04:15:02 +00:00
2010-09-28 02:15:02 +00:00
EFBRectangle efbPixelRc ;
2014-12-12 15:11:17 +00: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 04:15:02 +00: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 04:15:02 +00:00
if ( ! s_efbCacheValid [ 0 ] [ cacheRectIdx ] )
2010-09-28 02:15:02 +00:00
{
2011-08-22 04:15:02 +00:00
if ( s_MSAASamples > 1 )
{
2013-08-26 20:18:00 +00:00
g_renderer - > ResetAPIState ( ) ;
2013-10-29 05:23:17 +00:00
2011-08-22 04:15:02 +00:00
// Resolve our rectangle.
FramebufferManager : : GetEFBDepthTexture ( efbPixelRc ) ;
2013-01-03 11:06:47 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , FramebufferManager : : GetResolvedFramebuffer ( ) ) ;
2013-10-29 05:23:17 +00:00
2013-08-26 20:18:00 +00:00
g_renderer - > RestoreAPIState ( ) ;
2011-08-22 04:15:02 +00:00
}
2010-09-28 02:15:02 +00:00
2015-05-08 13:28:29 +00:00
std : : unique_ptr < float > depthMap ( new float [ targetPixelRcWidth * targetPixelRcHeight ] ) ;
2010-09-28 02:15:02 +00:00
2011-08-22 04:15:02 +00:00
glReadPixels ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRcWidth , targetPixelRcHeight ,
2015-05-08 13:28:29 +00:00
GL_DEPTH_COMPONENT , GL_FLOAT , depthMap . get ( ) ) ;
2011-08-22 04:15:02 +00:00
2015-05-08 13:28:29 +00:00
UpdateEFBCache ( type , cacheRectIdx , efbPixelRc , targetPixelRc , depthMap . get ( ) ) ;
2011-08-22 04:15:02 +00:00
}
u32 xRect = x % EFB_CACHE_RECT_SIZE ;
u32 yRect = y % EFB_CACHE_RECT_SIZE ;
2015-05-08 13:29:36 +00:00
u32 z = s_efbCache [ 0 ] [ cacheRectIdx ] [ yRect * EFB_CACHE_RECT_SIZE + xRect ] ;
2010-09-28 02:15:02 +00:00
2015-05-06 19:31:05 +00:00
// if Z is in 16 bit format you must return a 16 bit integer
2014-03-23 20:44:23 +00:00
if ( bpmem . zcontrol . pixel_format = = PEControl : : RGB565_Z16 )
2010-12-27 03:18:01 +00:00
z = z > > 8 ;
2015-05-06 19:31:05 +00:00
2010-12-27 03:18:01 +00: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 04:15:02 +00:00
if ( ! s_efbCacheValid [ 1 ] [ cacheRectIdx ] )
2010-09-28 02:15:02 +00:00
{
2011-08-22 04:15:02 +00:00
if ( s_MSAASamples > 1 )
{
2013-08-26 20:18:00 +00:00
g_renderer - > ResetAPIState ( ) ;
2013-10-29 05:23:17 +00:00
2011-08-22 04:15:02 +00:00
// Resolve our rectangle.
FramebufferManager : : GetEFBColorTexture ( efbPixelRc ) ;
2013-01-03 11:06:47 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , FramebufferManager : : GetResolvedFramebuffer ( ) ) ;
2013-10-29 05:23:17 +00:00
2013-08-26 20:18:00 +00:00
g_renderer - > RestoreAPIState ( ) ;
2011-08-22 04:15:02 +00:00
}
2015-05-08 13:28:29 +00:00
std : : unique_ptr < u32 > colorMap ( new u32 [ targetPixelRcWidth * targetPixelRcHeight ] ) ;
2011-08-22 04:15:02 +00:00
2013-12-30 13:22:50 +00:00
if ( GLInterface - > GetMode ( ) = = GLInterfaceMode : : MODE_OPENGLES3 )
2013-05-06 04:33:49 +00:00
// XXX: Swap colours
2013-12-30 13:22:50 +00:00
glReadPixels ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRcWidth , targetPixelRcHeight ,
2015-05-08 13:28:29 +00:00
GL_RGBA , GL_UNSIGNED_BYTE , colorMap . get ( ) ) ;
2013-12-30 13:22:50 +00:00
else
glReadPixels ( targetPixelRc . left , targetPixelRc . bottom , targetPixelRcWidth , targetPixelRcHeight ,
2015-05-08 13:28:29 +00:00
GL_BGRA , GL_UNSIGNED_INT_8_8_8_8_REV , colorMap . get ( ) ) ;
2010-09-28 02:15:02 +00:00
2015-05-08 13:28:29 +00:00
UpdateEFBCache ( type , cacheRectIdx , efbPixelRc , targetPixelRc , colorMap . get ( ) ) ;
2011-08-22 04:15:02 +00:00
}
u32 xRect = x % EFB_CACHE_RECT_SIZE ;
u32 yRect = y % EFB_CACHE_RECT_SIZE ;
2015-05-08 13:29:36 +00: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 02:23:35 +00:00
PixelEngine : : UPEAlphaReadReg alpha_read_mode = PixelEngine : : GetAlphaReadMode ( ) ;
2010-12-27 03:18:01 +00:00
2014-03-23 20:44:23 +00:00
if ( bpmem . zcontrol . pixel_format = = PEControl : : RGBA6_Z24 )
2010-12-27 03:18:01 +00:00
{
color = RGBA8ToRGBA6ToRGBA8 ( color ) ;
}
2014-03-23 20:44:23 +00: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 20:44:23 +00:00
if ( bpmem . zcontrol . pixel_format ! = PEControl : : RGBA6_Z24 )
2010-12-27 03:18:01 +00:00
{
color | = 0xFF000000 ;
}
2014-08-15 18:09:53 +00: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 :
2013-11-23 11:33:03 +00: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 ( ) ;
break ;
}
2010-12-27 18:09:03 +00:00
case POKE_Z :
2013-11-23 11:33:03 +00:00
{
ResetAPIState ( ) ;
glDepthMask ( GL_TRUE ) ;
2015-05-07 21:29:16 +00:00
glClearDepthf ( float ( poke_data & 0xFFFFFF ) / 16777216.0f ) ;
2013-11-23 11:33:03 +00:00
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 ( ) ;
2010-09-28 02:15:02 +00:00
break ;
2013-11-23 11:33:03 +00:00
}
2010-09-28 02:15:02 +00:00
default :
break ;
}
return 0 ;
}
2014-11-13 22:26:49 +00: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 09:45:38 +00: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 18:59:04 +00: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-10 11:30:55 +00:00
if ( g_ogl_config . bSupportViewportFloat )
2013-10-29 14:29:06 +00:00
{
glViewportIndexedf ( 0 , X , Y , Width , Height ) ;
}
else
{
2014-08-20 03:18:02 +00:00
auto iceilf = [ ] ( float f )
{
return static_cast < GLint > ( ceilf ( f ) ) ;
} ;
glViewport ( iceilf ( X ) , iceilf ( Y ) , iceilf ( Width ) , iceilf ( Height ) ) ;
2013-10-29 14:29:06 +00:00
}
2013-05-06 04:33:49 +00: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 ( ) ;
2009-07-15 00:51:24 +00:00
2011-11-30 03:50:31 +00: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-30 03:50:31 +00: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 ) ;
// depth
glDepthMask ( zEnable ? GL_TRUE : GL_FALSE ) ;
2009-07-15 00:51:24 +00:00
2015-05-07 21:29:16 +00:00
glClearDepthf ( float ( z & 0xFFFFFF ) / 16777216.0f ) ;
2011-11-30 03:50:31 +00: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 ( ) ) ;
2009-07-15 00:51:24 +00:00
2011-11-30 03:50:31 +00:00
// 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 04:15:02 +00:00
ClearEFBCache ( ) ;
2009-07-15 00:51:24 +00:00
}
2010-08-10 01:08:22 +00:00
2014-12-20 16:10:58 +00: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 ;
ConvertStereoRectangle ( dst , leftRc , rightRc ) ;
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 13:41:10 +00: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 22:35:07 +00: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 20:44:23 +00:00
bool target_has_alpha = bpmem . zcontrol . pixel_format = = PEControl : : RGBA6_Z24 ;
2013-10-29 05:23:17 +00:00
2013-03-16 23:36:13 +00: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 05:23:17 +00:00
2013-01-13 22:35:07 +00:00
const GLenum glSrcFactors [ 8 ] =
{
GL_ZERO ,
GL_ONE ,
GL_DST_COLOR ,
GL_ONE_MINUS_DST_COLOR ,
2013-03-28 21:32:59 +00:00
( useDualSource ) ? GL_SRC1_ALPHA : ( GLenum ) GL_SRC_ALPHA ,
( useDualSource ) ? GL_ONE_MINUS_SRC1_ALPHA : ( GLenum ) GL_ONE_MINUS_SRC_ALPHA ,
2013-01-13 22:35:07 +00: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 21:32:59 +00:00
( useDualSource ) ? GL_SRC1_ALPHA : ( GLenum ) GL_SRC_ALPHA ,
( useDualSource ) ? GL_ONE_MINUS_SRC1_ALPHA : ( GLenum ) GL_ONE_MINUS_SRC_ALPHA ,
2013-01-13 22:35:07 +00: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-17 23:44:35 +00: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-17 23:44:35 +00: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 18:09:53 +00:00
{
2010-09-28 02:15:02 +00:00
newval | = 0x0049 ; // enable blending src 1 dst 1
2014-08-15 18:09:53 +00: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 18:09:53 +00:00
{
2010-09-28 02:15:02 +00:00
// blend enable change
( newval & 1 ) ? glEnable ( GL_BLEND ) : glDisable ( GL_BLEND ) ;
2014-08-15 18:09:53 +00: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 05:23:17 +00:00
2010-10-23 19:55:19 +00:00
glBlendEquationSeparate ( equation , equationAlpha ) ;
}
2010-09-28 02:15:02 +00:00
2013-01-17 23:44:35 +00:00
if ( changes & 0x1FA )
2010-10-23 19:55:19 +00:00
{
2013-03-28 21:04:33 +00:00
u32 srcidx = ( newval > > 3 ) & 7 ;
u32 dstidx = ( newval > > 6 ) & 7 ;
GLenum srcFactor = glSrcFactors [ srcidx ] ;
GLenum dstFactor = glDestFactors [ dstidx ] ;
2013-03-28 21:59:42 +00:00
// adjust alpha factors
2010-10-23 19:55:19 +00:00
if ( useDualSource )
{
2014-03-24 19:21:34 +00:00
srcidx = BlendMode : : ONE ;
dstidx = BlendMode : : ZERO ;
2013-10-29 05:23:17 +00:00
}
2013-03-28 21:04:33 +00: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 18:09:53 +00:00
if ( srcidx = = BlendMode : : DSTCLR )
srcidx = BlendMode : : DSTALPHA ;
else if ( srcidx = = BlendMode : : INVDSTCLR )
srcidx = BlendMode : : INVDSTALPHA ;
2013-03-28 21:59:42 +00:00
2013-03-28 21:04:33 +00: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 18:09:53 +00:00
if ( dstidx = = BlendMode : : SRCCLR )
dstidx = BlendMode : : SRCALPHA ;
else if ( dstidx = = BlendMode : : INVSRCCLR )
dstidx = BlendMode : : INVSRCALPHA ;
2013-10-29 05:23:17 +00:00
}
2013-03-28 21:04:33 +00: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
}
2014-07-08 12:29:26 +00:00
static void DumpFrame ( const std : : vector < u8 > & data , int w , int h )
2013-02-27 01:47:48 +00:00
{
2015-01-26 01:35:29 +00:00
# if defined(HAVE_LIBAV) || defined(_WIN32)
2014-10-13 03:53:10 +00:00
if ( SConfig : : GetInstance ( ) . m_DumpFrames & & ! data . empty ( ) )
{
AVIDump : : AddFrame ( & data [ 0 ] , w , h ) ;
}
2013-02-27 01:47:48 +00: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 07:08:44 +00: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 20:46:00 +00:00
if ( g_ogl_config . bSupportsDebug )
{
if ( LogManager : : GetInstance ( ) - > IsEnabled ( LogTypes : : VIDEO , LogTypes : : LERROR ) )
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 21:19:50 +00:00
if ( g_bSkipCurrentFrame | | ( ! XFBWrited & & ! g_ActiveConfig . RealXFBEnabled ( ) ) | | ! fbWidth | | ! fbHeight )
2010-01-28 14:37:03 +00:00
{
2013-02-27 01:47:48 +00: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 06:20:46 +00:00
const XFBSourceBase * const * xfbSourceList = FramebufferManager : : GetXFBSource ( xfbAddr , fbStride , fbHeight , & xfbCount ) ;
2012-09-28 21:19:50 +00:00
if ( g_ActiveConfig . VirtualXFBEnabled ( ) & & ( ! xfbSourceList | | xfbCount = = 0 ) )
2009-06-29 07:30:48 +00:00
{
2013-02-27 01:47:48 +00: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-28 22:04:55 +00:00
UpdateDrawRectangle ( s_backbuffer_width , s_backbuffer_height ) ;
TargetRectangle flipped_trc = GetTargetRectangle ( ) ;
2014-10-30 00:50:39 +00: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 05:23:17 +00:00
2013-01-03 19:44:47 +00:00
// Copy the framebuffer to screen.
2014-10-18 07:32:24 +00:00
const XFBSource * xfbSource = nullptr ;
2010-03-10 06:45:13 +00:00
2014-03-10 11:30:55 +00:00
if ( g_ActiveConfig . bUseXFB )
2009-06-26 08:57:53 +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 07:32:24 +00:00
xfbSource = ( const XFBSource * ) xfbSourceList [ i ] ;
2010-03-10 06:45:13 +00:00
2014-10-18 07:32:24 +00:00
TargetRectangle drawRc ;
2010-03-10 06:45:13 +00:00
2012-09-28 21:21:09 +00:00
if ( g_ActiveConfig . bUseRealXFB )
{
2014-10-18 07:32:24 +00:00
drawRc = flipped_trc ;
2012-09-28 21:21:09 +00:00
}
else
2010-03-10 06:45:13 +00:00
{
2010-07-02 17:09:53 +00:00
// use virtual xfb with offset
int xfbHeight = xfbSource - > srcHeight ;
int xfbWidth = xfbSource - > srcWidth ;
2014-05-02 07:08:44 +00:00
int hOffset = ( ( s32 ) xfbSource - > srcAddr - ( s32 ) xfbAddr ) / ( ( s32 ) fbStride * 2 ) ;
2013-10-29 05:23:17 +00:00
2015-01-19 16:01:32 +00:00
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 05:23:17 +00: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-28 22:04:55 +00: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-03-10 06:45:13 +00:00
}
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 ( ) ;
2010-03-10 06:45:13 +00:00
2014-10-18 07:32:24 +00:00
TargetRectangle 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 ;
2015-01-03 22:05:22 +00:00
sourceRc . right - = Renderer : : EFBToScaledX ( fbStride - fbWidth ) ;
2014-05-02 07:08:44 +00:00
2014-12-20 16:10:58 +00: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 05:23:17 +00:00
2013-01-09 00:50:52 +00:00
// for msaa mode, we must resolve the efb content to non-msaa
2014-10-18 07:32:24 +00:00
GLuint tex = FramebufferManager : : ResolveAndGetRenderTarget ( rc ) ;
2014-12-20 16:10:58 +00:00
BlitScreen ( targetRc , flipped_trc , tex , s_target_width , s_target_height ) ;
2009-06-26 08:57:53 +00:00
}
2013-10-29 05:23:17 +00:00
2013-01-03 20:40:54 +00: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-09 23:10:20 +00: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-09 23:10:20 +00: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 11:43:04 +00:00
// Frame dumping disabled entirely on GLES3
2014-01-02 02:06:11 +00:00
if ( GLInterface - > GetMode ( ) = = GLInterfaceMode : : MODE_OPENGL )
2009-06-08 10:16:08 +00:00
{
2015-01-26 01:35:29 +00:00
# if defined _WIN32 || defined HAVE_LIBAV
2014-10-13 03:53:10 +00:00
if ( SConfig : : GetInstance ( ) . m_DumpFrames )
2009-04-03 14:35:49 +00:00
{
2014-01-02 02:06:11 +00: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-02 02:06:11 +00:00
w = flipped_trc . GetWidth ( ) ;
h = flipped_trc . GetHeight ( ) ;
frame_data . resize ( 3 * w * h ) ;
2009-03-28 21:07:16 +00:00
}
2014-01-02 02:06:11 +00:00
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 08:53:22 +00:00
if ( w > 0 & & h > 0 )
2010-11-14 21:14:26 +00:00
{
2014-01-02 02:06:11 +00:00
if ( ! bLastFrameDumped )
{
2015-01-26 01:35:29 +00:00
# ifdef _WIN32
bAVIDumping = AVIDump : : Start ( nullptr , w , h ) ;
# else
bAVIDumping = AVIDump : : Start ( w , h ) ;
# endif
2014-01-02 02:06:11 +00:00
if ( ! bAVIDumping )
2014-08-15 18:09:53 +00:00
{
2014-01-02 02:06:11 +00:00
OSD : : AddMessage ( " AVIDump Start failed " , 2000 ) ;
2014-08-15 18:09:53 +00:00
}
2014-01-02 02:06:11 +00:00
else
{
OSD : : AddMessage ( StringFromFormat (
" Dumping Frames to \" %sframedump0.avi \" (%dx%d RGB24) " ,
2014-03-12 19:33:41 +00:00
File : : GetUserPath ( D_DUMPFRAMES_IDX ) . c_str ( ) , w , h ) , 2000 ) ;
2014-01-02 02:06:11 +00:00
}
}
if ( bAVIDumping )
{
2015-01-26 01:35:29 +00:00
# ifndef _WIN32
FlipImageData ( & frame_data [ 0 ] , w , h ) ;
# endif
AVIDump : : AddFrame ( & frame_data [ 0 ] , w , h ) ;
2014-01-02 02:06:11 +00:00
}
2009-04-03 14:35:49 +00:00
2014-01-02 02:06:11 +00:00
bLastFrameDumped = true ;
}
else
2014-08-15 18:09:53 +00:00
{
2014-01-02 02:06:11 +00:00
NOTICE_LOG ( VIDEO , " Error reading framebuffer " ) ;
2014-08-15 18:09:53 +00: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-02 02:06:11 +00: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 01:35:29 +00: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-02 02:06:11 +00:00
}
2013-05-06 11:43:04 +00:00
# endif
2014-01-02 02:06:11 +00: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 07:08:44 +00:00
SetWindowSize ( fbStride , fbHeight ) ;
2011-01-07 04:57:59 +00:00
2012-12-17 20:54:20 +00:00
GLInterface - > Update ( ) ; // just updates the render window position and the backbuffer size
2011-05-07 22:00:05 +00:00
2014-12-28 17:26:25 +00:00
bool xfbchanged = s_last_xfb_mode ! = g_ActiveConfig . bUseRealXFB ;
2010-09-28 02:15:02 +00:00
2014-05-02 07:08:44 +00:00
if ( FramebufferManagerBase : : LastXfbWidth ( ) ! = fbStride | | FramebufferManagerBase : : LastXfbHeight ( ) ! = fbHeight )
2010-05-19 03:15:36 +00:00
{
xfbchanged = true ;
2014-05-02 07:08:44 +00:00
unsigned int const last_w = ( fbStride < 1 | | fbStride > MAX_XFB_WIDTH ) ? MAX_XFB_WIDTH : fbStride ;
2013-01-29 22:40:15 +00: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 20:54:20 +00:00
int W = ( int ) GLInterface - > GetBackBufferWidth ( ) ;
int H = ( int ) GLInterface - > GetBackBufferHeight ( ) ;
2014-12-28 17:35:23 +00: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 17:35:23 +00:00
s_last_efb_scale = g_ActiveConfig . iEFBScale ;
2010-05-19 03:15:36 +00:00
}
2014-12-28 17:35:23 +00:00
if ( xfbchanged | | WindowResized | | ( 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 17:26:25 +00:00
s_last_xfb_mode = g_ActiveConfig . bUseRealXFB ;
2012-09-28 22:04:55 +00:00
UpdateDrawRectangle ( s_backbuffer_width , s_backbuffer_height ) ;
2010-08-10 01:08:22 +00:00
2014-12-28 17:35:23 +00:00
if ( CalculateTargetSize ( s_backbuffer_width , s_backbuffer_height ) | | s_last_multisample_mode ! = g_ActiveConfig . iMultisampleMode | | s_last_stereo_mode ! = ( g_ActiveConfig . iStereoMode > 0 ) )
2010-05-19 03:15:36 +00:00
{
2015-01-03 00:35:39 +00:00
s_last_stereo_mode = g_ActiveConfig . iStereoMode > 0 ;
2014-12-28 17:35:23 +00:00
s_last_multisample_mode = g_ActiveConfig . iMultisampleMode ;
s_MSAASamples = GetNumMSAASamples ( s_last_multisample_mode ) ;
2013-03-25 14:45:10 +00:00
ApplySSAASettings ( ) ;
2013-10-29 05:23:17 +00: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 16:13:31 +00:00
s_MSAASamples ) ;
2015-01-02 17:06:56 +00:00
PixelShaderManager : : SetEfbScaleChanged ( ) ;
2010-05-19 03:15:36 +00:00
}
}
2009-06-07 12:06:15 +00:00
// ---------------------------------------------------------------------
2013-09-18 07:36:48 +00:00
if ( ! DriverDetails : : HasBug ( DriverDetails : : BUG_BROKENSWAP ) )
{
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
2009-06-07 12:06:15 +00:00
2014-12-20 12:02:00 +00:00
// Reset viewport for drawing text
glViewport ( 0 , 0 , GLInterface - > GetBackBufferWidth ( ) , GLInterface - > GetBackBufferHeight ( ) ) ;
ShowEfbCopyRegions ( ) ;
2013-09-18 07:36:48 +00:00
DrawDebugText ( ) ;
// Do our OSD callbacks
OSD : : DoCallbacks ( OSD : : OSD_ONFRAME ) ;
OSD : : DrawMessages ( ) ;
}
2010-06-05 01:38:22 +00:00
// Copy the rendered frame to the real window
2012-12-26 06:34:09 +00:00
GLInterface - > Swap ( ) ;
2010-08-10 01:08:22 +00:00
2009-06-08 20:07:20 +00:00
// Clear framebuffer
2013-09-18 07:36:48 +00:00
if ( ! DriverDetails : : HasBug ( DriverDetails : : BUG_BROKENSWAP ) )
{
glClearColor ( 0 , 0 , 0 , 0 ) ;
2014-12-20 14:17:20 +00:00
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
2013-09-18 07:36:48 +00:00
}
2009-06-07 12:06:15 +00:00
2014-03-10 11:30:55 +00:00
if ( s_vsync ! = g_ActiveConfig . IsVSync ( ) )
2013-03-30 21:17:39 +00:00
{
s_vsync = g_ActiveConfig . IsVSync ( ) ;
GLInterface - > SwapInterval ( s_vsync ) ;
}
2013-03-19 00:41:45 +00: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 16:31:38 +00: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 09:31:37 +00: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 02:59:59 +00:00
// SaveTexture("tex.png", GL_TEXTURE_2D, s_FakeZTarget,
2010-11-18 02:21:26 +00:00
// GetTargetWidth(), GetTargetHeight());
2011-08-22 04:15:02 +00: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 13:22:50 +00: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-02-23 07:23:34 +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 20:04:28 +00: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 21:21:56 +00:00
SetLogicOpMode ( ) ;
2014-02-04 09:45:38 +00:00
SetViewport ( ) ;
2009-03-16 07:54:44 +00:00
2012-12-15 13:43:01 +00:00
VertexManager * vm = ( OGL : : VertexManager * ) g_vertex_manager ;
2012-12-15 16:28:58 +00:00
glBindBuffer ( GL_ARRAY_BUFFER , vm - > m_vertex_buffers ) ;
2014-09-03 04:08:50 +00:00
if ( vm - > m_last_vao )
glBindVertexArray ( vm - > m_last_vao ) ;
2013-10-29 05:23:17 +00:00
2013-01-18 23:39:31 +00:00
TextureCache : : SetStage ( ) ;
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 : : SetGenerationMode ( )
{
// none, ccw, cw, ccw
if ( bpmem . genMode . cullmode > 0 )
{
2013-12-30 19:37:59 +00: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 18:09:53 +00:00
{
2010-09-28 02:15:02 +00:00
glDisable ( GL_CULL_FACE ) ;
2014-08-15 18:09:53 +00: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 22:35:07 +00: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-10-22 20:22:50 +00:00
2010-09-28 02:15:02 +00:00
void Renderer : : SetLogicOpMode ( )
{
2014-01-02 02:06:11 +00:00
if ( GLInterface - > GetMode ( ) ! = GLInterfaceMode : : MODE_OPENGL )
return ;
2013-05-06 04:33:49 +00:00
// Logic ops aren't available in GLES3/GLES2
2013-01-13 22:35:07 +00: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-16 04:08:54 +00: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 ) ;
}
}
2009-10-22 20:22:50 +00:00
2010-09-28 02:15:02 +00:00
void Renderer : : SetDitherMode ( )
{
if ( bpmem . blendmode . dither )
glEnable ( GL_DITHER ) ;
else
glDisable ( GL_DITHER ) ;
}
2009-10-22 20:22:50 +00:00
2015-03-01 12:04:48 +00:00
void Renderer : : SetSamplerState ( int stage , int texindex , bool custom_tex )
2010-09-28 02:15:02 +00:00
{
2013-02-20 00:22:38 +00:00
auto const & tex = bpmem . tex [ texindex ] ;
auto const & tm0 = tex . texMode0 [ stage ] ;
auto const & tm1 = tex . texMode1 [ stage ] ;
2013-10-29 05:23:17 +00:00
2015-03-01 12:04:48 +00:00
g_sampler_cache - > SetSamplerState ( ( texindex * 4 ) + stage , tm0 , tm1 , custom_tex ) ;
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 : : SetInterlacingMode ( )
2009-02-23 07:23:34 +00:00
{
2010-09-28 02:15:02 +00:00
// TODO
2009-02-23 07:23:34 +00:00
}
2013-11-15 00:00:38 +00:00
void Renderer : : FlipImageData ( u8 * data , int w , int h , int pixel_width )
2010-09-28 02:15:02 +00:00
{
2013-11-09 23:10:20 +00:00
// Flip image upside down. Damn OpenGL.
2013-11-15 00:00:38 +00:00
for ( int y = 0 ; y < h / 2 ; + + y )
2010-09-28 02:15:02 +00:00
{
2014-03-10 11:30:55 +00:00
for ( int x = 0 ; x < w ; + + x )
2010-09-28 02:15:02 +00:00
{
2014-02-12 15:00:34 +00:00
for ( int delta = 0 ; delta < pixel_width ; + + delta )
2013-11-15 00:00:38 +00: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-09 23:10:20 +00:00
}
namespace OGL
{
bool Renderer : : SaveScreenshot ( const std : : string & filename , const TargetRectangle & back_rc )
2009-02-27 03:56:34 +00:00
{
2010-07-09 20:56:16 +00:00
u32 W = back_rc . GetWidth ( ) ;
u32 H = back_rc . GetHeight ( ) ;
2014-09-26 03:47:34 +00: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-26 03:47:34 +00: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-26 03:47:34 +00:00
FlipImageData ( data . get ( ) , W , H , 4 ) ;
2013-11-16 21:34:34 +00:00
2014-09-26 03:47:34 +00:00
return TextureToPng ( data . get ( ) , W * 4 , filename , W , H , false ) ;
2013-11-16 21:34:34 +00:00
2009-03-28 21:07:16 +00:00
}
2010-11-18 02:21:26 +00:00
2014-09-25 23:50:25 +00: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
}