2009-07-28 21:32:10 +00:00
// Copyright (C) 2003 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-07-12 21:58:32 +00:00
#include "Thread.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"
2009-08-09 11:03:58 +00:00
#include "DLCache.h"
2009-02-23 07:23:34 +00:00
#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-07-11 02:34:16 +00:00
#include "Fifo.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-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-07-31 01:55:26 +00:00
static Common :: Thread * scrshotThread = 0 ;
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-08-08 01:39:56 +00:00
static bool s_skipSwap = false ;
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
2009-07-31 01:55:26 +00:00
#if defined(HAVE_WX) && HAVE_WX
2009-07-29 03:11:35 +00:00
// Screenshot thread struct
typedef struct
{
int W , H ;
std :: string filename ;
wxImage * img ;
} ScrStrct ;
2009-07-31 01:55:26 +00:00
#endif
2009-07-29 03:11:35 +00:00
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-07-31 01:55:26 +00:00
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-07-15 00:51:24 +00:00
TargetRectangle Renderer :: ConvertEFBRectangle ( const EFBRectangle & rc )
{
return s_framebufferManager . ConvertEFBRectangle ( rc );
}
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-07-15 00:51:24 +00:00
glDisable ( GL_VERTEX_PROGRAM_ARB );
glDisable ( GL_FRAGMENT_PROGRAM_ARB );
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 );
}
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-07-15 00:51:24 +00:00
u32 Renderer :: AccessEFB ( EFBAccessType type , int x , int y )
{
// Get the rectangular target region covered by the EFB pixel.
EFBRectangle efbPixelRc ;
efbPixelRc . left = x ;
efbPixelRc . top = y ;
efbPixelRc . right = x + 1 ;
efbPixelRc . bottom = y + 1 ;
TargetRectangle targetPixelRc = Renderer :: ConvertEFBRectangle ( efbPixelRc );
2009-08-22 13:22:35 +00:00
// TODO (FIX) : currently, AA path is broken/offset and doesn't return the correct pixel
2009-07-15 00:51:24 +00:00
switch ( type )
{
case PEEK_Z :
{
if ( s_MSAASamples > 1 )
{
2009-07-16 09:06:16 +00:00
// Resolve our rectangle.
s_framebufferManager . GetEFBDepthTexture ( efbPixelRc );
glBindFramebufferEXT ( GL_READ_FRAMEBUFFER_EXT , s_framebufferManager . GetResolvedFramebuffer ());
2009-07-15 00:51:24 +00:00
}
// Sample from the center of the target region.
int srcX = ( targetPixelRc . left + targetPixelRc . right ) / 2 ;
int srcY = ( targetPixelRc . top + targetPixelRc . bottom ) / 2 ;
2009-07-15 15:09:20 +00:00
u32 z = 0 ;
2009-07-15 00:51:24 +00:00
glReadPixels ( srcX , srcY , 1 , 1 , GL_DEPTH_COMPONENT , GL_UNSIGNED_INT , & z );
GL_REPORT_ERRORD ();
// Scale the 32-bit value returned by glReadPixels to a 24-bit
// value (GC uses a 24-bit Z-buffer).
2009-08-25 18:30:15 +00:00
// TODO: in RE0 this value is often off by one, which causes lighting to disappear
2009-07-15 00:51:24 +00:00
return z >> 8 ;
}
case POKE_Z :
// TODO: Implement
break ;
2009-08-22 13:22:35 +00:00
case PEEK_COLOR : // GXPeekARGB
2009-07-16 09:06:16 +00:00
{
2009-08-22 13:22:35 +00:00
// 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
2009-07-16 22:45:18 +00:00
2009-07-16 09:06:16 +00:00
if ( s_MSAASamples > 1 )
{
// Resolve our rectangle.
s_framebufferManager . GetEFBColorTexture ( efbPixelRc );
glBindFramebufferEXT ( GL_READ_FRAMEBUFFER_EXT , s_framebufferManager . GetResolvedFramebuffer ());
}
2009-07-15 15:09:20 +00:00
2009-07-16 09:06:16 +00:00
// Sample from the center of the target region.
int srcX = ( targetPixelRc . left + targetPixelRc . right ) / 2 ;
int srcY = ( targetPixelRc . top + targetPixelRc . bottom ) / 2 ;
2009-07-15 15:09:20 +00:00
2009-08-22 13:22:35 +00:00
// Read back pixel in BGRA format, then byteswap to get GameCube's ARGB Format.
2009-07-16 09:06:16 +00:00
u32 color = 0 ;
2009-08-22 13:22:35 +00:00
glReadPixels ( srcX , srcY , 1 , 1 , GL_BGRA , GL_UNSIGNED_INT_8_8_8_8_REV , & color );
2009-07-16 09:06:16 +00:00
GL_REPORT_ERRORD ();
2009-07-15 15:09:20 +00:00
2009-08-22 13:22:35 +00:00
return color ;
2009-07-16 09:06:16 +00:00
}
2009-07-15 00:51:24 +00:00
case POKE_COLOR :
// TODO: Implement. One way is to draw a tiny pixel-sized rectangle at
// the exact location. Note: EFB pokes are susceptible to Z-buffering
// and perhaps blending.
//WARN_LOG(VIDEOINTERFACE, "This is probably some kind of software rendering");
break ;
}
return 0 ;
}
2009-06-08 00:44:48 +00:00
// Apply AA if enabled
2009-07-15 00:51:24 +00:00
GLuint Renderer :: ResolveAndGetRenderTarget ( const EFBRectangle & source_rect )
2009-06-08 00:44:48 +00:00
{
2009-06-28 23:35:08 +00:00
return s_framebufferManager . GetEFBColorTexture ( source_rect );
2009-06-08 00:44:48 +00:00
}
2009-07-15 00:51:24 +00:00
GLuint Renderer :: ResolveAndGetDepthTarget ( const EFBRectangle & source_rect )
2009-06-08 00:44:48 +00:00
{
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-07-15 00:51:24 +00:00
static void ComputeBackbufferRectangle ( TargetRectangle * 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 ;
// 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 ;
2009-07-15 00:51:24 +00:00
rc -> top = YOffset + ceil ( FloatGLHeight );
2009-02-28 16:33:59 +00:00
rc -> right = XOffset + ceil ( FloatGLWidth );
2009-07-15 00:51:24 +00:00
rc -> bottom = YOffset ;
2009-02-28 16:33:59 +00:00
}
2009-02-25 05:54:36 +00:00
2009-07-15 00:51:24 +00:00
void Renderer :: ClearScreen ( const EFBRectangle & rc , bool colorEnable , bool alphaEnable , bool zEnable , u32 color , u32 z )
{
// Update the view port for clearing the picture
glViewport ( 0 , 0 , Renderer :: GetTargetWidth (), Renderer :: GetTargetHeight ());
TargetRectangle targetRc = Renderer :: ConvertEFBRectangle ( rc );
// Always set the scissor in case it was set by the game and has not been reset
glScissor ( targetRc . left , targetRc . bottom , targetRc . GetWidth (), targetRc . GetHeight ());
VertexShaderManager :: SetViewportChanged ();
GLbitfield bits = 0 ;
if ( colorEnable )
{
bits |= GL_COLOR_BUFFER_BIT ;
glClearColor (
(( color >> 16 ) & 0xFF ) / 255.0f ,
(( color >> 8 ) & 0xFF ) / 255.0f ,
( color & 0xFF ) / 255.0f ,
( alphaEnable ? (( color >> 24 ) & 0xFF ) / 255.0f : 1.0f )
);
}
if ( zEnable )
{
bits |= GL_DEPTH_BUFFER_BIT ;
glClearDepth (( z & 0xFFFFFF ) / float ( 0xFFFFFF ));
}
glDrawBuffer ( GL_COLOR_ATTACHMENT0_EXT );
glClear ( bits );
}
void Renderer :: RenderToXFB ( u32 xfbAddr , u32 fbWidth , u32 fbHeight , const EFBRectangle & sourceRc )
2009-06-26 08:57:53 +00:00
{
2009-08-08 01:39:56 +00:00
s_skipSwap = g_bSkipCurrentFrame ;
2009-07-11 02:34:16 +00:00
// If we're about to write to a requested XFB, make sure the previous
2009-06-29 07:30:48 +00:00
// contents make it to the screen first.
2009-07-11 02:34:16 +00:00
VideoFifo_CheckSwapRequestAt ( xfbAddr , fbWidth , fbHeight );
2009-08-04 22:36:50 +00:00
s_framebufferManager . CopyToXFB ( xfbAddr , fbWidth , fbHeight , sourceRc );
2009-08-08 05:47:08 +00:00
// TODO: Find better name for this because I don't know if it means what it says.
g_VideoInitialize . pCopiedToXFB ( false );
2009-08-04 22:36:50 +00:00
// XXX: Without the VI, how would we know what kind of field this is? So
// just use progressive.
2009-08-25 18:30:15 +00:00
if ( ! g_Config . bUseXFB )
Renderer :: Swap ( xfbAddr , FIELD_PROGRESSIVE , fbWidth , fbHeight );
2009-06-26 08:57:53 +00:00
}
2009-06-07 12:06:15 +00:00
2009-06-29 07:30:48 +00:00
// This function has the final picture. We adjust the aspect ratio here.
2009-07-11 02:34:16 +00:00
void Renderer :: Swap ( u32 xfbAddr , FieldType field , u32 fbWidth , u32 fbHeight )
2009-02-28 16:33:59 +00:00
{
2009-08-08 01:39:56 +00:00
if ( s_skipSwap )
return ;
2009-07-11 02:34:16 +00:00
const XFBSource * xfbSource = s_framebufferManager . GetXFBSource ( xfbAddr , fbWidth , fbHeight );
2009-06-29 07:30:48 +00:00
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-07-15 00:51:24 +00:00
TargetRectangle back_rc ;
2009-02-28 16:33:59 +00:00
ComputeBackbufferRectangle ( & back_rc );
2009-07-15 00:51:24 +00:00
TargetRectangle sourceRc ;
2009-07-02 01:49:28 +00:00
2009-07-15 00:51:24 +00:00
if ( g_Config . bAutoScale || g_Config . bUseXFB )
2009-06-26 10:47:37 +00:00
{
2009-07-15 00:51:24 +00:00
sourceRc = xfbSource -> sourceRc ;
2009-02-28 16:33:59 +00:00
}
2009-03-08 19:19:51 +00:00
else
{
2009-07-15 00:51:24 +00:00
sourceRc . left = 0 ;
sourceRc . top = xfbSource -> texHeight ;
sourceRc . right = xfbSource -> texWidth ;
sourceRc . bottom = 0 ;
2009-03-08 19:19:51 +00:00
}
2009-07-02 01:49:28 +00:00
2009-07-11 02:34:16 +00:00
int yOffset = ( g_Config . bUseXFB && field == FIELD_LOWER ) ? - 1 : 0 ;
2009-07-15 00:51:24 +00:00
sourceRc . top -= yOffset ;
sourceRc . bottom -= 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
2009-07-15 00:51:24 +00:00
glViewport ( back_rc . left , back_rc . bottom , back_rc . GetWidth (), back_rc . GetHeight ());
2009-06-26 08:57:53 +00:00
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.
2009-07-15 00:51:24 +00:00
if ( PostProcessing :: ApplyShader ())
2009-03-08 19:19:51 +00:00
{
2009-06-26 08:57:53 +00:00
glBegin ( GL_QUADS );
2009-07-15 00:51:24 +00:00
glTexCoord2f ( sourceRc . left , sourceRc . bottom ); glMultiTexCoord2fARB ( GL_TEXTURE1 , 0 , 0 ); glVertex2f ( - 1 , - 1 );
glTexCoord2f ( sourceRc . left , sourceRc . top ); glMultiTexCoord2fARB ( GL_TEXTURE1 , 0 , 1 ); glVertex2f ( - 1 , 1 );
glTexCoord2f ( sourceRc . right , sourceRc . top ); glMultiTexCoord2fARB ( GL_TEXTURE1 , 1 , 1 ); glVertex2f ( 1 , 1 );
glTexCoord2f ( sourceRc . right , sourceRc . bottom ); glMultiTexCoord2fARB ( GL_TEXTURE1 , 1 , 0 ); glVertex2f ( 1 , - 1 );
2009-06-26 08:57:53 +00:00
glEnd ();
glBindProgramARB ( GL_FRAGMENT_PROGRAM_ARB , 0 );
glDisable ( GL_FRAGMENT_PROGRAM_ARB );
2009-03-08 19:19:51 +00:00
}
2009-07-15 00:51:24 +00:00
else
2009-03-08 19:19:51 +00:00
{
2009-06-26 08:57:53 +00:00
glBegin ( GL_QUADS );
2009-07-15 00:51:24 +00:00
glTexCoord2f ( sourceRc . left , sourceRc . bottom ); glVertex2f ( - 1 , - 1 );
glTexCoord2f ( sourceRc . left , sourceRc . top ); glVertex2f ( - 1 , 1 );
glTexCoord2f ( sourceRc . right , sourceRc . top ); glVertex2f ( 1 , 1 );
glTexCoord2f ( sourceRc . right , sourceRc . bottom ); glVertex2f ( 1 , - 1 );
2009-06-26 08:57:53 +00:00
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-07-15 00:51:24 +00:00
SaveRenderTarget ( s_sScreenshotName . c_str (), xfbSource -> sourceRc . GetWidth (), xfbSource -> sourceRc . GetHeight (), yOffset );
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-07-29 00:49:12 +00:00
2009-03-28 21:07:16 +00:00
u8 * data = ( u8 * ) malloc ( 3 * w * h );
glPixelStorei ( GL_PACK_ALIGNMENT , 1 );
2009-07-29 00:49:12 +00:00
glReadPixels ( 0 , Renderer :: GetTargetHeight () - h + yOffset , 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 );
2009-07-29 00:49:12 +00:00
glReadPixels ( 0 , Renderer :: GetTargetHeight () - h , w , h , GL_RGB , GL_UNSIGNED_BYTE , data );
2009-03-28 21:07:16 +00:00
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-02-23 07:23:34 +00:00
// Place messages on the picture, then copy it to the screen
SwapBuffers ();
2009-07-02 01:49:28 +00:00
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
//////////////////////////////////////////////////////////////////////////////////////////
// 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
2009-08-08 01:39:56 +00:00
// Copy the rendered frame to the real window
2009-06-07 12:06:15 +00:00
OpenGL_SwapBuffers ();
2009-08-08 01:39:56 +00:00
2009-06-13 22:08:01 +00:00
GL_REPORT_ERRORD ();
2009-08-08 01:39:56 +00:00
2009-06-08 20:07:20 +00:00
// Clear framebuffer
2009-06-07 12:06:15 +00:00
glClearColor ( 0 , 0 , 0 , 0 );
glClear ( GL_COLOR_BUFFER_BIT );
2009-08-08 01:39:56 +00:00
GL_REPORT_ERRORD ();
2009-06-07 12:06:15 +00:00
2009-08-08 01:39:56 +00:00
// Clean out old stuff from caches
2009-08-09 11:03:58 +00:00
DLCache :: ProgressiveCleanup ();
2009-06-07 12:06:15 +00:00
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-07-15 00:51:24 +00:00
for ( std :: vector < EFBRectangle >:: const_iterator it = stats . efb_regions . begin (); it != stats . efb_regions . end (); ++ it )
2009-03-16 07:54:44 +00:00
{
2009-07-28 05:02:47 +00:00
GLfloat halfWidth = EFB_WIDTH / 2.0f ;
GLfloat halfHeight = EFB_HEIGHT / 2.0f ;
2009-03-21 16:04:44 +00:00
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 );
2009-08-09 11:03:58 +00:00
p += sprintf ( p , "dlists alive: %i \n " , stats . numDListsAlive );
2009-02-23 07:23:34 +00:00
// 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-07-31 01:55:26 +00:00
#if defined(HAVE_WX) && HAVE_WX
2009-07-29 03:11:35 +00:00
THREAD_RETURN TakeScreenshot ( void * pArgs )
{
ScrStrct * threadStruct = ( ScrStrct * ) pArgs ;
// These will contain the final image size
float FloatW = ( float ) threadStruct -> W ;
float FloatH = ( float ) threadStruct -> H ;
// Handle aspect ratio for the final ScrStrct to look exactly like what's on screen.
if ( g_Config . bKeepAR43 || g_Config . bKeepAR169 )
{
float Ratio = ( FloatW / FloatH ) / ( g_Config . bKeepAR43 ? ( 4.0f / 3.0f ) : ( 16.0f / 9.0f ));
// If ratio > 1 the picture is too wide and we have to limit the width.
if ( Ratio > 1 )
FloatW /= Ratio ;
// ratio == 1 or the image is too high, we have to limit the height.
else
FloatH *= Ratio ;
2009-07-31 01:55:26 +00:00
// This is a bit expensive on high resolutions
2009-07-29 03:11:35 +00:00
threadStruct -> img -> Rescale (( int ) FloatW , ( int ) FloatH , wxIMAGE_QUALITY_HIGH );
}
// Save the screenshot and finally kill the wxImage object
2009-07-31 01:55:26 +00:00
// This is really expensive when saving to PNG, but not at all when using BMP
2009-07-29 03:11:35 +00:00
threadStruct -> img -> SaveFile ( wxString :: FromAscii ( threadStruct -> filename . c_str ()), wxBITMAP_TYPE_PNG );
threadStruct -> img -> Destroy ();
// Show success messages
OSD :: AddMessage ( StringFromFormat ( "Saved %i x %i %s" , ( int ) FloatW , ( int ) FloatH , threadStruct -> filename . c_str ()). c_str (), 2000 );
2009-07-31 01:55:26 +00:00
delete threadStruct ;
2009-07-29 03:11:35 +00:00
return 0 ;
}
2009-07-31 01:55:26 +00:00
#endif
2009-07-29 03:11:35 +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-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-07-28 23:38:49 +00:00
glReadPixels ( 0 , Renderer :: GetTargetHeight () - H + 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
2009-07-29 03:11:35 +00:00
wxImage * a = new wxImage ( W , H , data );
2009-06-06 13:36:33 +00:00
2009-07-29 03:11:35 +00:00
if ( scrshotThread )
2009-07-28 23:38:49 +00:00
{
2009-07-29 03:11:35 +00:00
delete scrshotThread ;
scrshotThread = NULL ;
2009-07-28 23:38:49 +00:00
}
2009-07-29 03:11:35 +00:00
ScrStrct * threadStruct = new ScrStrct ;
threadStruct -> filename = std :: string ( filename );
threadStruct -> img = a ;
threadStruct -> H = H ; threadStruct -> W = W ;
scrshotThread = new Common :: Thread ( TakeScreenshot , threadStruct );
2009-07-31 01:55:26 +00:00
#ifdef _WIN32
scrshotThread -> SetPriority ( THREAD_PRIORITY_BELOW_NORMAL );
#endif
2009-03-28 21:07:16 +00:00
bool result = true ;
2009-06-06 13:36:33 +00:00
2009-07-29 03:11:35 +00:00
OSD :: AddMessage ( "Saving Screenshot... " , 2000 );
2009-06-06 13:36:33 +00:00
2009-03-28 21:07:16 +00:00
#else
2009-07-13 06:34:12 +00:00
bool result = SaveTGA ( filename , W , H , data );
2009-07-28 23:38:49 +00:00
free ( data );
2009-07-29 00:49:12 +00:00
#endif
2009-07-28 23:38:49 +00:00
2009-03-28 21:07:16 +00:00
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-07-15 00:51:24 +00:00
// This function does not have the final picture. Use Renderer::Swap() to adjust the final picture.
2009-02-23 07:23:34 +00:00
// Call schedule: Called from VertexShaderManager
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-08-24 23:36:28 +00:00
double GLNear = ( xfregs . rawViewport [ 5 ] - xfregs . rawViewport [ 2 ]) / 16777215.0f ;
double GLFar = xfregs . rawViewport [ 5 ] / 16777215.0f ;
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
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