2009-04-03 14:35:49 +00:00
// Copyright (C) 2003-2009 Dolphin Project.
2009-02-23 07:23:34 +00:00
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
2009-06-07 12:06:15 +00:00
2009-02-23 07:23:34 +00:00
#include "Globals.h"
2009-03-05 23:11:13 +00:00
2009-02-23 07:23:34 +00:00
#include <vector>
#include <cmath>
2009-03-28 21:07:16 +00:00
#include <cstdio>
2009-02-23 07:23:34 +00:00
#include "GLUtil.h"
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#ifdef _WIN32
#include <mmsystem.h>
#endif
2009-03-28 21:07:16 +00:00
#include "CommonPaths.h"
2009-02-23 07:23:34 +00:00
#include "Config.h"
#include "Profiler.h"
#include "Statistics.h"
#include "ImageWrite.h"
#include "Render.h"
#include "OpcodeDecoding.h"
2009-06-22 09:31:30 +00:00
#include "BPStructs.h"
2009-02-23 07:23:34 +00:00
#include "TextureMngr.h"
#include "rasterfont.h"
#include "VertexShaderGen.h"
#include "PixelShaderCache.h"
#include "PixelShaderManager.h"
2009-02-28 16:33:59 +00:00
#include "VertexShaderCache.h"
#include "VertexShaderManager.h"
2009-02-23 07:23:34 +00:00
#include "VertexLoaderManager.h"
#include "VertexLoader.h"
2009-06-08 19:42:25 +00:00
#include "PostProcessing.h"
2009-06-26 08:57:53 +00:00
#include "TextureConverter.h"
2009-02-23 07:23:34 +00:00
#include "XFB.h"
#include "OnScreenDisplay.h"
#include "Timer.h"
2009-06-06 13:36:33 +00:00
#include "StringUtil.h"
2009-06-28 23:35:08 +00:00
#include "FramebufferManager.h"
2009-02-23 07:23:34 +00:00
#include "main.h" // Local
#ifdef _WIN32
#include "OS/Win32.h"
2009-03-28 21:07:16 +00:00
#include "AVIDump.h"
2009-02-23 07:23:34 +00:00
#endif
2009-02-27 03:56:34 +00:00
#if defined(HAVE_WX) && HAVE_WX
#include <wx/image.h>
#endif
2009-02-23 07:23:34 +00:00
#ifdef _WIN32
#include "Win32Window.h" // warning: crapcode
#else
#endif
2009-06-07 12:06:15 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
// Declarations and definitions
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
2009-02-23 07:23:34 +00:00
CGcontext g_cgcontext ;
CGprofile g_cgvProf ;
CGprofile g_cgfProf ;
RasterFont * s_pfont = NULL ;
static bool s_bFullscreen = false ;
2009-03-28 21:07:16 +00:00
static bool s_bLastFrameDumped = false ;
#ifdef _WIN32
static bool s_bAVIDumping = false ;
#else
static FILE * f_pFrameDump ;
#endif
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 ;
2009-03-08 20:04:40 +00:00
static int s_MSAACoverageSamples = 0 ;
2009-03-05 23:11:13 +00:00
2009-02-23 07:23:34 +00:00
static bool s_bHaveStencilBuffer = false ;
2009-06-29 21:54:42 +00:00
bool s_bHaveFramebufferBlit = false ; // export to FramebufferManager.cpp
2009-03-08 20:04:40 +00:00
static bool s_bHaveCoverageMSAA = false ;
2009-03-08 19:19:51 +00:00
static u32 s_blendMode ;
2009-03-08 21:42:27 +00:00
static bool s_bNativeResolution = false ;
2009-02-23 07:23:34 +00:00
2009-02-28 16:33:59 +00:00
static volatile bool s_bScreenshot = false ;
2009-02-27 03:56:34 +00:00
static Common :: CriticalSection s_criticalScreenshot ;
static std :: string s_sScreenshotName ;
2009-02-23 07:23:34 +00:00
int frameCount ;
2009-02-28 16:33:59 +00:00
static int s_fps = 0 ;
2009-03-08 19:19:51 +00:00
// These STAY CONSTANT during execution, no matter how much you resize the game window.
2009-03-05 23:11:13 +00:00
// TODO: Add functionality to reinit all the render targets when the window is resized.
2009-02-28 16:33:59 +00:00
static int s_targetwidth ; // Size of render buffer FBO.
static int s_targetheight ;
2009-06-28 23:35:08 +00:00
static FramebufferManager s_framebufferManager ;
2009-06-29 20:54:47 +00:00
static GLuint s_tempScreenshotFramebuffer = 0 ;
2009-06-28 23:35:08 +00:00
2009-06-09 21:29:54 +00:00
#ifndef _WIN32
int OSDChoice = 0 , OSDTime = 0 , OSDInternalW = 0 , OSDInternalH = 0 ;
#endif
2009-02-23 07:23:34 +00:00
2009-03-05 23:11:13 +00:00
namespace {
2009-02-23 07:23:34 +00:00
static const GLenum glSrcFactors [ 8 ] =
{
GL_ZERO ,
GL_ONE ,
GL_DST_COLOR ,
GL_ONE_MINUS_DST_COLOR ,
GL_SRC_ALPHA ,
GL_ONE_MINUS_SRC_ALPHA ,
GL_DST_ALPHA ,
GL_ONE_MINUS_DST_ALPHA
};
static const GLenum glDestFactors [ 8 ] = {
GL_ZERO , GL_ONE , GL_SRC_COLOR , GL_ONE_MINUS_SRC_COLOR ,
GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA , GL_DST_ALPHA , GL_ONE_MINUS_DST_ALPHA
};
2009-03-05 23:11:13 +00:00
void SetDefaultRectTexParams ()
{
// Set some standard texture filter modes.
glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE );
glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE );
if ( glGetError () != GL_NO_ERROR ) {
glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_S , GL_CLAMP );
glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_T , GL_CLAMP );
GL_REPORT_ERROR ();
}
glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
}
2009-04-08 17:58:58 +00:00
void HandleCgError ( CGcontext ctx , CGerror err , void * appdata )
{
2009-06-09 19:40:47 +00:00
DEBUG_LOG ( VIDEO , "Cg error: %s" , cgGetErrorString ( err ));
2009-04-08 17:58:58 +00:00
const char * listing = cgGetLastListing ( g_cgcontext );
if ( listing != NULL ) {
2009-06-09 19:40:47 +00:00
DEBUG_LOG ( VIDEO , " last listing: %s" , listing );
2009-04-08 17:58:58 +00:00
}
}
2009-03-05 23:11:13 +00:00
2009-06-07 12:06:15 +00:00
} // namespace
2009-03-05 23:11:13 +00:00
2009-06-07 12:06:15 +00:00
// Init functions
2009-02-23 07:23:34 +00:00
bool Renderer :: Init ()
{
bool bSuccess = true ;
2009-02-28 16:33:59 +00:00
s_blendMode = 0 ;
2009-03-08 20:04:40 +00:00
s_MSAACoverageSamples = 0 ;
2009-03-08 21:42:27 +00:00
s_bNativeResolution = g_Config . bNativeResolution ;
2009-03-08 20:04:40 +00:00
switch ( g_Config . iMultisampleMode )
{
case MULTISAMPLE_OFF : s_MSAASamples = 1 ; break ;
case MULTISAMPLE_2X : s_MSAASamples = 2 ; break ;
case MULTISAMPLE_4X : s_MSAASamples = 4 ; break ;
case MULTISAMPLE_8X : s_MSAASamples = 8 ; break ;
case MULTISAMPLE_CSAA_8X : s_MSAASamples = 4 ; s_MSAACoverageSamples = 8 ; break ;
case MULTISAMPLE_CSAA_8XQ : s_MSAASamples = 8 ; s_MSAACoverageSamples = 8 ; break ;
case MULTISAMPLE_CSAA_16X : s_MSAASamples = 4 ; s_MSAACoverageSamples = 16 ; break ;
case MULTISAMPLE_CSAA_16XQ : s_MSAASamples = 8 ; s_MSAACoverageSamples = 16 ; break ;
default :
s_MSAASamples = 1 ;
}
2009-02-28 16:33:59 +00:00
GLint numvertexattribs = 0 ;
2009-02-23 07:23:34 +00:00
g_cgcontext = cgCreateContext ();
cgGetError ();
2009-03-08 19:19:51 +00:00
cgSetErrorHandler ( HandleCgError , NULL );
2009-03-05 23:11:13 +00:00
2009-02-28 16:33:59 +00:00
// Look for required extensions.
const char * ptoken = ( const char * ) glGetString ( GL_EXTENSIONS );
if ( ! ptoken )
{
2009-04-03 21:32:10 +00:00
PanicAlert ( "Your OpenGL Driver seems to be not working. \n "
"Please make sure your drivers are up-to-date and \n "
2009-04-03 21:01:05 +00:00
"that your video hardware is OpenGL 2.x compatible "
);
2009-02-28 16:33:59 +00:00
return false ;
}
2009-03-21 20:07:56 +00:00
INFO_LOG ( VIDEO , "Supported OpenGL Extensions:" );
2009-02-28 01:26:56 +00:00
INFO_LOG ( VIDEO , ptoken ); // write to the log file
2009-03-21 20:07:56 +00:00
INFO_LOG ( VIDEO , "" );
2009-02-23 07:23:34 +00:00
2009-04-03 20:22:45 +00:00
OSD :: AddMessage ( StringFromFormat ( "Video Info: %s, %s, %s" , ( const char * ) glGetString ( GL_VENDOR ),
( const char * ) glGetString ( GL_RENDERER ),
( const char * ) glGetString ( GL_VERSION )). c_str (), 5000 );
2009-02-23 07:23:34 +00:00
s_bFullscreen = g_Config . bFullscreen ;
2009-02-28 16:33:59 +00:00
glGetIntegerv ( GL_MAX_VERTEX_ATTRIBS , & numvertexattribs );
2009-02-23 07:23:34 +00:00
if ( numvertexattribs < 11 ) {
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "********* \n GPU: OGL ERROR: Number of attributes %d not enough \n GPU: *********Does your video card support OpenGL 2.x?" , numvertexattribs );
2009-02-23 07:23:34 +00:00
bSuccess = false ;
}
2009-02-28 16:33:59 +00:00
// Init extension support.
if ( glewInit () != GLEW_OK ) {
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "glewInit() failed!Does your video card support OpenGL 2.x?" );
2009-02-28 16:33:59 +00:00
return false ;
}
if ( ! GLEW_EXT_framebuffer_object ) {
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "********* \n GPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets \n GPU: *********Does your video card support OpenGL 2.x?" );
2009-02-28 16:33:59 +00:00
bSuccess = false ;
}
if ( ! GLEW_EXT_secondary_color ) {
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "********* \n GPU: OGL ERROR: Need GL_EXT_secondary_color \n GPU: *********Does your video card support OpenGL 2.x?" );
2009-02-28 16:33:59 +00:00
bSuccess = false ;
}
2009-03-08 19:19:51 +00:00
s_bHaveFramebufferBlit = strstr ( ptoken , "GL_EXT_framebuffer_blit" ) != NULL ;
if ( ! s_bHaveFramebufferBlit )
{
// MSAA ain't gonna work. turn it off if enabled.
s_MSAASamples = 1 ;
}
2009-03-08 20:04:40 +00:00
s_bHaveCoverageMSAA = strstr ( ptoken , "GL_NV_framebuffer_multisample_coverage" ) != NULL ;
if ( ! s_bHaveCoverageMSAA )
{
s_MSAACoverageSamples = 0 ;
}
2009-02-23 07:23:34 +00:00
if ( ! bSuccess )
return false ;
2009-03-05 23:11:13 +00:00
// Handle VSync on/off
2009-02-23 07:23:34 +00:00
#ifdef _WIN32
2009-02-28 16:33:59 +00:00
if ( WGLEW_EXT_swap_control )
2009-03-05 23:11:13 +00:00
wglSwapIntervalEXT ( g_Config . bVSync ? 1 : 0 );
2009-02-28 16:33:59 +00:00
else
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "no support for SwapInterval (framerate clamped to monitor refresh rate) Does your video card support OpenGL 2. x ? ") ;
2009-02-23 07:23:34 +00:00
#elif defined(HAVE_X11) && HAVE_X11
2009-02-28 16:33:59 +00:00
if ( glXSwapIntervalSGI )
2009-03-05 23:11:13 +00:00
glXSwapIntervalSGI ( g_Config . bVSync ? 1 : 0 );
2009-02-28 16:33:59 +00:00
else
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "no support for SwapInterval (framerate clamped to monitor refresh rate)") ;
2009-02-23 07:23:34 +00:00
#endif
// 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 ) {
2009-02-28 01:26:56 +00:00
ERROR_LOG ( VIDEO , "GL_MAX_TEXTURE_SIZE too small at %i - must be at least 1024" , max_texture_size );
2009-02-23 07:23:34 +00:00
}
2009-03-22 11:21:44 +00:00
if ( GL_REPORT_ERROR () != GL_NO_ERROR )
bSuccess = false ;
2009-02-23 07:23:34 +00:00
if ( glDrawBuffers == NULL && ! GLEW_ARB_draw_buffers )
glDrawBuffers = glDrawBuffersARB ;
2009-03-22 11:21:44 +00:00
if ( ! GLEW_ARB_texture_non_power_of_two ) {
WARN_LOG ( VIDEO , "ARB_texture_non_power_of_two not supported. This extension is not yet used, though." );
}
2009-02-23 07:23:34 +00:00
2009-06-28 23:35:08 +00:00
if ( g_Config . bNativeResolution )
{
s_targetwidth = g_Config . b2xResolution ? EFB_WIDTH * 2 : EFB_WIDTH ;
s_targetheight = g_Config . b2xResolution ? EFB_HEIGHT * 2 : EFB_HEIGHT ;
}
else
{
// The size of the framebuffer targets should really NOT be the size of the OpenGL viewport.
// The EFB is larger than 640x480 - in fact, it's 640x528, give or take a couple of lines.
// So the below is wrong.
// This should really be grabbed from config rather than from OpenGL.
// JP: Set these to the biggest of the 2x mode and the custom resolution so that the framebuffer
// does not get to be too small
int W = ( int ) OpenGL_GetBackbufferWidth (), H = ( int ) OpenGL_GetBackbufferHeight ();
s_targetwidth = ( 1280 >= W ) ? 1280 : W ;
s_targetheight = ( 960 >= H ) ? 960 : H ;
2009-06-08 20:07:20 +00:00
2009-06-28 23:35:08 +00:00
// Compensate height of render target for scaling, so that we get something close to the correct number of
// vertical pixels.
s_targetheight *= 528.0 / 480.0 ;
}
2009-02-28 19:02:37 +00:00
// Ensure a minimum target size so that the native res target always fits.
if ( s_targetwidth < EFB_WIDTH )
s_targetwidth = EFB_WIDTH ;
if ( s_targetheight < EFB_HEIGHT )
s_targetheight = EFB_HEIGHT ;
2009-02-28 16:33:59 +00:00
2009-03-22 11:21:44 +00:00
if ( GL_REPORT_ERROR () != GL_NO_ERROR )
bSuccess = false ;
2009-06-28 23:35:08 +00:00
// Initialize the FramebufferManager
s_framebufferManager . Init ( s_targetwidth , s_targetheight , s_MSAASamples , s_MSAACoverageSamples );
2009-03-08 19:19:51 +00:00
glDrawBuffer ( GL_COLOR_ATTACHMENT0_EXT );
2009-02-23 07:23:34 +00:00
2009-03-22 11:21:44 +00:00
if ( GL_REPORT_ERROR () != GL_NO_ERROR )
2009-02-23 07:23:34 +00:00
bSuccess = false ;
s_pfont = new RasterFont ();
// load the effect, find the best profiles (if any)
if ( cgGLIsProfileSupported ( CG_PROFILE_ARBVP1 ) != CG_TRUE ) {
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "arbvp1 not supported" );
2009-02-23 07:23:34 +00:00
return false ;
}
if ( cgGLIsProfileSupported ( CG_PROFILE_ARBFP1 ) != CG_TRUE ) {
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "arbfp1 not supported" );
2009-02-23 07:23:34 +00:00
return false ;
}
g_cgvProf = cgGLGetLatestProfile ( CG_GL_VERTEX );
g_cgfProf = cgGLGetLatestProfile ( CG_GL_FRAGMENT );
cgGLSetOptimalOptions ( g_cgvProf );
cgGLSetOptimalOptions ( g_cgfProf );
2009-03-21 20:07:56 +00:00
INFO_LOG ( VIDEO , "Max buffer sizes: %d %d" , cgGetProgramBufferMaxSize ( g_cgvProf ), cgGetProgramBufferMaxSize ( g_cgfProf ));
2009-02-23 07:23:34 +00:00
int nenvvertparams , nenvfragparams , naddrregisters [ 2 ];
2009-03-05 23:11:13 +00:00
glGetProgramivARB ( GL_VERTEX_PROGRAM_ARB , GL_MAX_PROGRAM_ENV_PARAMETERS_ARB , ( GLint * ) & nenvvertparams );
glGetProgramivARB ( GL_FRAGMENT_PROGRAM_ARB , GL_MAX_PROGRAM_ENV_PARAMETERS_ARB , ( GLint * ) & nenvfragparams );
glGetProgramivARB ( GL_VERTEX_PROGRAM_ARB , GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB , ( GLint * ) & naddrregisters [ 0 ]);
2009-02-23 07:23:34 +00:00
glGetProgramivARB ( GL_FRAGMENT_PROGRAM_ARB , GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB , ( GLint * ) & naddrregisters [ 1 ]);
2009-03-21 20:07:56 +00:00
DEBUG_LOG ( VIDEO , "Max program env parameters: vert=%d, frag=%d" , nenvvertparams , nenvfragparams );
DEBUG_LOG ( VIDEO , "Max program address register parameters: vert=%d, frag=%d" , naddrregisters [ 0 ], naddrregisters [ 1 ]);
2009-02-23 07:23:34 +00:00
if ( nenvvertparams < 238 )
2009-03-21 20:07:56 +00:00
ERROR_LOG ( VIDEO , "Not enough vertex shader environment constants!!" );
2009-02-23 07:23:34 +00:00
#ifndef _DEBUG
cgGLSetDebugMode ( GL_FALSE );
#endif
if ( ! InitializeGL ())
return false ;
return glGetError () == GL_NO_ERROR && bSuccess ;
}
2009-02-28 16:33:59 +00:00
2009-02-23 07:23:34 +00:00
void Renderer :: Shutdown ( void )
{
delete s_pfont ;
s_pfont = 0 ;
if ( g_cgcontext ) {
cgDestroyContext ( g_cgcontext );
g_cgcontext = 0 ;
2009-06-26 08:57:53 +00:00
}
2009-06-29 20:54:47 +00:00
glDeleteFramebuffersEXT ( 1 , & s_tempScreenshotFramebuffer );
s_tempScreenshotFramebuffer = 0 ;
2009-06-28 23:35:08 +00:00
s_framebufferManager . Shutdown ();
2009-03-28 21:07:16 +00:00
#ifdef _WIN32
if ( s_bAVIDumping ) {
AVIDump :: Stop ();
}
#else
if ( f_pFrameDump != NULL ) {
fclose ( f_pFrameDump );
}
#endif
2009-02-23 07:23:34 +00:00
}
bool Renderer :: InitializeGL ()
{
glStencilFunc ( GL_ALWAYS , 0 , 0 );
glBlendFunc ( GL_ONE , GL_ONE );
glViewport ( 0 , 0 , GetTargetWidth (), GetTargetHeight ()); // Reset The Current Viewport
glMatrixMode ( GL_PROJECTION );
glLoadIdentity ();
glMatrixMode ( GL_MODELVIEW );
glLoadIdentity ();
glShadeModel ( GL_SMOOTH );
glClearColor ( 0.0f , 0.0f , 0.0f , 0.0f );
glClearDepth ( 1.0f );
glEnable ( GL_DEPTH_TEST );
glDisable ( GL_LIGHTING );
glDepthFunc ( GL_LEQUAL );
glPixelStorei ( GL_UNPACK_ALIGNMENT , 4 ); // 4-byte pixel alignment
glDisable ( GL_STENCIL_TEST );
glEnable ( GL_SCISSOR_TEST );
2009-03-05 23:11:13 +00:00
glScissor ( 0 , 0 , GetTargetWidth (), GetTargetHeight ());
2009-02-23 07:23:34 +00:00
glBlendColorEXT ( 0 , 0 , 0 , 0.5f );
glClearDepth ( 1.0f );
glMatrixMode ( GL_PROJECTION );
glLoadIdentity ();
glMatrixMode ( GL_MODELVIEW );
glLoadIdentity ();
// legacy multitexturing: select texture channel only.
glActiveTexture ( GL_TEXTURE0 );
glClientActiveTexture ( GL_TEXTURE0 );
glTexEnvf ( GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE , GL_REPLACE );
2009-03-22 11:21:44 +00:00
return GL_REPORT_ERROR () == GL_NO_ERROR ;
2009-02-23 07:23:34 +00:00
}
// Return the rendering window width and height
int Renderer :: GetTargetWidth ()
{
2009-06-28 23:35:08 +00:00
return s_targetwidth ;
2009-02-23 07:23:34 +00:00
}
int Renderer :: GetTargetHeight ()
{
2009-06-28 23:35:08 +00:00
return s_targetheight ;
2009-02-23 07:23:34 +00:00
}
2009-02-28 16:33:59 +00:00
float Renderer :: GetTargetScaleX ()
{
2009-02-28 19:02:37 +00:00
return ( float ) GetTargetWidth () / ( float ) EFB_WIDTH ;
2009-02-28 16:33:59 +00:00
}
float Renderer :: GetTargetScaleY ()
{
2009-02-28 19:02:37 +00:00
return ( float ) GetTargetHeight () / ( float ) EFB_HEIGHT ;
2009-02-28 16:33:59 +00:00
}
2009-02-23 07:23:34 +00:00
void Renderer :: SetFramebuffer ( GLuint fb )
{
2009-06-28 23:35:08 +00:00
glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT , fb != 0 ? fb : s_framebufferManager . GetEFBFramebuffer ());
2009-02-23 07:23:34 +00:00
}
void Renderer :: ResetGLState ()
{
2009-02-28 19:02:37 +00:00
// Gets us to a reasonably sane state where it's possible to do things like
// image copies with textured quads, etc.
2009-02-23 07:23:34 +00:00
glDisable ( GL_SCISSOR_TEST );
glDisable ( GL_DEPTH_TEST );
glDisable ( GL_CULL_FACE );
glDisable ( GL_BLEND );
glDepthMask ( GL_FALSE );
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE );
glDisable ( GL_VERTEX_PROGRAM_ARB );
glDisable ( GL_FRAGMENT_PROGRAM_ARB );
}
2009-06-26 08:57:53 +00:00
void UpdateViewport ();
2009-02-23 07:23:34 +00:00
void Renderer :: RestoreGLState ()
{
2009-02-28 19:02:37 +00:00
// Gets us back into a more game-like state.
2009-06-26 08:57:53 +00:00
UpdateViewport ();
2009-06-22 09:31:30 +00:00
if ( bpmem . genMode . cullmode > 0 ) glEnable ( GL_CULL_FACE );
if ( bpmem . zmode . testenable ) glEnable ( GL_DEPTH_TEST );
if ( bpmem . zmode . updateenable ) glDepthMask ( GL_TRUE );
2009-02-23 07:23:34 +00:00
2009-03-05 23:11:13 +00:00
glEnable ( GL_SCISSOR_TEST );
SetScissorRect ();
2009-02-23 07:23:34 +00:00
SetColorMask ();
SetBlendMode ( true );
2009-03-05 23:11:13 +00:00
glEnable ( GL_VERTEX_PROGRAM_ARB );
glEnable ( GL_FRAGMENT_PROGRAM_ARB );
2009-02-23 07:23:34 +00:00
}
void Renderer :: SetColorMask ()
{
2009-06-22 09:31:30 +00:00
if ( bpmem . blendmode . alphaupdate && bpmem . blendmode . colorupdate )
2009-02-28 19:02:37 +00:00
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE );
2009-06-22 09:31:30 +00:00
else if ( bpmem . blendmode . alphaupdate )
2009-02-28 19:02:37 +00:00
glColorMask ( GL_FALSE , GL_FALSE , GL_FALSE , GL_TRUE );
2009-06-22 09:31:30 +00:00
else if ( bpmem . blendmode . colorupdate )
2009-02-28 19:02:37 +00:00
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_FALSE );
else
glColorMask ( GL_FALSE , GL_FALSE , GL_FALSE , GL_FALSE );
2009-02-23 07:23:34 +00:00
}
void Renderer :: SetBlendMode ( bool forceUpdate )
{
// blend mode bit mask
// 0 - blend enable
// 2 - reverse subtract enable (else add)
// 3-5 - srcRGB function
// 6-8 - dstRGB function
2009-06-22 09:31:30 +00:00
u32 newval = bpmem . blendmode . subtract << 2 ;
2009-02-23 07:23:34 +00:00
2009-06-22 09:31:30 +00:00
if ( bpmem . blendmode . subtract ) {
2009-03-16 02:47:48 +00:00
newval |= 0x0049 ; // enable blending src 1 dst 1
2009-06-22 09:31:30 +00:00
} else if ( bpmem . blendmode . blendenable ) {
2009-03-16 02:47:48 +00:00
newval |= 1 ; // enable blending
2009-06-22 09:31:30 +00:00
newval |= bpmem . blendmode . srcfactor << 3 ;
newval |= bpmem . blendmode . dstfactor << 6 ;
2009-02-23 07:23:34 +00:00
}
2009-03-16 02:47:48 +00:00
2009-02-23 07:23:34 +00:00
u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode ;
if ( changes & 1 ) {
2009-03-16 02:47:48 +00:00
// blend enable change
2009-03-05 23:11:13 +00:00
( newval & 1 ) ? glEnable ( GL_BLEND ) : glDisable ( GL_BLEND );
2009-02-23 07:23:34 +00:00
}
2009-03-16 02:47:48 +00:00
if ( changes & 4 ) {
// subtract enable change
glBlendEquation ( newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD );
2009-02-23 07:23:34 +00:00
}
2009-03-16 02:47:48 +00:00
if ( changes & 0x1F8 ) {
// blend RGB change
glBlendFunc ( glSrcFactors [( newval >> 3 ) & 7 ], glDestFactors [( newval >> 6 ) & 7 ]);
2009-02-23 07:23:34 +00:00
}
s_blendMode = newval ;
}
2009-06-08 00:44:48 +00:00
// Apply AA if enabled
GLuint Renderer :: ResolveAndGetRenderTarget ( const TRectangle & source_rect )
{
2009-06-28 23:35:08 +00:00
return s_framebufferManager . GetEFBColorTexture ( source_rect );
2009-06-08 00:44:48 +00:00
}
GLuint Renderer :: ResolveAndGetDepthTarget ( const TRectangle & source_rect )
{
2009-06-28 23:35:08 +00:00
return s_framebufferManager . GetEFBDepthTexture ( source_rect );
2009-06-08 00:44:48 +00:00
}
2009-06-09 21:29:54 +00:00
/////////////////////////////////////////////////////////////////////////////
2009-06-07 12:06:15 +00:00
// Function: This function handles the OpenGL glScissor() function
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
2009-02-23 07:23:34 +00:00
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
// case 0x52 > SetScissorRect()
2009-06-07 12:06:15 +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
bool Renderer :: SetScissorRect ()
{
2009-06-22 09:31:30 +00:00
int xoff = bpmem . scissorOffset . x * 2 - 342 ;
int yoff = bpmem . scissorOffset . y * 2 - 342 ;
2009-02-28 16:33:59 +00:00
float MValueX = GetTargetScaleX ();
float MValueY = GetTargetScaleY ();
2009-06-22 09:31:30 +00:00
float rc_left = ( float ) bpmem . scissorTL . x - xoff - 342 ; // left = 0
2009-02-23 07:23:34 +00:00
rc_left *= MValueX ;
if ( rc_left < 0 ) rc_left = 0 ;
2009-06-22 09:31:30 +00:00
float rc_top = ( float ) bpmem . scissorTL . y - yoff - 342 ; // right = 0
2009-02-23 07:23:34 +00:00
rc_top *= MValueY ;
if ( rc_top < 0 ) rc_top = 0 ;
2009-06-22 09:31:30 +00:00
float rc_right = ( float ) bpmem . scissorBR . x - xoff - 341 ; // right = 640
2009-02-23 07:23:34 +00:00
rc_right *= MValueX ;
2009-02-28 19:02:37 +00:00
if ( rc_right > EFB_WIDTH * MValueX ) rc_right = EFB_WIDTH * MValueX ;
2009-02-23 07:23:34 +00:00
2009-06-22 09:31:30 +00:00
float rc_bottom = ( float ) bpmem . scissorBR . y - yoff - 341 ; // bottom = 480
2009-02-23 07:23:34 +00:00
rc_bottom *= MValueY ;
2009-02-28 19:02:37 +00:00
if ( rc_bottom > EFB_HEIGHT * MValueY ) rc_bottom = EFB_HEIGHT * MValueY ;
2009-02-23 07:23:34 +00:00
2009-02-28 01:26:56 +00:00
/*LOG(VIDEO, "Scissor: lt=(%d,%d), rb=(%d,%d,%i), off=(%d,%d)\n",
2009-02-23 07:23:34 +00:00
rc_left, rc_top,
rc_right, rc_bottom, Renderer::GetTargetHeight(),
xoff, yoff
);*/
// Check that the coordinates are good
if ( rc_right >= rc_left && rc_bottom >= rc_top )
{
glScissor (
( int ) rc_left , // x = 0 for example
Renderer :: GetTargetHeight () - ( int )( rc_bottom ), // y = 0 for example
2009-03-05 23:11:13 +00:00
( int )( rc_right - rc_left ), // width = 640 for example
( int )( rc_bottom - rc_top ) // height = 480 for example
2009-02-23 07:23:34 +00:00
);
return true ;
}
return false ;
}
2009-06-07 12:06:15 +00:00
// Aspect ratio functions
2009-02-28 16:33:59 +00:00
void ComputeBackbufferRectangle ( TRectangle * rc )
2009-02-23 07:23:34 +00:00
{
2009-02-28 16:33:59 +00:00
float FloatGLWidth = ( float ) OpenGL_GetBackbufferWidth ();
float FloatGLHeight = ( float ) OpenGL_GetBackbufferHeight ();
float FloatXOffset = 0 ;
float FloatYOffset = 0 ;
2009-02-23 07:23:34 +00:00
// The rendering window size
2009-02-28 16:33:59 +00:00
const float WinWidth = FloatGLWidth ;
const float WinHeight = FloatGLHeight ;
2009-02-23 07:23:34 +00:00
2009-02-28 16:33:59 +00:00
// Handle aspect ratio.
if ( g_Config . bKeepAR43 || g_Config . bKeepAR169 )
2009-02-23 07:23:34 +00:00
{
2009-02-28 16:33:59 +00:00
// The rendering window aspect ratio as a proportion of the 4:3 or 16:9 ratio
float Ratio = ( WinWidth / WinHeight ) / ( g_Config . bKeepAR43 ? ( 4.0f / 3.0f ) : ( 16.0f / 9.0f ));
2009-02-23 07:23:34 +00:00
// Check if height or width is the limiting factor. If ratio > 1 the picture is to wide and have to limit the width.
if ( Ratio > 1 )
{
2009-02-28 16:33:59 +00:00
// Scale down and center in the X direction.
FloatGLWidth /= Ratio ;
FloatXOffset = ( WinWidth - FloatGLWidth ) / 2.0f ;
2009-02-23 07:23:34 +00:00
}
2009-02-28 16:33:59 +00:00
// The window is too high, we have to limit the height
2009-02-23 07:23:34 +00:00
else
{
2009-02-28 16:33:59 +00:00
// Scale down and center in the Y direction.
FloatGLHeight *= Ratio ;
FloatYOffset = FloatYOffset + ( WinHeight - FloatGLHeight ) / 2.0f ;
2009-02-23 07:23:34 +00:00
}
}
2009-02-25 05:54:36 +00:00
// -----------------------------------------------------------------------
2009-03-05 23:11:13 +00:00
// Crop the picture from 4:3 to 5:4 or from 16:9 to 16:10.
2009-02-25 05:54:36 +00:00
// Output: FloatGLWidth, FloatGLHeight, FloatXOffset, FloatYOffset
// ------------------
2009-02-28 16:33:59 +00:00
if (( g_Config . bKeepAR43 || g_Config . bKeepAR169 ) && g_Config . bCrop )
2009-02-25 05:54:36 +00:00
{
float Ratio = g_Config . bKeepAR43 ? (( 4.0 / 3.0 ) / ( 5.0 / 4.0 )) : ((( 16.0 / 9.0 ) / ( 16.0 / 10.0 )));
2009-02-26 00:11:51 +00:00
// The width and height we will add (calculate this before FloatGLWidth and FloatGLHeight is adjusted)
2009-02-25 05:54:36 +00:00
float IncreasedWidth = ( Ratio - 1.0 ) * FloatGLWidth ;
float IncreasedHeight = ( Ratio - 1.0 ) * FloatGLHeight ;
2009-02-26 00:11:51 +00:00
// The new width and height
2009-02-25 05:54:36 +00:00
FloatGLWidth = FloatGLWidth * Ratio ;
FloatGLHeight = FloatGLHeight * Ratio ;
2009-06-08 03:17:15 +00:00
// The new width and height ratio
float WidthRatio = (( float ) FloatGLWidth ) / 640.0 ;
float HeightRatio = (( float ) FloatGLHeight ) / 480.0 ;
2009-02-25 05:54:36 +00:00
// Adjust the X and Y offset
2009-06-08 03:17:15 +00:00
FloatXOffset = FloatXOffset - ( IncreasedWidth / 2.0 );
FloatYOffset = FloatYOffset - ( IncreasedHeight / 2.0 );
//NOTICE_LOG(OSREPORT, "Crop Ratio:%1.2f IncreasedHeight:%3.0f YOffset:%3.0f", Ratio, IncreasedHeight, FloatYOffset);
//NOTICE_LOG(OSREPORT, "Crop FloatGLWidth:%1.2f FloatGLHeight:%3.0f", (float)FloatGLWidth, (float)FloatGLHeight);
//NOTICE_LOG(OSREPORT, "");
2009-02-25 05:54:36 +00:00
}
2009-03-05 23:11:13 +00:00
// round(float) = floor(float + 0.5)
2009-02-28 16:33:59 +00:00
int XOffset = floor ( FloatXOffset + 0.5 );
int YOffset = floor ( FloatYOffset + 0.5 );
rc -> left = XOffset ;
rc -> top = YOffset ;
rc -> right = XOffset + ceil ( FloatGLWidth );
rc -> bottom = YOffset + ceil ( FloatGLHeight );
}
2009-02-25 05:54:36 +00:00
2009-06-29 07:30:48 +00:00
// TODO: Use something less ugly than an Evil Global Variable.
// Also, protect this structure with a mutex.
extern volatile struct // Comes from main . cpp
{
u32 xfbAddr ;
u32 width ;
u32 height ;
s32 yOffset ;
} tUpdateXFBArgs ;
2009-06-26 08:57:53 +00:00
2009-06-29 07:30:48 +00:00
void Renderer :: RenderToXFB ( u32 xfbAddr , u32 dstWidth , u32 dstHeight , const TRectangle & sourceRc )
2009-06-26 08:57:53 +00:00
{
2009-06-29 07:30:48 +00:00
u32 aLower = xfbAddr ;
u32 aUpper = xfbAddr + 2 * dstWidth * dstHeight ;
u32 bLower = tUpdateXFBArgs . xfbAddr ;
u32 bUpper = tUpdateXFBArgs . xfbAddr + 2 * tUpdateXFBArgs . width * tUpdateXFBArgs . height ;
// If we're about to write into a requested XFB, make sure the previous
// contents make it to the screen first.
if ( g_XFBUpdateRequested && addrRangesOverlap ( aLower , aUpper , bLower , bUpper ))
2009-06-26 08:57:53 +00:00
{
Video_UpdateXFB ( NULL , 0 , 0 , 0 , FALSE );
}
2009-06-29 07:30:48 +00:00
s_framebufferManager . CopyToXFB ( xfbAddr , dstWidth , dstHeight , sourceRc );
2009-06-26 08:57:53 +00:00
}
2009-06-07 12:06:15 +00:00
2009-06-09 21:29:54 +00:00
2009-06-29 07:30:48 +00:00
// This function has the final picture. We adjust the aspect ratio here.
// yOffset is used to eliminate interlacing jitter in Real XFB mode.
void Renderer :: Swap ( u32 xfbAddr , u32 srcWidth , u32 srcHeight , s32 yOffset )
2009-02-28 16:33:59 +00:00
{
2009-06-29 07:30:48 +00:00
const XFBSource * xfbSource = s_framebufferManager . GetXFBSource ( xfbAddr , srcWidth , srcHeight );
if ( ! xfbSource )
{
WARN_LOG ( VIDEO , "Failed to get video for this frame" );
return ;
}
2009-02-28 16:33:59 +00:00
OpenGL_Update (); // just updates the render window position and the backbuffer size
DVSTARTPROFILE ();
2009-02-23 07:23:34 +00:00
ResetGLState ();
2009-02-28 16:33:59 +00:00
TRectangle back_rc ;
ComputeBackbufferRectangle ( & back_rc );
float u_max ;
2009-06-29 07:30:48 +00:00
float v_min ;
2009-02-28 16:33:59 +00:00
float v_max ;
2009-06-29 07:30:48 +00:00
if ( g_Config . bAutoScale )
2009-06-26 10:47:37 +00:00
{
2009-06-29 07:30:48 +00:00
u_max = ( xfbSource -> sourceRc . right - xfbSource -> sourceRc . left );
v_min = ( float ) xfbSource -> texHeight - ( xfbSource -> sourceRc . bottom - xfbSource -> sourceRc . top );
v_max = ( float ) xfbSource -> texHeight ;
2009-02-28 16:33:59 +00:00
}
2009-03-08 19:19:51 +00:00
else
{
2009-06-29 07:30:48 +00:00
u_max = ( float ) xfbSource -> texWidth ;
v_max = ( float ) xfbSource -> texHeight ;
2009-03-08 19:19:51 +00:00
}
2009-06-29 07:30:48 +00:00
v_min -= yOffset ;
v_max -= yOffset ;
2009-02-23 07:23:34 +00:00
2009-06-08 02:48:38 +00:00
// Tell the OSD Menu about the current internal resolution
2009-06-29 20:54:47 +00:00
OSDInternalW = xfbSource -> sourceRc . GetWidth (); OSDInternalH = xfbSource -> sourceRc . GetHeight ();
2009-06-08 02:48:38 +00:00
2009-06-08 18:34:24 +00:00
// Make sure that the wireframe setting doesn't screw up the screen copy.
2009-03-22 13:25:05 +00:00
glPolygonMode ( GL_FRONT_AND_BACK , GL_FILL );
2009-06-26 08:57:53 +00:00
// Textured triangles are necessary because of post-processing shaders
// Disable all other stages
for ( int i = 1 ; i < 8 ; ++ i )
TextureMngr :: DisableStage ( i );
// Update GLViewPort
glViewport ( back_rc . left , back_rc . top ,
back_rc . right - back_rc . left , back_rc . bottom - back_rc . top );
GL_REPORT_ERRORD ();
// Copy the framebuffer to screen.
// Render to the real buffer now.
glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT , 0 ); // switch to the window backbuffer
// Texture map s_xfbTexture onto the main buffer
glActiveTexture ( GL_TEXTURE0 );
glEnable ( GL_TEXTURE_RECTANGLE_ARB );
2009-06-29 07:30:48 +00:00
glBindTexture ( GL_TEXTURE_RECTANGLE_ARB , xfbSource -> texture );
2009-06-26 08:57:53 +00:00
// Use linear filtering.
glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
// We must call ApplyShader here even if no post proc is selected - it takes
// care of disabling it in that case. It returns false in case of no post processing.
if ( PostProcessing :: ApplyShader ())
2009-03-08 19:19:51 +00:00
{
2009-06-26 08:57:53 +00:00
glBegin ( GL_QUADS );
glTexCoord2f ( 0 , v_min ); glMultiTexCoord2fARB ( GL_TEXTURE1 , 0 , 0 ); glVertex2f ( - 1 , - 1 );
glTexCoord2f ( 0 , v_max ); glMultiTexCoord2fARB ( GL_TEXTURE1 , 0 , 1 ); glVertex2f ( - 1 , 1 );
glTexCoord2f ( u_max , v_max ); glMultiTexCoord2fARB ( GL_TEXTURE1 , 1 , 1 ); glVertex2f ( 1 , 1 );
glTexCoord2f ( u_max , v_min ); glMultiTexCoord2fARB ( GL_TEXTURE1 , 1 , 0 ); glVertex2f ( 1 , - 1 );
glEnd ();
glBindProgramARB ( GL_FRAGMENT_PROGRAM_ARB , 0 );
glDisable ( GL_FRAGMENT_PROGRAM_ARB );
2009-03-08 19:19:51 +00:00
}
2009-06-26 08:57:53 +00:00
else
2009-03-08 19:19:51 +00:00
{
2009-06-26 08:57:53 +00:00
glBegin ( GL_QUADS );
glTexCoord2f ( 0 , v_min ); glVertex2f ( - 1 , - 1 );
glTexCoord2f ( 0 , v_max ); glVertex2f ( - 1 , 1 );
glTexCoord2f ( u_max , v_max ); glVertex2f ( 1 , 1 );
glTexCoord2f ( u_max , v_min ); glVertex2f ( 1 , - 1 );
glEnd ();
2009-03-08 19:19:51 +00:00
}
2009-02-23 07:23:34 +00:00
2009-06-26 08:57:53 +00:00
glBindTexture ( GL_TEXTURE_RECTANGLE_ARB , 0 );
TextureMngr :: DisableStage ( 0 );
2009-03-22 13:25:05 +00:00
// Wireframe
if ( g_Config . bWireFrame )
glPolygonMode ( GL_FRONT_AND_BACK , GL_LINE );
2009-06-08 00:44:48 +00:00
// Save screenshot
if ( s_bScreenshot )
2009-06-07 11:51:53 +00:00
{
2009-06-29 20:54:47 +00:00
if ( ! s_tempScreenshotFramebuffer )
glGenFramebuffersEXT ( 1 , & s_tempScreenshotFramebuffer );
glBindFramebufferEXT ( GL_READ_FRAMEBUFFER_EXT , s_tempScreenshotFramebuffer );
glFramebufferTexture2DEXT ( GL_READ_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT0_EXT , GL_TEXTURE_RECTANGLE_ARB , xfbSource -> texture , 0 );
2009-06-07 11:51:53 +00:00
2009-06-08 00:44:48 +00:00
s_criticalScreenshot . Enter ();
2009-06-07 11:51:53 +00:00
// Save screenshot
2009-06-29 20:54:47 +00:00
SaveRenderTarget ( s_sScreenshotName . c_str (), xfbSource -> sourceRc . GetWidth (), xfbSource -> sourceRc . GetHeight (), ( int )( v_min ));
2009-06-07 11:51:53 +00:00
// Reset settings
s_sScreenshotName = "" ;
s_bScreenshot = false ;
s_criticalScreenshot . Leave ();
2009-06-29 20:54:47 +00:00
glFramebufferTexture2DEXT ( GL_READ_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT0_EXT , GL_TEXTURE_RECTANGLE_ARB , 0 , 0 );
glBindFramebufferEXT ( GL_READ_FRAMEBUFFER_EXT , s_framebufferManager . GetEFBFramebuffer ());
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
#ifdef _WIN32
2009-06-08 10:16:08 +00:00
if ( g_Config . bDumpFrames )
{
2009-06-29 20:54:47 +00:00
if ( ! s_tempScreenshotFramebuffer )
glGenFramebuffersEXT ( 1 , & s_tempScreenshotFramebuffer );
glBindFramebufferEXT ( GL_READ_FRAMEBUFFER_EXT , s_tempScreenshotFramebuffer );
glFramebufferTexture2DEXT ( GL_READ_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT0_EXT , GL_TEXTURE_RECTANGLE_ARB , xfbSource -> texture , 0 );
2009-06-08 10:16:08 +00:00
2009-03-28 21:07:16 +00:00
s_criticalScreenshot . Enter ();
2009-06-29 20:54:47 +00:00
int w = xfbSource -> sourceRc . GetWidth ();
int h = xfbSource -> sourceRc . GetHeight ();
2009-06-08 10:16:08 +00:00
int t = ( int )( v_min );
2009-03-28 21:07:16 +00:00
u8 * data = ( u8 * ) malloc ( 3 * w * h );
glPixelStorei ( GL_PACK_ALIGNMENT , 1 );
2009-06-08 10:16:08 +00:00
glReadPixels ( 0 , t , w , h , GL_BGR , GL_UNSIGNED_BYTE , data );
2009-04-03 14:35:49 +00:00
if ( glGetError () == GL_NO_ERROR )
{
if ( ! s_bLastFrameDumped )
{
2009-03-28 21:07:16 +00:00
s_bAVIDumping = AVIDump :: Start ( EmuWindow :: GetChildParentWnd (), w , h );
2009-04-03 14:35:49 +00:00
if ( ! s_bAVIDumping )
2009-06-08 10:16:08 +00:00
OSD :: AddMessage ( "AVIDump Start failed" , 2000 );
2009-04-03 14:35:49 +00:00
else
{
2009-06-08 10:16:08 +00:00
OSD :: AddMessage ( StringFromFormat (
"Dumping Frames to \" %s/framedump0.avi \" (%dx%d RGB24)" , FULL_FRAMES_DIR , w , h ). c_str (), 2000 );
2009-03-28 21:07:16 +00:00
}
}
2009-04-03 14:35:49 +00:00
if ( s_bAVIDumping )
2009-03-28 21:07:16 +00:00
AVIDump :: AddFrame (( char * ) data );
2009-04-03 14:35:49 +00:00
2009-03-28 21:07:16 +00:00
s_bLastFrameDumped = true ;
}
2009-06-08 10:16:08 +00:00
else
{
NOTICE_LOG ( VIDEO , "Error reading framebuffer" );
}
2009-03-28 21:07:16 +00:00
free ( data );
s_criticalScreenshot . Leave ();
2009-06-29 20:54:47 +00:00
glFramebufferTexture2DEXT ( GL_READ_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT0_EXT , GL_TEXTURE_RECTANGLE_ARB , 0 , 0 );
glBindFramebufferEXT ( GL_READ_FRAMEBUFFER_EXT , s_framebufferManager . GetEFBFramebuffer ());
2009-04-03 14:35:49 +00:00
}
else
{
if ( s_bLastFrameDumped && s_bAVIDumping )
{
2009-03-28 21:07:16 +00:00
AVIDump :: Stop ();
s_bAVIDumping = false ;
2009-06-08 10:16:08 +00:00
OSD :: AddMessage ( "Stop dumping frames to AVI" , 2000 );
2009-03-28 21:07:16 +00:00
}
s_bLastFrameDumped = false ;
}
#else
if ( g_Config . bDumpFrames ) {
s_criticalScreenshot . Enter ();
char movie_file_name [ 255 ];
int w = OpenGL_GetBackbufferWidth ();
int h = OpenGL_GetBackbufferHeight ();
u8 * data = ( u8 * ) malloc ( 3 * w * h );
glPixelStorei ( GL_PACK_ALIGNMENT , 1 );
glReadPixels ( 0 , 0 , w , h , GL_RGB , GL_UNSIGNED_BYTE , data );
if ( glGetError () == GL_NO_ERROR ) {
if ( ! s_bLastFrameDumped ) {
sprintf ( movie_file_name , "%s/framedump.raw" , FULL_FRAMES_DIR );
f_pFrameDump = fopen ( movie_file_name , "wb" );
if ( f_pFrameDump == NULL ) {
PanicAlert ( "Error opening framedump.raw for writing." );
} else {
char msg [ 255 ];
sprintf ( msg , "Dumping Frames to \" %s \" (%dx%d RGB24)" , movie_file_name , w , h );
OSD :: AddMessage ( msg , 2000 );
}
}
if ( f_pFrameDump != NULL ) {
FlipImageData ( data , w , h );
fwrite ( data , w * 3 , h , f_pFrameDump );
fflush ( f_pFrameDump );
}
s_bLastFrameDumped = true ;
}
free ( data );
s_criticalScreenshot . Leave ();
} else {
if ( s_bLastFrameDumped && f_pFrameDump != NULL ) {
fclose ( f_pFrameDump );
f_pFrameDump = NULL ;
}
s_bLastFrameDumped = false ;
}
#endif
2009-06-07 11:51:53 +00:00
// ---------------------------------------------------------------------
2009-03-28 21:07:16 +00:00
2009-02-23 07:23:34 +00:00
// Place messages on the picture, then copy it to the screen
SwapBuffers ();
2009-06-08 20:07:20 +00:00
// Why save this as s_bNativeResolution if we updated it every frame?
2009-03-08 21:42:27 +00:00
s_bNativeResolution = g_Config . bNativeResolution ;
2009-02-23 07:23:34 +00:00
RestoreGLState ();
2009-02-28 16:33:59 +00:00
GL_REPORT_ERRORD ();
2009-02-23 07:23:34 +00:00
g_Config . iSaveTargetId = 0 ;
2009-02-28 16:33:59 +00:00
// For testing zbuffer targets.
// Renderer::SetZBufferRender();
2009-03-05 23:11:13 +00:00
// SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_ARB, s_FakeZTarget, GetTargetWidth(), GetTargetHeight());
2009-02-23 07:23:34 +00:00
}
2009-06-07 12:06:15 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
2009-02-23 07:23:34 +00:00
2009-06-07 12:06:15 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
// We can now draw whatever we want on top of the picture. Then we copy the final picture to the output.
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
void Renderer :: SwapBuffers ()
{
// ---------------------------------------------------------------------
// Count FPS.
// ¯¯¯¯¯¯¯¯¯¯¯¯¯
static int fpscount = 0 ;
static unsigned long lasttime ;
++ fpscount ;
if ( timeGetTime () - lasttime > 1000 )
{
lasttime = timeGetTime ();
s_fps = fpscount - 1 ;
fpscount = 0 ;
}
// ---------------------------------------------------------------------
2009-06-13 22:08:01 +00:00
GL_REPORT_ERRORD ();
2009-06-07 12:06:15 +00:00
for ( int i = 0 ; i < 8 ; i ++ ) {
glActiveTexture ( GL_TEXTURE0 + i );
glDisable ( GL_TEXTURE_2D );
glDisable ( GL_TEXTURE_RECTANGLE_ARB );
}
glActiveTexture ( GL_TEXTURE0 );
DrawDebugText ();
2009-06-13 22:08:01 +00:00
GL_REPORT_ERRORD ();
2009-06-07 12:06:15 +00:00
OSD :: DrawMessages ();
2009-06-13 22:08:01 +00:00
GL_REPORT_ERRORD ();
2009-06-07 12:06:15 +00:00
#if defined(DVPROFILE)
if ( g_bWriteProfile ) {
//g_bWriteProfile = 0;
static int framenum = 0 ;
const int UPDATE_FRAMES = 8 ;
if ( ++ framenum >= UPDATE_FRAMES ) {
DVProfWrite ( "prof.txt" , UPDATE_FRAMES );
DVProfClear ();
framenum = 0 ;
}
}
#endif
// Copy the rendered frame to the real window
OpenGL_SwapBuffers ();
2009-06-13 22:08:01 +00:00
GL_REPORT_ERRORD ();
2009-06-08 20:07:20 +00:00
// Clear framebuffer
2009-06-07 12:06:15 +00:00
glClearColor ( 0 , 0 , 0 , 0 );
glClear ( GL_COLOR_BUFFER_BIT );
GL_REPORT_ERRORD ();
// Clean out old stuff from caches
VertexShaderCache :: ProgressiveCleanup ();
PixelShaderCache :: ProgressiveCleanup ();
TextureMngr :: ProgressiveCleanup ();
frameCount ++ ;
// New frame
stats . ResetFrame ();
// Render to the framebuffer.
2009-06-26 08:57:53 +00:00
SetFramebuffer ( 0 );
2009-06-07 12:06:15 +00:00
GL_REPORT_ERRORD ();
}
// Create On-Screen-Messages
2009-02-28 16:33:59 +00:00
void Renderer :: DrawDebugText ()
2009-02-23 07:23:34 +00:00
{
2009-03-08 19:19:51 +00:00
// Reset viewport for drawing text
glViewport ( 0 , 0 , OpenGL_GetBackbufferWidth (), OpenGL_GetBackbufferHeight ());
2009-02-28 16:33:59 +00:00
// Draw various messages on the screen, like FPS, statistics, etc.
2009-02-23 07:23:34 +00:00
char debugtext_buffer [ 8192 ];
char * p = debugtext_buffer ;
p [ 0 ] = 0 ;
2009-04-03 14:35:49 +00:00
2009-02-23 07:23:34 +00:00
if ( g_Config . bShowFPS )
p += sprintf ( p , "FPS: %d \n " , s_fps );
2009-03-16 07:54:44 +00:00
if ( g_Config . bShowEFBCopyRegions )
{
2009-03-17 06:04:57 +00:00
// Store Line Size
2009-03-21 16:04:44 +00:00
GLfloat lSize ;
glGetFloatv ( GL_LINE_WIDTH , & lSize );
2009-03-17 06:04:57 +00:00
// Set Line Size
2009-04-03 21:32:10 +00:00
glLineWidth ( 3.0f );
2009-03-16 07:54:44 +00:00
glBegin ( GL_LINES );
2009-03-17 06:04:57 +00:00
// Draw EFB copy regions rectangles
2009-03-16 07:54:44 +00:00
for ( std :: vector < TRectangle >:: const_iterator it = stats . efb_regions . begin (); it != stats . efb_regions . end (); ++ it )
{
2009-03-21 16:04:44 +00:00
GLfloat halfWidth = Renderer :: GetTargetWidth () / 2.0f ;
GLfloat halfHeight = Renderer :: GetTargetHeight () / 2.0f ;
GLfloat x = ( GLfloat ) - 1.0f + (( GLfloat ) it -> left / halfWidth );
GLfloat y = ( GLfloat ) 1.0f - (( GLfloat ) it -> top / halfHeight );
GLfloat x2 = ( GLfloat ) - 1.0f + (( GLfloat ) it -> right / halfWidth );
GLfloat y2 = ( GLfloat ) 1.0f - (( GLfloat ) it -> bottom / halfHeight );
2009-03-16 07:54:44 +00:00
2009-03-17 06:04:57 +00:00
// Draw shadow of rect
2009-03-16 07:54:44 +00:00
glColor3f ( 0.0f , 0.0f , 0.0f );
2009-04-03 21:32:10 +00:00
glVertex2f ( x , y - 0.01 ); glVertex2f ( x2 , y - 0.01 );
glVertex2f ( x , y2 - 0.01 ); glVertex2f ( x2 , y2 - 0.01 );
glVertex2f ( x + 0.005 , y ); glVertex2f ( x + 0.005 , y2 );
glVertex2f ( x2 + 0.005 , y ); glVertex2f ( x2 + 0.005 , y2 );
2009-03-16 07:54:44 +00:00
2009-03-17 06:04:57 +00:00
// Draw rect
2009-03-16 07:54:44 +00:00
glColor3f ( 0.0f , 1.0f , 1.0f );
2009-04-03 21:32:10 +00:00
glVertex2f ( x , y ); glVertex2f ( x2 , y );
glVertex2f ( x , y2 ); glVertex2f ( x2 , y2 );
glVertex2f ( x , y ); glVertex2f ( x , y2 );
glVertex2f ( x2 , y ); glVertex2f ( x2 , y2 );
2009-03-16 07:54:44 +00:00
}
glEnd ();
2009-03-17 06:04:57 +00:00
// Restore Line Size
2009-03-21 16:04:44 +00:00
glLineWidth ( lSize );
2009-03-17 06:04:57 +00:00
// Clear stored regions
2009-03-16 07:54:44 +00:00
stats . efb_regions . clear ();
}
if ( g_Config . bOverlayStats )
{
2009-02-23 07:23:34 +00:00
p += sprintf ( p , "textures created: %i \n " , stats . numTexturesCreated );
p += sprintf ( p , "textures alive: %i \n " , stats . numTexturesAlive );
p += sprintf ( p , "pshaders created: %i \n " , stats . numPixelShadersCreated );
p += sprintf ( p , "pshaders alive: %i \n " , stats . numPixelShadersAlive );
p += sprintf ( p , "vshaders created: %i \n " , stats . numVertexShadersCreated );
p += sprintf ( p , "vshaders alive: %i \n " , stats . numVertexShadersAlive );
p += sprintf ( p , "dlists called: %i \n " , stats . numDListsCalled );
p += sprintf ( p , "dlists called(f): %i \n " , stats . thisFrame . numDListsCalled );
// not used.
//p+=sprintf(p,"dlists created: %i\n",stats.numDListsCreated);
//p+=sprintf(p,"dlists alive: %i\n",stats.numDListsAlive);
//p+=sprintf(p,"strip joins: %i\n",stats.numJoins);
p += sprintf ( p , "primitives: %i \n " , stats . thisFrame . numPrims );
p += sprintf ( p , "primitive joins: %i \n " , stats . thisFrame . numPrimitiveJoins );
p += sprintf ( p , "buffer splits: %i \n " , stats . thisFrame . numBufferSplits );
p += sprintf ( p , "draw calls: %i \n " , stats . thisFrame . numDrawCalls );
p += sprintf ( p , "primitives (DL): %i \n " , stats . thisFrame . numDLPrims );
p += sprintf ( p , "XF loads: %i \n " , stats . thisFrame . numXFLoads );
p += sprintf ( p , "XF loads (DL): %i \n " , stats . thisFrame . numXFLoadsInDL );
p += sprintf ( p , "CP loads: %i \n " , stats . thisFrame . numCPLoads );
p += sprintf ( p , "CP loads (DL): %i \n " , stats . thisFrame . numCPLoadsInDL );
p += sprintf ( p , "BP loads: %i \n " , stats . thisFrame . numBPLoads );
p += sprintf ( p , "BP loads (DL): %i \n " , stats . thisFrame . numBPLoadsInDL );
p += sprintf ( p , "vertex loaders: %i \n " , stats . numVertexLoaders );
std :: string text1 ;
VertexLoaderManager :: AppendListToString ( & text1 );
2009-02-28 16:33:59 +00:00
// TODO: Check for buffer overflow
2009-02-23 07:23:34 +00:00
p += sprintf ( p , "%s" , text1 . c_str ());
}
if ( g_Config . bOverlayProjStats )
{
p += sprintf ( p , "Projection #: X for Raw 6=0 (X for Raw 6!=0) \n\n " );
p += sprintf ( p , "Projection 0: %f (%f) Raw 0: %f \n " , stats . gproj_0 , stats . g2proj_0 , stats . proj_0 );
p += sprintf ( p , "Projection 1: %f (%f) \n " , stats . gproj_1 , stats . g2proj_1 );
p += sprintf ( p , "Projection 2: %f (%f) Raw 1: %f \n " , stats . gproj_2 , stats . g2proj_2 , stats . proj_1 );
p += sprintf ( p , "Projection 3: %f (%f) \n\n " , stats . gproj_3 , stats . g2proj_3 );
p += sprintf ( p , "Projection 4: %f (%f) \n " , stats . gproj_4 , stats . g2proj_4 );
p += sprintf ( p , "Projection 5: %f (%f) Raw 2: %f \n " , stats . gproj_5 , stats . g2proj_5 , stats . proj_2 );
p += sprintf ( p , "Projection 6: %f (%f) Raw 3: %f \n " , stats . gproj_6 , stats . g2proj_6 , stats . proj_3 );
p += sprintf ( p , "Projection 7: %f (%f) \n\n " , stats . gproj_7 , stats . g2proj_7 );
p += sprintf ( p , "Projection 8: %f (%f) \n " , stats . gproj_8 , stats . g2proj_8 );
p += sprintf ( p , "Projection 9: %f (%f) \n " , stats . gproj_9 , stats . g2proj_9 );
p += sprintf ( p , "Projection 10: %f (%f) Raw 4: %f \n\n " , stats . gproj_10 , stats . g2proj_10 , stats . proj_4 );
p += sprintf ( p , "Projection 11: %f (%f) Raw 5: %f \n\n " , stats . gproj_11 , stats . g2proj_11 , stats . proj_5 );
p += sprintf ( p , "Projection 12: %f (%f) \n " , stats . gproj_12 , stats . g2proj_12 );
p += sprintf ( p , "Projection 13: %f (%f) \n " , stats . gproj_13 , stats . g2proj_13 );
p += sprintf ( p , "Projection 14: %f (%f) \n " , stats . gproj_14 , stats . g2proj_14 );
p += sprintf ( p , "Projection 15: %f (%f) \n " , stats . gproj_15 , stats . g2proj_15 );
}
// Render a shadow, and then the text.
Renderer :: RenderText ( debugtext_buffer , 21 , 21 , 0xDD000000 );
Renderer :: RenderText ( debugtext_buffer , 20 , 20 , 0xFF00FFFF );
2009-06-08 02:48:38 +00:00
// OSD Menu messages
if ( OSDChoice > 0 && g_Config . bEFBCopyDisableHotKey )
{
OSDTime = timeGetTime () + 3000 ;
OSDChoice = - OSDChoice ;
}
2009-06-10 01:15:41 +00:00
if (( u32 ) OSDTime > timeGetTime () && g_Config . bEFBCopyDisableHotKey )
2009-06-08 02:48:38 +00:00
{
std :: string T1 = "" ;
std :: string T2 = "" ;
int W , H ;
sscanf ( g_Config . iInternalRes , "%dx%d" , & W , & H );
std :: string OSDM1 =
2009-06-08 06:05:09 +00:00
g_Config . bNativeResolution || g_Config . b2xResolution ?
( g_Config . bNativeResolution ?
StringFromFormat ( "%i x %i (native)" , OSDInternalW , OSDInternalH )
: StringFromFormat ( "%i x %i (2x)" , OSDInternalW , OSDInternalH ))
: StringFromFormat ( "%i x %i (custom)" , W , H );
2009-06-08 02:48:38 +00:00
std :: string OSDM21 =
! ( g_Config . bKeepAR43 || g_Config . bKeepAR169 ) ? "-" : ( g_Config . bKeepAR43 ? "4:3" : "16:9" );
std :: string OSDM22 =
g_Config . bCrop ? " (crop)" : "" ;
std :: string OSDM31 =
g_Config . bCopyEFBToRAM ? "RAM" : "Texture" ;
std :: string OSDM32 =
2009-06-08 20:07:20 +00:00
g_Config . bEFBCopyDisable ? "No" : "Yes" ;
2009-06-08 02:48:38 +00:00
// If there is more text than this we will have a collission
if ( g_Config . bShowFPS )
{ T1 += " \n\n " ; T2 += " \n\n " ; }
// The latest changed setting in yellow
T1 += ( OSDChoice == - 1 ) ? StringFromFormat ( "3: Internal Resolution: %s \n " , OSDM1 . c_str ()) : " \n " ;
T1 += ( OSDChoice == - 2 ) ? StringFromFormat ( "4: Lock Aspect Ratio: %s%s \n " , OSDM21 . c_str (), OSDM22 . c_str ()) : " \n " ;
T1 += ( OSDChoice == - 3 ) ? StringFromFormat ( "5: Copy Embedded Framebuffer to %s: %s \n " , OSDM31 . c_str (), OSDM32 . c_str ()) : " \n " ;
// The other settings in cyan
T2 += ! ( OSDChoice == - 1 ) ? StringFromFormat ( "3: Internal Resolution: %s \n " , OSDM1 . c_str ()) : " \n " ;
T2 += ! ( OSDChoice == - 2 ) ? StringFromFormat ( "4: Lock Aspect Ratio: %s \n " , OSDM21 . c_str (), OSDM22 . c_str ()) : " \n " ;
T2 += ! ( OSDChoice == - 3 ) ? StringFromFormat ( "5: Copy Embedded Framebuffer to %s: %s \n " , OSDM31 . c_str (), OSDM32 . c_str ()) : " \n " ;
// Render a shadow, and then the text
Renderer :: RenderText ( T1 . c_str (), 21 , 21 , 0xDD000000 );
Renderer :: RenderText ( T1 . c_str (), 20 , 20 , 0xFFffff00 );
Renderer :: RenderText ( T2 . c_str (), 21 , 21 , 0xDD000000 );
Renderer :: RenderText ( T2 . c_str (), 20 , 20 , 0xFF00FFFF );
}
2009-02-28 16:33:59 +00:00
}
2009-02-23 07:23:34 +00:00
void Renderer :: RenderText ( const char * pstr , int left , int top , u32 color )
{
2009-02-28 16:33:59 +00:00
int nBackbufferWidth = ( int ) OpenGL_GetBackbufferWidth ();
int nBackbufferHeight = ( int ) OpenGL_GetBackbufferHeight ();
2009-02-23 07:23:34 +00:00
glColor4f ((( color >> 16 ) & 0xff ) / 255.0f , (( color >> 8 ) & 0xff ) / 255.0f ,
(( color >> 0 ) & 0xff ) / 255.0f , (( color >> 24 ) & 0xFF ) / 255.0f );
s_pfont -> printMultilineText ( pstr ,
left * 2.0f / ( float ) nBackbufferWidth - 1 ,
1 - top * 2.0f / ( float ) nBackbufferHeight ,
0 , nBackbufferWidth , nBackbufferHeight );
2009-06-13 22:08:01 +00:00
GL_REPORT_ERRORD ();
2009-02-23 07:23:34 +00:00
}
2009-06-07 11:51:53 +00:00
// Save screenshot
2009-02-27 03:56:34 +00:00
void Renderer :: SetScreenshot ( const char * filename )
2009-02-23 07:23:34 +00:00
{
2009-02-27 03:56:34 +00:00
s_criticalScreenshot . Enter ();
s_sScreenshotName = filename ;
s_bScreenshot = true ;
s_criticalScreenshot . Leave ();
}
2009-02-23 07:23:34 +00:00
2009-06-06 13:36:33 +00:00
bool Renderer :: SaveRenderTarget ( const char * filename , int W , int H , int YOffset )
2009-02-27 03:56:34 +00:00
{
2009-06-08 00:44:48 +00:00
// The height seemed to often be one less than the setting (but sometimes not),
2009-06-22 09:31:30 +00:00
// perhaps the source is the (bpmem.copyTexSrcWH.y + 1) in BPStructs.cpp that I'm guessing
2009-06-08 00:44:48 +00:00
// is there because of how some GL function works. But the buffer we are reading from here
// seems to have the necessary pixels for a complete height so we use the complete height
// from the settings.
2009-06-08 06:05:09 +00:00
if ( ! ( g_Config . bNativeResolution || g_Config . b2xResolution ))
2009-06-08 00:44:48 +00:00
sscanf ( g_Config . iInternalRes , "%dx%d" , & W , & H );
2009-06-06 13:36:33 +00:00
2009-06-08 20:07:20 +00:00
2009-06-06 13:36:33 +00:00
u8 * data = ( u8 * ) malloc ( 3 * W * H );
2009-02-27 03:56:34 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 1 );
2009-06-06 13:36:33 +00:00
glReadPixels ( 0 , YOffset , W , H , GL_RGB , GL_UNSIGNED_BYTE , data );
2009-06-07 21:10:02 +00:00
2009-06-06 17:34:54 +00:00
// Show failure message
2009-02-27 03:56:34 +00:00
if ( glGetError () != GL_NO_ERROR )
2009-06-06 17:34:54 +00:00
{
2009-06-06 13:36:33 +00:00
OSD :: AddMessage ( "Error capturing or saving screenshot." , 2000 );
2009-06-06 17:34:54 +00:00
return false ;
}
2009-06-07 21:10:02 +00:00
2009-06-06 13:36:33 +00:00
// Turn image upside down
FlipImageData ( data , W , H );
2009-06-07 21:10:02 +00:00
2009-03-28 21:07:16 +00:00
#if defined(HAVE_WX) && HAVE_WX
2009-06-06 13:36:33 +00:00
// Create wxImage
wxImage a ( W , H , data );
2009-06-08 00:44:48 +00:00
// ---------------------------------------------------------------------
// To get past the problem of non-4:3 and non-16:9 native resolution pictures (for example
// in RE1 some pictures have non-4:3 resolutions like 640 x 448 and 512 x 448 and such that
// are meant to be rescaled to 4:3, and most Wii games use 640 x 480 even for the 16:9 mode)
// we let the user use the keep aspect ratio functions to control the resulting aspect ratio.
// ¯¯¯¯¯¯¯¯¯¯¯¯¯
// We don't adjust non-native resolutions to avoid blurring the picture.
// ¯¯¯¯¯¯¯¯¯¯¯¯¯
2009-06-08 20:07:20 +00:00
float Ratio = ( float ) W / ( float )( H ), TargetRatio ;
if (( g_Config . bNativeResolution || g_Config . b2xResolution ) && ( g_Config . bKeepAR169 || g_Config . bKeepAR43 )
2009-06-08 00:44:48 +00:00
&& Ratio != 4.0 / 3.0 && Ratio != 16.0 / 9.0 )
{
if ( g_Config . bKeepAR43 )
TargetRatio = 4.0 / 3.0 ;
else
TargetRatio = 16.0 / 9.0 ;
// Check if the height or width should be changed (we only increase the picture size, not
// the other way around)
if ( Ratio < TargetRatio )
2009-06-06 13:36:33 +00:00
{
2009-06-08 00:44:48 +00:00
float fW = ( float ) H * TargetRatio ;
W = ( int ) fW ;
2009-06-06 13:36:33 +00:00
}
2009-06-08 00:44:48 +00:00
else
2009-06-06 13:36:33 +00:00
{
2009-06-08 00:44:48 +00:00
float fH = ( float ) W * ( 1 / TargetRatio );
H = ( int ) fH ;
2009-06-06 13:36:33 +00:00
}
a . Rescale ( W , H , wxIMAGE_QUALITY_HIGH );
2009-06-07 21:10:02 +00:00
}
2009-06-08 00:44:48 +00:00
// ---------------------------------------------------------------------
2009-06-06 13:36:33 +00:00
2009-03-28 21:07:16 +00:00
a . SaveFile ( wxString :: FromAscii ( filename ), wxBITMAP_TYPE_BMP );
bool result = true ;
2009-06-06 13:36:33 +00:00
// Show success messages
OSD :: AddMessage ( StringFromFormat ( "Saved %i x %i %s \n " , W , H , s_sScreenshotName . c_str ()). c_str (), 2000 );
2009-03-28 21:07:16 +00:00
#else
bool result = SaveTGA ( filename , w , h , data );
free ( data );
#endif
return result ;
}
2009-06-07 11:51:53 +00:00
2009-03-28 21:07:16 +00:00
void Renderer :: FlipImageData ( u8 * data , int w , int h )
{
2009-02-28 16:33:59 +00:00
// Flip image upside down. Damn OpenGL.
for ( int y = 0 ; y < h / 2 ; y ++ )
{
for ( int x = 0 ; x < w ; x ++ )
{
std :: swap ( data [( y * w + x ) * 3 ], data [(( h - 1 - y ) * w + x ) * 3 ]);
std :: swap ( data [( y * w + x ) * 3 + 1 ], data [(( h - 1 - y ) * w + x ) * 3 + 1 ]);
std :: swap ( data [( y * w + x ) * 3 + 2 ], data [(( h - 1 - y ) * w + x ) * 3 + 2 ]);
2009-02-27 03:56:34 +00:00
}
}
2009-02-23 07:23:34 +00:00
}
2009-06-07 11:51:53 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
2009-02-23 07:23:34 +00:00
2009-06-07 12:06:15 +00:00
//////////////////////////////////////////////////////////////////////////////////////////
2009-02-23 07:23:34 +00:00
// Function: This function does not have the final picture. Use Renderer::Swap() to adjust the final picture.
// Call schedule: Called from VertexShaderManager
2009-06-07 12:06:15 +00:00
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
2009-02-23 07:23:34 +00:00
void UpdateViewport ()
{
2009-04-03 14:35:49 +00:00
// ---------
2009-02-23 07:23:34 +00:00
// Logging
2009-04-03 14:35:49 +00:00
// ---------
2009-02-23 07:23:34 +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
2009-03-21 20:07:56 +00:00
/*INFO_LOG(VIDEO, "view: topleft=(%f,%f), wh=(%f,%f), z=(%f,%f)",
2009-02-23 07:23:34 +00:00
rawViewport[3]-rawViewport[0]-342, rawViewport[4]+rawViewport[1]-342,
2 * rawViewport[0], 2 * rawViewport[1],
(rawViewport[5] - rawViewport[2]) / 16777215.0f, rawViewport[5] / 16777215.0f);*/
2009-04-03 14:35:49 +00:00
// --------
2009-02-23 07:23:34 +00:00
2009-06-22 09:31:30 +00:00
int scissorXOff = bpmem . scissorOffset . x * 2 - 342 ;
int scissorYOff = bpmem . scissorOffset . y * 2 - 342 ;
2009-02-23 07:23:34 +00:00
// -------------------------------------
2009-02-28 16:33:59 +00:00
float MValueX = Renderer :: GetTargetScaleX ();
float MValueY = Renderer :: GetTargetScaleY ();
2009-04-03 14:35:49 +00:00
2009-02-23 07:23:34 +00:00
// Stretch picture with increased internal resolution
2009-02-28 16:33:59 +00:00
int GLx = ( int ) ceil (( xfregs . rawViewport [ 3 ] - xfregs . rawViewport [ 0 ] - 342 - scissorXOff ) * MValueX );
int GLy = ( int ) ceil ( Renderer :: GetTargetHeight () - (( int )( xfregs . rawViewport [ 4 ] - xfregs . rawViewport [ 1 ] - 342 - scissorYOff )) * MValueY );
int GLWidth = ( int ) ceil ( abs (( int )( 2 * xfregs . rawViewport [ 0 ])) * MValueX );
int GLHeight = ( int ) ceil ( abs (( int )( 2 * xfregs . rawViewport [ 1 ])) * MValueY );
2009-02-23 07:23:34 +00:00
// Update the view port
2009-02-28 16:33:59 +00:00
glViewport ( GLx , GLy , GLWidth , GLHeight );
2009-02-23 07:23:34 +00:00
2009-02-28 16:33:59 +00:00
// GLDepthRange - this could be a source of trouble - see the viewport hacks.
2009-02-23 07:23:34 +00:00
double GLNear = ( xfregs . rawViewport [ 5 ] - xfregs . rawViewport [ 2 ]) / 16777215.0f ;
double GLFar = xfregs . rawViewport [ 5 ] / 16777215.0f ;
glDepthRange ( GLNear , GLFar );
2009-02-28 16:33:59 +00:00
2009-02-23 07:23:34 +00:00
// -------------------------------------
// Logging
/*
RECT RcTop, RcParent, RcChild;
HWND Child = EmuWindow::GetWnd();
HWND Parent = GetParent(Child);
HWND Top = GetParent(Parent);
GetWindowRect(Top, &RcTop);
GetWindowRect(Parent, &RcParent);
GetWindowRect(Child, &RcChild);
//Console::ClearScreen();
2009-03-21 20:07:56 +00:00
DEBUG_LOG(CONSOLE, "----------------------------------------------------------------");
DEBUG_LOG(CONSOLE, "Top window: X:%03i Y:%03i Width:%03i Height:%03i", RcTop.left, RcTop.top, RcTop.right - RcTop.left, RcTop.bottom - RcTop.top);
DEBUG_LOG(CONSOLE, "Parent window: X:%03i Y:%03i Width:%03i Height:%03i", RcParent.left, RcParent.top, RcParent.right - RcParent.left, RcParent.bottom - RcParent.top);
DEBUG_LOG(CONSOLE, "Child window: X:%03i Y:%03i Width:%03i Height:%03i", RcChild.left, RcChild.top, RcChild.right - RcChild.left, RcChild.bottom - RcChild.top);
DEBUG_LOG(CONSOLE, "----------------------------------------------------------------");
DEBUG_LOG(CONSOLE, "Res. MValue: X:%f Y:%f XOffs:%f YOffs:%f", OpenGL_GetXmax(), OpenGL_GetYmax(), OpenGL_GetXoff(), OpenGL_GetYoff());
DEBUG_LOG(CONSOLE, "GLViewPort: X:%03i Y:%03i Width:%03i Height:%03i", GLx, GLy, GLWidth, GLHeight);
DEBUG_LOG(CONSOLE, "GLDepthRange: Near:%f Far:%f", GLNear, GLFar);
DEBUG_LOG(CONSOLE, "GLScissor: X:%03i Y:%03i Width:%03i Height:%03i", GLScissorX, GLScissorY, GLScissorW, GLScissorH);
DEBUG_LOG(CONSOLE, "----------------------------------------------------------------");
2009-02-23 07:23:34 +00:00
*/
}
2009-06-07 21:10:02 +00:00