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/
|
|
|
|
|
|
|
|
#include "Globals.h"
|
2009-07-12 21:58:32 +00:00
|
|
|
#include "Thread.h"
|
2010-01-26 14:56:51 +00:00
|
|
|
#include "Atomic.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"
|
|
|
|
|
2010-02-02 21:56:29 +00:00
|
|
|
#include "FileUtil.h"
|
|
|
|
|
2009-02-23 07:23:34 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <mmsystem.h>
|
|
|
|
#endif
|
|
|
|
|
2009-03-28 21:07:16 +00:00
|
|
|
#include "CommonPaths.h"
|
2009-09-13 09:23:30 +00:00
|
|
|
#include "VideoConfig.h"
|
2009-02-23 07:23:34 +00:00
|
|
|
#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"
|
2010-09-28 02:15:02 +00:00
|
|
|
#include "TextureCache.h"
|
2009-10-13 06:12:58 +00:00
|
|
|
#include "RasterFont.h"
|
2009-02-23 07:23:34 +00:00
|
|
|
#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-06-07 12:06:15 +00:00
|
|
|
// Declarations and definitions
|
2009-09-08 16:07:13 +00:00
|
|
|
// ----------------------------
|
2010-02-20 04:18:19 +00:00
|
|
|
int s_fps=0;
|
|
|
|
|
2010-07-24 10:24:16 +00:00
|
|
|
#if defined HAVE_CG && HAVE_CG
|
2009-02-23 07:23:34 +00:00
|
|
|
CGcontext g_cgcontext;
|
|
|
|
CGprofile g_cgvProf;
|
|
|
|
CGprofile g_cgfProf;
|
2010-07-24 10:24:16 +00:00
|
|
|
#endif
|
2009-02-23 07:23:34 +00:00
|
|
|
|
|
|
|
RasterFont* s_pfont = NULL;
|
|
|
|
|
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;
|
2010-10-01 12:38:31 +00:00
|
|
|
static int s_LastEFBScale;
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2009-02-28 16:33:59 +00:00
|
|
|
static volatile bool s_bScreenshot = false;
|
2010-05-26 20:46:28 +00:00
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
2009-07-31 01:55:26 +00:00
|
|
|
static Common::Thread *scrshotThread = 0;
|
2010-05-26 20:46:28 +00:00
|
|
|
#endif
|
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
|
|
|
|
2009-09-06 15:11:21 +00:00
|
|
|
// The custom resolution
|
2010-09-30 15:24:34 +00:00
|
|
|
static int s_Fulltarget_width;
|
|
|
|
static int s_Fulltarget_height;
|
|
|
|
|
2009-03-05 23:11:13 +00:00
|
|
|
// TODO: Add functionality to reinit all the render targets when the window is resized.
|
2010-09-28 02:15:02 +00:00
|
|
|
static int s_backbuffer_width;
|
|
|
|
static int s_backbuffer_height;
|
2009-09-06 15:11:21 +00:00
|
|
|
// The framebuffer size
|
2010-09-28 02:15:02 +00:00
|
|
|
static int s_target_width;
|
|
|
|
static int s_target_height;
|
2009-02-28 16:33:59 +00:00
|
|
|
|
2010-05-26 20:46:28 +00:00
|
|
|
static unsigned int s_XFB_width;
|
|
|
|
static unsigned int s_XFB_height;
|
2010-05-19 03:15:36 +00:00
|
|
|
|
|
|
|
static float xScale;
|
|
|
|
static float yScale;
|
|
|
|
|
2010-08-10 01:08:22 +00:00
|
|
|
static float EFBxScale;
|
|
|
|
static float EFByScale;
|
2010-05-19 03:15:36 +00:00
|
|
|
|
2009-08-08 01:39:56 +00:00
|
|
|
static bool s_skipSwap = false;
|
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
static bool XFBWrited = false;
|
|
|
|
|
2009-06-09 21:29:54 +00:00
|
|
|
int OSDChoice = 0 , OSDTime = 0, OSDInternalW = 0, OSDInternalH = 0;
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-08-10 01:08:22 +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] =
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
GL_ZERO,
|
|
|
|
GL_ONE,
|
|
|
|
GL_DST_COLOR,
|
|
|
|
GL_ONE_MINUS_DST_COLOR,
|
|
|
|
GL_SRC_ALPHA,
|
2010-08-10 01:08:22 +00:00
|
|
|
GL_ONE_MINUS_SRC_ALPHA,
|
2010-06-05 01:38:22 +00:00
|
|
|
GL_DST_ALPHA,
|
|
|
|
GL_ONE_MINUS_DST_ALPHA
|
2009-02-23 07:23:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const GLenum glDestFactors[8] = {
|
2010-08-10 01:08:22 +00:00
|
|
|
GL_ZERO,
|
|
|
|
GL_ONE,
|
|
|
|
GL_SRC_COLOR,
|
2009-10-25 02:35:21 +00:00
|
|
|
GL_ONE_MINUS_SRC_COLOR,
|
2010-08-10 01:08:22 +00:00
|
|
|
GL_SRC_ALPHA,
|
|
|
|
GL_ONE_MINUS_SRC_ALPHA,
|
|
|
|
GL_DST_ALPHA,
|
2009-10-25 02:35:21 +00:00
|
|
|
GL_ONE_MINUS_DST_ALPHA
|
2009-02-23 07:23:34 +00:00
|
|
|
};
|
|
|
|
|
2009-11-27 19:42:27 +00:00
|
|
|
static const GLenum glCmpFuncs[8] = {
|
2010-05-19 03:15:36 +00:00
|
|
|
GL_NEVER,
|
|
|
|
GL_LESS,
|
|
|
|
GL_EQUAL,
|
|
|
|
GL_LEQUAL,
|
|
|
|
GL_GREATER,
|
|
|
|
GL_NOTEQUAL,
|
|
|
|
GL_GEQUAL,
|
|
|
|
GL_ALWAYS
|
2009-11-27 19:42:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const GLenum glLogicOpCodes[16] = {
|
2010-06-05 01:38:22 +00:00
|
|
|
GL_CLEAR,
|
2010-05-19 03:15:36 +00:00
|
|
|
GL_AND,
|
|
|
|
GL_AND_REVERSE,
|
|
|
|
GL_COPY,
|
|
|
|
GL_AND_INVERTED,
|
|
|
|
GL_NOOP,
|
2010-08-10 01:08:22 +00:00
|
|
|
GL_XOR,
|
2010-05-19 03:15:36 +00:00
|
|
|
GL_OR,
|
|
|
|
GL_NOR,
|
|
|
|
GL_EQUIV,
|
|
|
|
GL_INVERT,
|
|
|
|
GL_OR_REVERSE,
|
|
|
|
GL_COPY_INVERTED,
|
|
|
|
GL_OR_INVERTED,
|
|
|
|
GL_NAND,
|
|
|
|
GL_SET
|
2009-11-27 19:42:27 +00:00
|
|
|
};
|
|
|
|
|
2010-07-24 10:24:16 +00:00
|
|
|
#if defined HAVE_CG && HAVE_CG
|
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);
|
2010-08-10 01:08:22 +00:00
|
|
|
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
|
|
|
}
|
2010-07-24 10:24:16 +00:00
|
|
|
#endif
|
2009-03-05 23:11:13 +00:00
|
|
|
|
2009-06-07 12:06:15 +00:00
|
|
|
} // namespace
|
2009-09-18 19:56:49 +00:00
|
|
|
void VideoConfig::UpdateProjectionHack()
|
|
|
|
{
|
|
|
|
::UpdateProjectionHack(g_Config.iPhackvalue);
|
|
|
|
}
|
2009-06-07 12:06:15 +00:00
|
|
|
|
|
|
|
// Init functions
|
2009-02-23 07:23:34 +00:00
|
|
|
bool Renderer::Init()
|
|
|
|
{
|
2009-09-13 08:21:35 +00:00
|
|
|
UpdateActiveConfig();
|
2010-06-05 01:38:22 +00:00
|
|
|
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;
|
2010-07-24 10:24:16 +00:00
|
|
|
GLint numvertexattribs = 0;
|
|
|
|
|
2009-09-13 08:21:35 +00:00
|
|
|
switch (g_ActiveConfig.iMultisampleMode)
|
2009-03-08 20:04:40 +00:00
|
|
|
{
|
2010-05-19 03:15:36 +00:00
|
|
|
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;
|
2010-08-10 01:08:22 +00:00
|
|
|
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;
|
2010-05-19 03:15:36 +00:00
|
|
|
default:
|
2009-03-08 20:04:40 +00:00
|
|
|
s_MSAASamples = 1;
|
|
|
|
}
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-07-24 10:24:16 +00:00
|
|
|
#if defined HAVE_CG && HAVE_CG
|
|
|
|
g_cgcontext = cgCreateContext();
|
2010-06-05 01:38:22 +00:00
|
|
|
cgGetError();
|
2009-03-08 19:19:51 +00:00
|
|
|
cgSetErrorHandler(HandleCgError, NULL);
|
2010-07-24 10:24:16 +00:00
|
|
|
#endif
|
2009-03-05 23:11:13 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
// Look for required extensions.
|
|
|
|
const char *ptoken = (const char*)glGetString(GL_EXTENSIONS);
|
2009-02-28 16:33:59 +00:00
|
|
|
if (!ptoken)
|
|
|
|
{
|
2009-04-03 21:32:10 +00:00
|
|
|
PanicAlert("Your OpenGL Driver seems to be not working.\n"
|
2010-08-10 01:08:22 +00:00
|
|
|
"Please make sure your drivers are up-to-date and\n"
|
|
|
|
"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
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
INFO_LOG(VIDEO, "Supported OpenGL Extensions:");
|
|
|
|
INFO_LOG(VIDEO, ptoken); // write to the log file
|
|
|
|
INFO_LOG(VIDEO, "");
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-08-10 01:08:22 +00:00
|
|
|
OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s",
|
|
|
|
glGetString(GL_VENDOR),
|
|
|
|
glGetString(GL_RENDERER),
|
|
|
|
glGetString(GL_VERSION)).c_str(), 5000);
|
2009-04-03 20:22:45 +00:00
|
|
|
|
2009-02-28 16:33:59 +00:00
|
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &numvertexattribs);
|
2010-08-10 01:08:22 +00:00
|
|
|
if (numvertexattribs < 11)
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "GPU: OGL ERROR: Number of attributes %d not enough.\n"
|
|
|
|
"GPU: Does your video card support OpenGL 2.x?",
|
|
|
|
numvertexattribs);
|
2010-06-05 01:38:22 +00:00
|
|
|
bSuccess = false;
|
|
|
|
}
|
2009-02-28 16:33:59 +00:00
|
|
|
|
|
|
|
// Init extension support.
|
2010-08-10 01:08:22 +00:00
|
|
|
if (glewInit() != GLEW_OK)
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "glewInit() failed! Does your video card support OpenGL 2.x?");
|
2010-06-05 01:38:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
|
|
|
if (!GLEW_EXT_framebuffer_object)
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "GPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets.\n"
|
|
|
|
"GPU: Does your video card support OpenGL 2.x?");
|
2010-06-05 01:38:22 +00:00
|
|
|
bSuccess = false;
|
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
|
|
|
if (!GLEW_EXT_secondary_color)
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "GPU: OGL ERROR: Need GL_EXT_secondary_color.\n"
|
|
|
|
"GPU: Does your video card support OpenGL 2.x?");
|
2010-06-05 01:38:22 +00:00
|
|
|
bSuccess = false;
|
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
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;
|
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-03-08 20:04:40 +00:00
|
|
|
s_bHaveCoverageMSAA = strstr(ptoken, "GL_NV_framebuffer_multisample_coverage") != NULL;
|
|
|
|
if (!s_bHaveCoverageMSAA)
|
|
|
|
{
|
|
|
|
s_MSAACoverageSamples = 0;
|
|
|
|
}
|
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
if (!bSuccess)
|
|
|
|
return false;
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Decide frambuffer size
|
|
|
|
s_backbuffer_width = (int)OpenGL_GetBackbufferWidth();
|
|
|
|
s_backbuffer_height = (int)OpenGL_GetBackbufferHeight();
|
|
|
|
|
2009-03-05 23:11:13 +00:00
|
|
|
// Handle VSync on/off
|
2009-09-09 20:47:11 +00:00
|
|
|
#if defined USE_WX && USE_WX
|
|
|
|
// TODO: FILL IN
|
|
|
|
#elif defined _WIN32
|
2009-02-28 16:33:59 +00:00
|
|
|
if (WGLEW_EXT_swap_control)
|
2009-09-13 08:21:35 +00:00
|
|
|
wglSwapIntervalEXT(g_ActiveConfig.bVSync ? 1 : 0);
|
2009-02-28 16:33:59 +00:00
|
|
|
else
|
2010-08-10 01:08:22 +00:00
|
|
|
ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate).");
|
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-09-13 08:21:35 +00:00
|
|
|
glXSwapIntervalSGI(g_ActiveConfig.bVSync ? 1 : 0);
|
2009-02-28 16:33:59 +00:00
|
|
|
else
|
2010-08-10 01:08:22 +00:00
|
|
|
ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate).");
|
2009-02-23 07:23:34 +00:00
|
|
|
#endif
|
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
// check the max texture width and height
|
2009-02-23 07:23:34 +00:00
|
|
|
GLint max_texture_size;
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&max_texture_size);
|
2010-08-10 01:08:22 +00:00
|
|
|
if (max_texture_size < 1024)
|
|
|
|
ERROR_LOG(VIDEO, "GL_MAX_TEXTURE_SIZE too small at %i - must be at least 1024.",
|
|
|
|
max_texture_size);
|
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
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
if (glDrawBuffers == NULL && !GLEW_ARB_draw_buffers)
|
|
|
|
glDrawBuffers = glDrawBuffersARB;
|
2010-08-10 01:08:22 +00:00
|
|
|
|
|
|
|
if (!GLEW_ARB_texture_non_power_of_two)
|
2010-01-18 21:11:50 +00:00
|
|
|
WARN_LOG(VIDEO, "ARB_texture_non_power_of_two not supported.");
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-05-19 03:15:36 +00:00
|
|
|
s_XFB_width = MAX_XFB_WIDTH;
|
|
|
|
s_XFB_height = MAX_XFB_HEIGHT;
|
|
|
|
|
|
|
|
TargetRectangle dst_rect;
|
2010-09-28 02:15:02 +00:00
|
|
|
ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, false, &dst_rect);
|
2010-05-19 03:15:36 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
if(g_ActiveConfig.bUseRealXFB)
|
2009-06-28 23:35:08 +00:00
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
xScale = 1.0f;
|
|
|
|
yScale = 1.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
xScale = (float)(dst_rect.right - dst_rect.left) / (float)s_XFB_width;
|
|
|
|
yScale = (float)(dst_rect.bottom - dst_rect.top) / (float)s_XFB_height;
|
2009-06-28 23:35:08 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
s_LastEFBScale = g_ActiveConfig.iEFBScale;
|
|
|
|
switch(s_LastEFBScale)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
EFBxScale = xScale;
|
|
|
|
EFByScale = yScale;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
EFBxScale = ceilf(xScale);
|
|
|
|
EFByScale = ceilf(yScale);
|
|
|
|
break;
|
|
|
|
default:
|
2010-10-01 11:03:37 +00:00
|
|
|
EFBxScale = (float)(g_ActiveConfig.iEFBScale - 1);
|
2010-09-30 15:24:34 +00:00
|
|
|
EFByScale = EFBxScale;
|
|
|
|
break;
|
|
|
|
};
|
2010-05-19 03:15:36 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
s_target_width = EFB_WIDTH * EFBxScale;
|
|
|
|
s_target_height = EFB_HEIGHT * EFByScale;
|
2009-09-06 15:11:21 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
s_Fulltarget_width = s_target_width;
|
|
|
|
s_Fulltarget_height = s_target_height;
|
|
|
|
|
2010-08-10 01:08:22 +00:00
|
|
|
// Because of the fixed framebuffer size we need to disable the resolution
|
|
|
|
// options while running
|
2009-10-22 20:22:50 +00:00
|
|
|
g_Config.bRunning = true;
|
2009-02-28 16:33:59 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
if (GL_REPORT_ERROR() != GL_NO_ERROR)
|
2009-03-22 11:21:44 +00:00
|
|
|
bSuccess = false;
|
|
|
|
|
2009-06-28 23:35:08 +00:00
|
|
|
// Initialize the FramebufferManager
|
2010-09-28 02:15:02 +00:00
|
|
|
g_framebufferManager.Init(s_target_width, s_target_height,
|
2010-08-10 01:08:22 +00:00
|
|
|
s_MSAASamples, s_MSAACoverageSamples);
|
2009-09-06 13:36:05 +00:00
|
|
|
|
2009-03-08 19:19:51 +00:00
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
if (GL_REPORT_ERROR() != GL_NO_ERROR)
|
2009-02-23 07:23:34 +00:00
|
|
|
bSuccess = false;
|
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
s_pfont = new RasterFont();
|
|
|
|
|
2010-07-24 10:24:16 +00:00
|
|
|
#if defined HAVE_CG && HAVE_CG
|
2010-06-05 01:38:22 +00:00
|
|
|
// load the effect, find the best profiles (if any)
|
2010-08-10 01:08:22 +00:00
|
|
|
if (cgGLIsProfileSupported(CG_PROFILE_ARBVP1) != CG_TRUE)
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
ERROR_LOG(VIDEO, "arbvp1 not supported");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-08-10 01:08:22 +00:00
|
|
|
if (cgGLIsProfileSupported(CG_PROFILE_ARBFP1) != CG_TRUE)
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
ERROR_LOG(VIDEO, "arbfp1 not supported");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_cgvProf = cgGLGetLatestProfile(CG_GL_VERTEX);
|
|
|
|
g_cgfProf = cgGLGetLatestProfile(CG_GL_FRAGMENT);
|
2010-10-06 18:03:31 +00:00
|
|
|
if (strstr((const char*)glGetString(GL_VENDOR), "Humper") == NULL)
|
|
|
|
{
|
2010-07-03 21:21:28 +00:00
|
|
|
#if CG_VERSION_NUM == 2100
|
2010-07-03 19:10:23 +00:00
|
|
|
// A bug was introduced in Cg2.1's handling of very large profile option values
|
|
|
|
// so this will not work on ATI. ATI returns MAXINT = 2147483647 (0x7fffffff)
|
|
|
|
// which is correct in OpenGL but Cg fails to handle it properly. As a result
|
|
|
|
// -1 is used by Cg resulting (signedness incorrect) and compilation fails.
|
2010-10-06 18:03:31 +00:00
|
|
|
if (strstr((const char*)glGetString(GL_VENDOR), "ATI") == NULL)
|
2010-08-10 01:08:22 +00:00
|
|
|
#endif
|
2010-10-06 18:03:31 +00:00
|
|
|
{
|
|
|
|
cgGLSetOptimalOptions(g_cgvProf);
|
|
|
|
cgGLSetOptimalOptions(g_cgfProf);
|
|
|
|
}
|
2010-07-03 19:10:23 +00:00
|
|
|
}
|
2010-07-24 10:24:16 +00:00
|
|
|
#endif // HAVE_CG
|
2010-06-05 01:38:22 +00:00
|
|
|
|
|
|
|
int nenvvertparams, nenvfragparams, naddrregisters[2];
|
2010-08-10 01:08:22 +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]);
|
|
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,
|
|
|
|
GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB,
|
|
|
|
(GLint *)&naddrregisters[1]);
|
|
|
|
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)
|
2010-06-05 01:38:22 +00:00
|
|
|
ERROR_LOG(VIDEO, "Not enough vertex shader environment constants!!");
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-07-24 10:24:16 +00:00
|
|
|
#if defined HAVE_CG && HAVE_CG
|
|
|
|
INFO_LOG(VIDEO, "Max buffer sizes: %d %d",
|
|
|
|
cgGetProgramBufferMaxSize(g_cgvProf),
|
|
|
|
cgGetProgramBufferMaxSize(g_cgfProf));
|
2009-02-23 07:23:34 +00:00
|
|
|
#ifndef _DEBUG
|
2010-06-05 01:38:22 +00:00
|
|
|
cgGLSetDebugMode(GL_FALSE);
|
2010-07-24 10:24:16 +00:00
|
|
|
#endif
|
2009-02-23 07:23:34 +00:00
|
|
|
#endif
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
glStencilFunc(GL_ALWAYS, 0, 0);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
|
|
|
|
glViewport(0, 0, GetTargetWidth(), GetTargetHeight()); // Reset The Current Viewport
|
|
|
|
|
|
|
|
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);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
|
|
|
|
glScissor(0, 0, GetTargetWidth(), GetTargetHeight());
|
|
|
|
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-02-23 07:23:34 +00:00
|
|
|
|
2009-09-13 08:21:35 +00:00
|
|
|
UpdateActiveConfig();
|
2010-08-03 10:48:49 +00:00
|
|
|
return GL_REPORT_ERROR() == GL_NO_ERROR && bSuccess;
|
2009-02-23 07:23:34 +00:00
|
|
|
}
|
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
void Renderer::Shutdown()
|
2010-06-05 01:38:22 +00:00
|
|
|
{
|
2009-09-06 15:11:21 +00:00
|
|
|
g_Config.bRunning = false;
|
2009-09-13 08:21:35 +00:00
|
|
|
UpdateActiveConfig();
|
2010-06-05 01:38:22 +00:00
|
|
|
delete s_pfont;
|
2009-09-03 19:24:16 +00:00
|
|
|
s_pfont = 0;
|
|
|
|
|
2010-07-24 10:24:16 +00:00
|
|
|
#if defined HAVE_CG && HAVE_CG
|
2010-08-10 01:08:22 +00:00
|
|
|
if (g_cgcontext)
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
cgDestroyContext(g_cgcontext);
|
|
|
|
g_cgcontext = 0;
|
2009-09-03 19:24:16 +00:00
|
|
|
}
|
2010-07-24 10:24:16 +00:00
|
|
|
#endif
|
2009-09-03 19:24:16 +00:00
|
|
|
|
2010-08-08 00:13:05 +00:00
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
|
|
if (scrshotThread)
|
|
|
|
delete scrshotThread;
|
|
|
|
#endif
|
|
|
|
|
2009-09-03 20:37:35 +00:00
|
|
|
g_framebufferManager.Shutdown();
|
2009-09-03 19:24:16 +00:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
2010-08-10 01:08:22 +00:00
|
|
|
if(s_bAVIDumping)
|
2009-09-03 19:24:16 +00:00
|
|
|
AVIDump::Stop();
|
|
|
|
#else
|
2010-08-10 01:08:22 +00:00
|
|
|
if(f_pFrameDump != NULL)
|
2009-09-03 19:24:16 +00:00
|
|
|
fclose(f_pFrameDump);
|
|
|
|
#endif
|
|
|
|
}
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Return the rendering target width and height
|
|
|
|
int Renderer::GetTargetWidth()
|
2009-09-06 15:11:21 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
return s_target_width;
|
2009-09-06 15:11:21 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
int Renderer::GetTargetHeight()
|
2009-09-06 15:11:21 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
return s_target_height;
|
2009-09-06 15:11:21 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
int Renderer::GetFullTargetWidth()
|
2009-09-06 15:11:21 +00:00
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
return s_Fulltarget_width;
|
2009-09-06 15:11:21 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
int Renderer::GetFullTargetHeight()
|
2009-09-06 15:11:21 +00:00
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
return s_Fulltarget_height;
|
2009-02-28 16:33:59 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-02-28 16:33:59 +00:00
|
|
|
float Renderer::GetTargetScaleX()
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
return EFBxScale;
|
2009-02-28 16:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
float Renderer::GetTargetScaleY()
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
return EFByScale;
|
2009-02-23 07:23:34 +00:00
|
|
|
}
|
|
|
|
|
2010-08-10 01:08:22 +00:00
|
|
|
float Renderer::GetXFBScaleX()
|
|
|
|
{
|
|
|
|
return xScale;
|
|
|
|
}
|
2010-05-19 03:15:36 +00:00
|
|
|
|
2010-08-10 01:08:22 +00:00
|
|
|
float Renderer::GetXFBScaleY()
|
|
|
|
{
|
|
|
|
return yScale;
|
|
|
|
}
|
2010-05-19 03:15:36 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Create On-Screen-Messages
|
|
|
|
void Renderer::DrawDebugText()
|
2009-02-23 07:23:34 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
// Reset viewport for drawing text
|
|
|
|
glViewport(0, 0, OpenGL_GetBackbufferWidth(), OpenGL_GetBackbufferHeight());
|
|
|
|
// Draw various messages on the screen, like FPS, statistics, etc.
|
|
|
|
char debugtext_buffer[8192];
|
|
|
|
char *p = debugtext_buffer;
|
|
|
|
p[0] = 0;
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
if (g_ActiveConfig.bShowFPS)
|
|
|
|
p+=sprintf(p, "FPS: %d\n", s_fps);
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
if (g_ActiveConfig.bShowEFBCopyRegions)
|
|
|
|
{
|
|
|
|
// Store Line Size
|
|
|
|
GLfloat lSize;
|
|
|
|
glGetFloatv(GL_LINE_WIDTH, &lSize);
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Set Line Size
|
|
|
|
glLineWidth(3.0f);
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glBegin(GL_LINES);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Draw EFB copy regions rectangles
|
|
|
|
for (std::vector<EFBRectangle>::const_iterator it = stats.efb_regions.begin();
|
|
|
|
it != stats.efb_regions.end(); ++it)
|
|
|
|
{
|
|
|
|
GLfloat halfWidth = EFB_WIDTH / 2.0f;
|
|
|
|
GLfloat halfHeight = EFB_HEIGHT / 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-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Draw shadow of rect
|
|
|
|
glColor3f(0.0f, 0.0f, 0.0f);
|
|
|
|
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-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Draw rect
|
|
|
|
glColor3f(0.0f, 1.0f, 1.0f);
|
|
|
|
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-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glEnd();
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Restore Line Size
|
|
|
|
glLineWidth(lSize);
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Clear stored regions
|
|
|
|
stats.efb_regions.clear();
|
|
|
|
}
|
2010-01-07 20:01:41 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
if (g_ActiveConfig.bOverlayStats)
|
|
|
|
p = Statistics::ToString(p);
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
if (g_ActiveConfig.bOverlayProjStats)
|
|
|
|
p = Statistics::ToStringProj(p);
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Render a shadow, and then the text.
|
|
|
|
if (p != debugtext_buffer)
|
2009-07-15 00:51:24 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
Renderer::RenderText(debugtext_buffer, 21, 21, 0xDD000000);
|
|
|
|
Renderer::RenderText(debugtext_buffer, 20, 20, 0xFF00FFFF);
|
|
|
|
}
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// OSD Menu messages
|
|
|
|
if (g_ActiveConfig.bOSDHotKey)
|
2009-07-15 00:51:24 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
if (OSDChoice > 0)
|
2009-07-15 00:51:24 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
OSDTime = Common::Timer::GetTimeMs() + 3000;
|
|
|
|
OSDChoice = -OSDChoice;
|
2009-07-15 00:51:24 +00:00
|
|
|
}
|
2010-09-28 02:15:02 +00:00
|
|
|
if ((u32)OSDTime > Common::Timer::GetTimeMs())
|
|
|
|
{
|
|
|
|
std::string T1 = "", T2 = "";
|
|
|
|
std::vector<std::string> T0;
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
std::string OSDM1;
|
|
|
|
switch(g_ActiveConfig.iEFBScale)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
OSDM1 = "Auto (fractional)";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
OSDM1 = "Auto (integral)";
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
OSDM1 = "Native";
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
OSDM1 = "2x";
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
OSDM1 = "3x";
|
|
|
|
break;
|
|
|
|
}
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
std::string OSDM21;
|
|
|
|
switch(g_ActiveConfig.iAspectRatio)
|
|
|
|
{
|
|
|
|
case ASPECT_AUTO:
|
|
|
|
OSDM21 = "Auto";
|
|
|
|
break;
|
|
|
|
case ASPECT_FORCE_16_9:
|
|
|
|
OSDM21 = "16:9";
|
|
|
|
break;
|
|
|
|
case ASPECT_FORCE_4_3:
|
|
|
|
OSDM21 = "4:3";
|
|
|
|
break;
|
|
|
|
case ASPECT_STRETCH:
|
|
|
|
OSDM21 = "Stretch";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
std::string OSDM22 =
|
|
|
|
g_ActiveConfig.bCrop ? " (crop)" : "";
|
|
|
|
std::string OSDM3 = g_ActiveConfig.bEFBCopyDisable ? "Disabled" :
|
|
|
|
g_ActiveConfig.bCopyEFBToTexture ? "To Texture" : "To RAM";
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// If there is more text than this we will have a collision
|
|
|
|
if (g_ActiveConfig.bShowFPS)
|
|
|
|
{
|
|
|
|
T1 += "\n\n";
|
|
|
|
T2 += "\n\n";
|
|
|
|
}
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// The rows
|
|
|
|
T0.push_back(StringFromFormat("3: Internal Resolution: %s\n", OSDM1.c_str()));
|
|
|
|
T0.push_back(StringFromFormat("4: Aspect Ratio: %s%s\n", OSDM21.c_str(), OSDM22.c_str()));
|
|
|
|
T0.push_back(StringFromFormat("5: Copy EFB: %s\n", OSDM3.c_str()));
|
|
|
|
T0.push_back(StringFromFormat("6: Fog: %s\n", g_ActiveConfig.bDisableFog ? "Disabled" : "Enabled"));
|
|
|
|
T0.push_back(StringFromFormat("7: Material Lighting: %s\n", g_ActiveConfig.bDisableLighting ? "Disabled" : "Enabled"));
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// The latest changed setting in yellow
|
|
|
|
T1 += (OSDChoice == -1) ? T0.at(0) : "\n";
|
|
|
|
T1 += (OSDChoice == -2) ? T0.at(1) : "\n";
|
|
|
|
T1 += (OSDChoice == -3) ? T0.at(2) : "\n";
|
|
|
|
T1 += (OSDChoice == -4) ? T0.at(3) : "\n";
|
|
|
|
T1 += (OSDChoice == -5) ? T0.at(4) : "\n";
|
2009-08-22 13:22:35 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// The other settings in cyan
|
|
|
|
T2 += (OSDChoice != -1) ? T0.at(0) : "\n";
|
|
|
|
T2 += (OSDChoice != -2) ? T0.at(1) : "\n";
|
|
|
|
T2 += (OSDChoice != -3) ? T0.at(2) : "\n";
|
|
|
|
T2 += (OSDChoice != -4) ? T0.at(3) : "\n";
|
|
|
|
T2 += (OSDChoice != -5) ? T0.at(4) : "\n";
|
2009-07-16 22:45:18 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// 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-07-16 09:06:16 +00:00
|
|
|
}
|
2009-07-15 00:51:24 +00:00
|
|
|
}
|
2010-09-28 02:15:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::RenderText(const char *text, int left, int top, u32 color)
|
|
|
|
{
|
|
|
|
int nBackbufferWidth = (int)OpenGL_GetBackbufferWidth();
|
|
|
|
int nBackbufferHeight = (int)OpenGL_GetBackbufferHeight();
|
|
|
|
glColor4f(((color>>16) & 0xff)/255.0f, ((color>> 8) & 0xff)/255.0f,
|
|
|
|
((color>> 0) & 0xff)/255.0f, ((color>>24) & 0xFF)/255.0f);
|
|
|
|
s_pfont->printMultilineText(text,
|
|
|
|
left * 2.0f / (float)nBackbufferWidth - 1,
|
|
|
|
1 - top * 2.0f / (float)nBackbufferHeight,
|
|
|
|
0, nBackbufferWidth, nBackbufferHeight);
|
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
}
|
|
|
|
|
|
|
|
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
|
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
TargetRectangle result;
|
|
|
|
int Xstride = (s_Fulltarget_width - s_target_width) / 2;
|
|
|
|
int Ystride = (s_Fulltarget_height - s_target_height) / 2;
|
|
|
|
result.left = (int)(rc.left * EFBxScale) + Xstride;
|
|
|
|
result.top = (int)((EFB_HEIGHT - rc.top) * EFByScale) + Ystride;
|
|
|
|
result.right = (int)(rc.right * EFBxScale) - (Xstride * 2);
|
|
|
|
result.bottom = (int)((EFB_HEIGHT - rc.bottom) * EFByScale) - (Ystride * 2);
|
|
|
|
return result;
|
2010-09-28 02:15:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc)
|
|
|
|
{
|
|
|
|
if (!fbWidth || !fbHeight)
|
|
|
|
return;
|
|
|
|
s_skipSwap = g_bSkipCurrentFrame;
|
|
|
|
VideoFifo_CheckEFBAccess();
|
|
|
|
VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight);
|
|
|
|
XFBWrited = true;
|
|
|
|
// XXX: Without the VI, how would we know what kind of field this is? So
|
|
|
|
// just use progressive.
|
|
|
|
if (g_ActiveConfig.bUseXFB)
|
|
|
|
{
|
|
|
|
g_framebufferManager.CopyToXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Renderer::Swap(xfbAddr, FIELD_PROGRESSIVE, fbWidth, fbHeight,sourceRc);
|
|
|
|
Common::AtomicStoreRelease(s_swapRequested, FALSE);
|
|
|
|
}
|
2009-07-15 00:51:24 +00:00
|
|
|
}
|
|
|
|
|
2009-06-07 12:06:15 +00:00
|
|
|
// Function: This function handles the OpenGL glScissor() function
|
2009-09-08 16:07:13 +00:00
|
|
|
// ----------------------------
|
2009-02-23 07:23:34 +00:00
|
|
|
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
|
|
|
|
// case 0x52 > SetScissorRect()
|
2009-09-08 16:07:13 +00:00
|
|
|
// ----------------------------
|
2009-06-22 09:31:30 +00:00
|
|
|
// bpmem.scissorTL.x, y = 342x342
|
|
|
|
// bpmem.scissorBR.x, y = 981x821
|
2009-02-23 07:23:34 +00:00
|
|
|
// Renderer::GetTargetHeight() = the fixed ini file setting
|
|
|
|
// donkopunchstania - it appears scissorBR is the bottom right pixel inside the scissor box
|
|
|
|
// therefore the width and height are (scissorBR + 1) - scissorTL
|
|
|
|
bool Renderer::SetScissorRect()
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
int xoff = bpmem.scissorOffset.x * 2 - 342;
|
|
|
|
int yoff = bpmem.scissorOffset.y * 2 - 342;
|
2010-09-30 15:24:34 +00:00
|
|
|
float rc_left = (float)bpmem.scissorTL.x - xoff - 342; // left = 0
|
2009-06-22 09:31:30 +00:00
|
|
|
float rc_top = (float)bpmem.scissorTL.y - yoff - 342; // right = 0
|
|
|
|
float rc_right = (float)bpmem.scissorBR.x - xoff - 341; // right = 640
|
|
|
|
float rc_bottom = (float)bpmem.scissorBR.y - yoff - 341; // bottom = 480
|
2010-09-30 15:24:34 +00:00
|
|
|
|
|
|
|
if (rc_left < 0) rc_left = 0;
|
|
|
|
if (rc_right > EFB_WIDTH) rc_right = EFB_WIDTH;
|
|
|
|
if (rc_top < 0) rc_top = 0;
|
2010-06-05 01:38:22 +00:00
|
|
|
if (rc_bottom > EFB_HEIGHT) rc_bottom = EFB_HEIGHT;
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
if (rc_left > rc_right)
|
2009-11-23 14:08:08 +00:00
|
|
|
{
|
|
|
|
int temp = rc_right;
|
|
|
|
rc_right = rc_left;
|
|
|
|
rc_left = temp;
|
|
|
|
}
|
2010-09-30 15:24:34 +00:00
|
|
|
if (rc_top > rc_bottom)
|
2009-11-23 14:08:08 +00:00
|
|
|
{
|
|
|
|
int temp = rc_bottom;
|
|
|
|
rc_bottom = rc_top;
|
|
|
|
rc_top = temp;
|
|
|
|
}
|
2009-02-23 07:23:34 +00:00
|
|
|
|
|
|
|
// Check that the coordinates are good
|
2010-06-15 21:19:09 +00:00
|
|
|
if (rc_right != rc_left && rc_bottom != rc_top)
|
2009-02-23 07:23:34 +00:00
|
|
|
{
|
2010-05-19 03:15:36 +00:00
|
|
|
glScissor(
|
2010-10-01 11:03:37 +00:00
|
|
|
(int)(rc_left * EFBxScale), // x = 0 for example
|
|
|
|
(int)((EFB_HEIGHT - rc_bottom) * EFByScale), // y = 0 for example
|
|
|
|
(int)((rc_right - rc_left)* EFBxScale), // width = 640 for example
|
|
|
|
(int)((rc_bottom - rc_top) * EFByScale) // height = 480 for example
|
2010-08-10 01:08:22 +00:00
|
|
|
);
|
2010-06-05 01:38:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
2010-05-19 03:15:36 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
glScissor(
|
|
|
|
0,
|
|
|
|
0,
|
2010-08-10 01:08:22 +00:00
|
|
|
Renderer::GetTargetWidth(),
|
2010-06-05 01:38:22 +00:00
|
|
|
Renderer::GetTargetHeight()
|
|
|
|
);
|
2010-05-19 03:15:36 +00:00
|
|
|
}
|
2010-06-05 01:38:22 +00:00
|
|
|
return false;
|
2009-02-23 07:23:34 +00:00
|
|
|
}
|
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetColorMask()
|
|
|
|
{
|
|
|
|
GLenum ColorMask = (bpmem.blendmode.colorupdate) ? GL_TRUE : GL_FALSE;
|
|
|
|
GLenum AlphaMask = (bpmem.blendmode.alphaupdate) ? GL_TRUE : GL_FALSE;
|
|
|
|
glColorMask(ColorMask, ColorMask, ColorMask, AlphaMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 Renderer::AccessEFB(EFBAccessType type, int x, int y)
|
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
if (!g_ActiveConfig.bEFBAccessEnable)
|
2010-09-28 02:15:02 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
TargetRectangle targetPixelRc = ConvertEFBRectangle(efbPixelRc);
|
2010-09-28 02:15:02 +00:00
|
|
|
|
|
|
|
// TODO (FIX) : currently, AA path is broken/offset and doesn't return the correct pixel
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case PEEK_Z:
|
|
|
|
{
|
|
|
|
if (s_MSAASamples > 1)
|
|
|
|
{
|
|
|
|
// Resolve our rectangle.
|
|
|
|
g_framebufferManager.GetEFBDepthTexture(efbPixelRc);
|
|
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, g_framebufferManager.GetResolvedFramebuffer());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sample from the center of the target region.
|
|
|
|
int srcX = (targetPixelRc.left + targetPixelRc.right) / 2;
|
|
|
|
int srcY = (targetPixelRc.top + targetPixelRc.bottom) / 2;
|
|
|
|
|
|
|
|
u32 z = 0;
|
|
|
|
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).
|
|
|
|
// TODO: in RE0 this value is often off by one, which causes lighting to disappear
|
|
|
|
return z >> 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
case POKE_Z:
|
|
|
|
// TODO: Implement
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PEEK_COLOR: // GXPeekARGB
|
|
|
|
{
|
|
|
|
// Although it may sound strange, this really is A8R8G8B8 and not RGBA or 24-bit...
|
|
|
|
|
|
|
|
// Tested in Killer 7, the first 8bits represent the alpha value which is used to
|
|
|
|
// determine if we're aiming at an enemy (0x80 / 0x88) or not (0x70)
|
|
|
|
// Wind Waker is also using it for the pictograph to determine the color of each pixel
|
|
|
|
|
|
|
|
if (s_MSAASamples > 1)
|
|
|
|
{
|
|
|
|
// Resolve our rectangle.
|
|
|
|
g_framebufferManager.GetEFBColorTexture(efbPixelRc);
|
|
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, g_framebufferManager.GetResolvedFramebuffer());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sample from the center of the target region.
|
|
|
|
int srcX = (targetPixelRc.left + targetPixelRc.right) / 2;
|
|
|
|
int srcY = (targetPixelRc.top + targetPixelRc.bottom) / 2;
|
|
|
|
|
|
|
|
// Read back pixel in BGRA format, then byteswap to get GameCube's ARGB Format.
|
|
|
|
u32 color = 0;
|
|
|
|
glReadPixels(srcX, srcY, 1, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &color);
|
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
// TODO: Implement POKE_Z and POKE_COLOR
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called from VertexShaderManager
|
|
|
|
void UpdateViewport()
|
|
|
|
{
|
|
|
|
// reversed gxsetviewport(xorig, yorig, width, height, nearz, farz)
|
|
|
|
// [0] = width/2
|
|
|
|
// [1] = height/2
|
|
|
|
// [2] = 16777215 * (farz - nearz)
|
|
|
|
// [3] = xorig + width/2 + 342
|
|
|
|
// [4] = yorig + height/2 + 342
|
|
|
|
// [5] = 16777215 * farz
|
2010-09-30 15:24:34 +00:00
|
|
|
|
|
|
|
int scissorXOff = bpmem.scissorOffset.x * 2;
|
|
|
|
int scissorYOff = bpmem.scissorOffset.y * 2;
|
|
|
|
|
2010-10-01 12:38:31 +00:00
|
|
|
// int Xstride = (s_Fulltarget_width - s_target_width) / 2;
|
|
|
|
// int Ystride = (s_Fulltarget_height - s_target_height) / 2;
|
2010-09-28 02:15:02 +00:00
|
|
|
|
|
|
|
// Stretch picture with increased internal resolution
|
2010-09-30 15:24:34 +00:00
|
|
|
int X = (int)ceil((xfregs.rawViewport[3] - xfregs.rawViewport[0] - float(scissorXOff)) * EFBxScale);
|
|
|
|
int Y = (int)ceil((float(EFB_HEIGHT) - xfregs.rawViewport[4] + xfregs.rawViewport[1] + float(scissorYOff)) * EFByScale);
|
|
|
|
int Width = (int)ceil(2.0f * xfregs.rawViewport[0] * EFBxScale);
|
|
|
|
int Height = (int)ceil(-2.0f * xfregs.rawViewport[1] * EFByScale);
|
2010-09-28 02:15:02 +00:00
|
|
|
double GLNear = (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777216.0f;
|
|
|
|
double GLFar = xfregs.rawViewport[5] / 16777216.0f;
|
2010-09-30 15:24:34 +00:00
|
|
|
if (Width < 0)
|
2010-09-28 02:15:02 +00:00
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
X += Width;
|
|
|
|
Width*=-1;
|
2010-09-28 02:15:02 +00:00
|
|
|
}
|
2010-09-30 15:24:34 +00:00
|
|
|
if (Height < 0)
|
2010-09-28 02:15:02 +00:00
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
Y += Height;
|
|
|
|
Height *= -1;
|
2010-09-28 02:15:02 +00:00
|
|
|
}
|
|
|
|
// Update the view port
|
2010-09-30 15:24:34 +00:00
|
|
|
glViewport(X, Y, Width, Height);
|
2010-09-28 02:15:02 +00:00
|
|
|
glDepthRange(GLNear, GLFar);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z)
|
2009-07-15 00:51:24 +00:00
|
|
|
{
|
|
|
|
// Update the view port for clearing the picture
|
2010-09-30 15:24:34 +00:00
|
|
|
TargetRectangle targetRc = ConvertEFBRectangle(rc);
|
2010-06-19 21:12:09 +00:00
|
|
|
glViewport(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight());
|
2010-09-30 15:24:34 +00:00
|
|
|
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
// Always set the scissor in case it was set by the game and has not been reset
|
2010-09-30 15:24:34 +00:00
|
|
|
glScissor(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight());
|
2009-07-15 00:51:24 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
VertexShaderManager::SetViewportChanged();
|
2009-07-15 00:51:24 +00:00
|
|
|
|
|
|
|
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,
|
2010-06-19 21:12:09 +00:00
|
|
|
((color >> 24) & 0xFF) / 255.0f
|
2009-07-15 00:51:24 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if (zEnable)
|
|
|
|
{
|
|
|
|
bits |= GL_DEPTH_BUFFER_BIT;
|
|
|
|
glClearDepth((z & 0xFFFFFF) / float(0xFFFFFF));
|
|
|
|
}
|
|
|
|
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
|
|
|
glClear(bits);
|
2010-05-19 03:15:36 +00:00
|
|
|
SetScissorRect();
|
2009-07-15 00:51:24 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetBlendMode(bool forceUpdate)
|
2009-06-26 08:57:53 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
// blend mode bit mask
|
|
|
|
// 0 - blend enable
|
|
|
|
// 2 - reverse subtract enable (else add)
|
|
|
|
// 3-5 - srcRGB function
|
|
|
|
// 6-8 - dstRGB function
|
|
|
|
|
|
|
|
u32 newval = bpmem.blendmode.subtract << 2;
|
|
|
|
|
|
|
|
if (bpmem.blendmode.subtract)
|
|
|
|
newval |= 0x0049; // enable blending src 1 dst 1
|
|
|
|
else if (bpmem.blendmode.blendenable)
|
2010-07-02 17:09:53 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
newval |= 1; // enable blending
|
|
|
|
newval |= bpmem.blendmode.srcfactor << 3;
|
|
|
|
newval |= bpmem.blendmode.dstfactor << 6;
|
2009-08-31 17:47:17 +00:00
|
|
|
}
|
2010-09-28 02:15:02 +00:00
|
|
|
|
|
|
|
u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode;
|
|
|
|
|
|
|
|
if (changes & 1)
|
|
|
|
// blend enable change
|
|
|
|
(newval & 1) ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
|
|
|
|
|
|
|
if (changes & 4)
|
|
|
|
// subtract enable change
|
|
|
|
glBlendEquation(newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD);
|
|
|
|
|
|
|
|
if (changes & 0x1F8)
|
|
|
|
// blend RGB change
|
|
|
|
glBlendFunc(glSrcFactors[(newval >> 3) & 7], glDestFactors[(newval >> 6) & 7]);
|
|
|
|
|
|
|
|
s_blendMode = newval;
|
2009-06-26 08:57:53 +00:00
|
|
|
}
|
|
|
|
|
2009-06-29 07:30:48 +00:00
|
|
|
// This function has the final picture. We adjust the aspect ratio here.
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,const EFBRectangle& rc)
|
2009-02-28 16:33:59 +00:00
|
|
|
{
|
2010-05-14 21:47:16 +00:00
|
|
|
if (g_bSkipCurrentFrame || (!XFBWrited && !g_ActiveConfig.bUseRealXFB) || !fbWidth || !fbHeight)
|
2010-01-28 14:37:03 +00:00
|
|
|
{
|
|
|
|
g_VideoInitialize.pCopiedToXFB(false);
|
2009-08-08 01:39:56 +00:00
|
|
|
return;
|
2010-06-05 01:38:22 +00:00
|
|
|
}
|
2010-09-28 02:15:02 +00:00
|
|
|
// this function is called after the XFB field is changed, not after
|
|
|
|
// EFB is copied to XFB. In this way, flickering is reduced in games
|
|
|
|
// and seems to also give more FPS in ZTP
|
|
|
|
|
2010-04-14 13:57:16 +00:00
|
|
|
if (field == FIELD_LOWER) xfbAddr -= fbWidth * 2;
|
2010-03-10 06:45:13 +00:00
|
|
|
u32 xfbCount = 0;
|
|
|
|
const XFBSource** xfbSourceList = g_framebufferManager.GetXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount);
|
2010-07-02 17:09:53 +00:00
|
|
|
if ((!xfbSourceList || xfbCount == 0) && g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB)
|
2009-06-29 07:30:48 +00:00
|
|
|
{
|
fixed fps limiting when using using virtual xfb, now fps = vps, in fact now real xfb is as fast as no using xfb, i'm thinking now that the correct thing is leave it enabled as default, and even remove the option.
the problem is one strange behavior i found, in opengl when xfb is enable, frame limit causes the frame rate to be limited exact half the correct speed, so if you choose auto and the game uses 30 fps you get 15 fps
so in opengl, you have to limit to the exact double of the game speed, 100 to pal games and 120 to ntsc.
in d3d this not happened every time, it just happen when you change some time consuming setting like changing the ssaa or resizing the window, in that case you have to disable and re enable frame limit to get the correct fps
to all the devs please if you can help me debug this, will give you a lot of thanks as i'm short in time to debug this error and is driving me crazy not to find the source of the problem.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5249 8ced0084-cf51-0410-be5f-012b33b47a6e
2010-03-28 23:51:32 +00:00
|
|
|
g_VideoInitialize.pCopiedToXFB(false);
|
2009-06-29 07:30:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
DVSTARTPROFILE();
|
|
|
|
ResetAPIState();
|
2010-09-30 15:24:34 +00:00
|
|
|
TargetRectangle dst_rect;
|
|
|
|
ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, true, &dst_rect);
|
2009-02-23 07:23:34 +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
|
2009-02-28 19:02:37 +00:00
|
|
|
|
2009-06-26 08:57:53 +00:00
|
|
|
// Disable all other stages
|
|
|
|
for (int i = 1; i < 8; ++i)
|
2010-10-19 22:24:27 +00:00
|
|
|
OGL::TextureCache::DisableStage(i);
|
2009-03-08 19:19:51 +00:00
|
|
|
|
2009-06-26 08:57:53 +00:00
|
|
|
// Update GLViewPort
|
2010-09-30 15:24:34 +00:00
|
|
|
glViewport(dst_rect.left, dst_rect.bottom, dst_rect.GetWidth(), dst_rect.GetHeight());
|
2009-03-08 19:19:51 +00:00
|
|
|
|
2009-06-26 08:57:53 +00:00
|
|
|
GL_REPORT_ERRORD();
|
2009-03-08 19:19:51 +00:00
|
|
|
|
2009-06-26 08:57:53 +00:00
|
|
|
// Copy the framebuffer to screen.
|
|
|
|
|
|
|
|
// Texture map s_xfbTexture onto the main buffer
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glEnable(GL_TEXTURE_RECTANGLE_ARB);
|
|
|
|
// 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);
|
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
// We must call ApplyShader here even if no post proc is selected - it takes
|
2009-06-26 08:57:53 +00:00
|
|
|
// care of disabling it in that case. It returns false in case of no post processing.
|
2010-03-10 06:45:13 +00:00
|
|
|
bool applyShader = PostProcessing::ApplyShader();
|
|
|
|
|
2010-04-12 01:33:10 +00:00
|
|
|
const XFBSource* xfbSource = NULL;
|
2010-03-10 06:45:13 +00:00
|
|
|
|
2010-07-02 17:09:53 +00:00
|
|
|
if(g_ActiveConfig.bUseXFB)
|
2009-06-26 08:57:53 +00:00
|
|
|
{
|
2010-07-02 17:09:53 +00:00
|
|
|
// draw each xfb source
|
2010-07-12 19:30:25 +00:00
|
|
|
// Render to the real buffer now.
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer
|
|
|
|
|
2010-07-02 17:09:53 +00:00
|
|
|
for (u32 i = 0; i < xfbCount; ++i)
|
2010-03-10 06:45:13 +00:00
|
|
|
{
|
2010-07-02 17:09:53 +00:00
|
|
|
xfbSource = xfbSourceList[i];
|
2010-03-10 06:45:13 +00:00
|
|
|
|
2010-07-02 17:09:53 +00:00
|
|
|
TargetRectangle sourceRc;
|
2010-03-10 06:45:13 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
sourceRc = xfbSource->sourceRc;
|
2010-03-10 06:45:13 +00:00
|
|
|
|
2010-07-02 17:09:53 +00:00
|
|
|
MathUtil::Rectangle<float> drawRc;
|
2010-03-10 06:45:13 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
if (!g_ActiveConfig.bUseRealXFB)
|
2010-03-10 06:45:13 +00:00
|
|
|
{
|
2010-07-02 17:09:53 +00:00
|
|
|
// use virtual xfb with offset
|
|
|
|
int xfbHeight = xfbSource->srcHeight;
|
|
|
|
int xfbWidth = xfbSource->srcWidth;
|
|
|
|
int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbWidth * 2);
|
|
|
|
|
|
|
|
drawRc.top = 1.0f - (2.0f * (hOffset) / (float)fbHeight);
|
|
|
|
drawRc.bottom = 1.0f - (2.0f * (hOffset + xfbHeight) / (float)fbHeight);
|
|
|
|
drawRc.left = -(xfbWidth / (float)fbWidth);
|
|
|
|
drawRc.right = (xfbWidth / (float)fbWidth);
|
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
// The following code disables auto stretch. Kept for reference.
|
|
|
|
// scale draw area for a 1 to 1 pixel mapping with the draw target
|
|
|
|
//float vScale = (float)fbHeight / (float)dst_rect.GetHeight();
|
|
|
|
//float hScale = (float)fbWidth / (float)dst_rect.GetWidth();
|
|
|
|
//drawRc.top *= vScale;
|
|
|
|
//drawRc.bottom *= vScale;
|
|
|
|
//drawRc.left *= hScale;
|
|
|
|
//drawRc.right *= hScale;
|
2010-03-10 06:45:13 +00:00
|
|
|
}
|
2010-07-02 17:09:53 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
drawRc.top = 1;
|
|
|
|
drawRc.bottom = -1;
|
|
|
|
drawRc.left = -1;
|
|
|
|
drawRc.right = 1;
|
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-07-02 17:09:53 +00:00
|
|
|
// Tell the OSD Menu about the current internal resolution
|
|
|
|
OSDInternalW = xfbSource->sourceRc.GetWidth(); OSDInternalH = xfbSource->sourceRc.GetHeight();
|
2010-03-10 06:45:13 +00:00
|
|
|
|
2010-07-02 17:09:53 +00:00
|
|
|
// Texture map xfbSource->texture onto the main buffer
|
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfbSource->texture);
|
2010-03-10 06:45:13 +00:00
|
|
|
|
2010-08-10 01:08:22 +00:00
|
|
|
// 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.
|
2010-07-02 17:09:53 +00:00
|
|
|
if (applyShader)
|
|
|
|
{
|
|
|
|
glBegin(GL_QUADS);
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(sourceRc.left, sourceRc.bottom);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE1, 0, 0);
|
|
|
|
glVertex2f(drawRc.left, drawRc.bottom);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(sourceRc.left, sourceRc.top);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE1, 0, 1);
|
|
|
|
glVertex2f(drawRc.left, drawRc.top);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(sourceRc.right, sourceRc.top);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE1, 1, 1);
|
|
|
|
glVertex2f(drawRc.right, drawRc.top);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(sourceRc.right, sourceRc.bottom);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE1, 1, 0);
|
|
|
|
glVertex2f(drawRc.right, drawRc.bottom);
|
2010-07-02 17:09:53 +00:00
|
|
|
glEnd();
|
|
|
|
PixelShaderCache::DisableShader();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glBegin(GL_QUADS);
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(sourceRc.left, sourceRc.bottom);
|
|
|
|
glVertex2f(drawRc.left, drawRc.bottom);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(sourceRc.left, sourceRc.top);
|
|
|
|
glVertex2f(drawRc.left, drawRc.top);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(sourceRc.right, sourceRc.top);
|
|
|
|
glVertex2f(drawRc.right, drawRc.top);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(sourceRc.right, sourceRc.bottom);
|
|
|
|
glVertex2f(drawRc.right, drawRc.bottom);
|
2010-07-02 17:09:53 +00:00
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
TargetRectangle targetRc = ConvertEFBRectangle(rc);
|
2010-09-28 02:15:02 +00:00
|
|
|
GLuint read_texture = g_framebufferManager.ResolveAndGetRenderTarget(rc);
|
2010-07-12 19:30:25 +00:00
|
|
|
// Render to the real buffer now.
|
2010-07-07 22:09:52 +00:00
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer
|
2010-07-02 17:09:53 +00:00
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture);
|
2010-03-10 06:45:13 +00:00
|
|
|
if (applyShader)
|
|
|
|
{
|
|
|
|
glBegin(GL_QUADS);
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(targetRc.left, targetRc.bottom);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE1, 0, 0);
|
|
|
|
glVertex2f(-1, -1);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(targetRc.left, targetRc.top);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE1, 0, 1);
|
|
|
|
glVertex2f(-1, 1);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(targetRc.right, targetRc.top);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE1, 1, 1);
|
|
|
|
glVertex2f( 1, 1);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(targetRc.right, targetRc.bottom);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE1, 1, 0);
|
|
|
|
glVertex2f( 1, -1);
|
2010-03-10 06:45:13 +00:00
|
|
|
glEnd();
|
2010-06-05 01:38:22 +00:00
|
|
|
PixelShaderCache::DisableShader();
|
2010-03-10 06:45:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glBegin(GL_QUADS);
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(targetRc.left, targetRc.bottom);
|
|
|
|
glVertex2f(-1, -1);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(targetRc.left, targetRc.top);
|
|
|
|
glVertex2f(-1, 1);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(targetRc.right, targetRc.top);
|
|
|
|
glVertex2f( 1, 1);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glTexCoord2f(targetRc.right, targetRc.bottom);
|
|
|
|
glVertex2f( 1, -1);
|
2010-03-10 06:45:13 +00:00
|
|
|
glEnd();
|
|
|
|
}
|
2009-06-26 08:57:53 +00:00
|
|
|
}
|
2010-09-30 15:24:34 +00:00
|
|
|
|
2009-06-26 08:57:53 +00:00
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
|
2010-10-19 22:24:27 +00:00
|
|
|
OGL::TextureCache::DisableStage(0);
|
2010-09-30 15:24:34 +00:00
|
|
|
|
2009-03-22 13:25:05 +00:00
|
|
|
// Wireframe
|
2009-09-13 08:21:35 +00:00
|
|
|
if (g_ActiveConfig.bWireFrame)
|
2009-03-22 13:25:05 +00:00
|
|
|
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-08 00:44:48 +00:00
|
|
|
s_criticalScreenshot.Enter();
|
2009-06-07 11:51:53 +00:00
|
|
|
// Save screenshot
|
2010-09-30 15:24:34 +00:00
|
|
|
SaveRenderTarget(s_sScreenshotName.c_str(), dst_rect);
|
2009-06-07 11:51:53 +00:00
|
|
|
// Reset settings
|
|
|
|
s_sScreenshotName = "";
|
|
|
|
s_bScreenshot = false;
|
2010-08-10 01:08:22 +00:00
|
|
|
s_criticalScreenshot.Leave();
|
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-09-13 08:21:35 +00:00
|
|
|
if (g_ActiveConfig.bDumpFrames)
|
2009-06-08 10:16:08 +00:00
|
|
|
{
|
2009-03-28 21:07:16 +00:00
|
|
|
s_criticalScreenshot.Enter();
|
2010-09-30 15:24:34 +00:00
|
|
|
int w = dst_rect.GetWidth();
|
|
|
|
int h = dst_rect.GetHeight();
|
2009-03-28 21:07:16 +00:00
|
|
|
u8 *data = (u8 *) malloc(3 * w * h);
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
2010-09-30 15:24:34 +00:00
|
|
|
glReadPixels(dst_rect.left, dst_rect.bottom, w, h, GL_BGR, GL_UNSIGNED_BYTE, data);
|
2010-08-03 10:48:49 +00:00
|
|
|
if (GL_REPORT_ERROR() == GL_NO_ERROR && w > 0 && h > 0)
|
2009-04-03 14:35:49 +00:00
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
if (!s_bLastFrameDumped)
|
2009-04-03 14:35:49 +00:00
|
|
|
{
|
2009-09-06 19:30:24 +00:00
|
|
|
s_bAVIDumping = AVIDump::Start(EmuWindow::GetParentWnd(), w, h);
|
2010-08-10 01:08:22 +00:00
|
|
|
if (!s_bAVIDumping)
|
2009-06-08 10:16:08 +00:00
|
|
|
OSD::AddMessage("AVIDump Start failed", 2000);
|
2010-06-05 01:38:22 +00:00
|
|
|
else
|
2009-04-03 14:35:49 +00:00
|
|
|
{
|
2009-06-08 10:16:08 +00:00
|
|
|
OSD::AddMessage(StringFromFormat(
|
2010-08-10 01:08:22 +00:00
|
|
|
"Dumping Frames to \"%sframedump0.avi\" (%dx%d RGB24)",
|
|
|
|
File::GetUserPath(D_DUMPFRAMES_IDX), w, h).c_str(), 2000);
|
2009-03-28 21:07:16 +00:00
|
|
|
}
|
|
|
|
}
|
2010-06-05 01:38:22 +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");
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-03-28 21:07:16 +00:00
|
|
|
free(data);
|
2010-08-10 01:08:22 +00:00
|
|
|
s_criticalScreenshot.Leave();
|
2010-06-05 01:38:22 +00:00
|
|
|
}
|
|
|
|
else
|
2009-04-03 14:35:49 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
if (s_bLastFrameDumped && s_bAVIDumping)
|
2009-04-03 14:35:49 +00:00
|
|
|
{
|
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
|
2010-08-10 01:08:22 +00:00
|
|
|
if (g_ActiveConfig.bDumpFrames)
|
|
|
|
{
|
2009-03-28 21:07:16 +00:00
|
|
|
s_criticalScreenshot.Enter();
|
|
|
|
char movie_file_name[255];
|
2010-09-30 15:24:34 +00:00
|
|
|
int w = dst_rect.GetWidth();
|
|
|
|
int h = dst_rect.GetHeight();
|
2009-03-28 21:07:16 +00:00
|
|
|
u8 *data = (u8 *) malloc(3 * w * h);
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
2010-09-30 15:24:34 +00:00
|
|
|
glReadPixels(dst_rect.left, dst_rect.bottom, w, h, GL_BGR, GL_UNSIGNED_BYTE, data);
|
2010-08-10 01:08:22 +00:00
|
|
|
if (GL_REPORT_ERROR() == GL_NO_ERROR)
|
|
|
|
{
|
|
|
|
if (!s_bLastFrameDumped)
|
|
|
|
{
|
2010-02-02 21:56:29 +00:00
|
|
|
sprintf(movie_file_name, "%sframedump.raw", File::GetUserPath(D_DUMPFRAMES_IDX));
|
2009-03-28 21:07:16 +00:00
|
|
|
f_pFrameDump = fopen(movie_file_name, "wb");
|
2010-08-10 01:08:22 +00:00
|
|
|
if (f_pFrameDump == NULL)
|
|
|
|
OSD::AddMessage("Error opening framedump.raw for writing.", 2000);
|
|
|
|
else
|
|
|
|
{
|
2009-03-28 21:07:16 +00:00
|
|
|
char msg [255];
|
|
|
|
sprintf(msg, "Dumping Frames to \"%s\" (%dx%d RGB24)", movie_file_name, w, h);
|
|
|
|
OSD::AddMessage(msg, 2000);
|
|
|
|
}
|
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
if (f_pFrameDump != NULL)
|
|
|
|
{
|
2009-03-28 21:07:16 +00:00
|
|
|
FlipImageData(data, w, h);
|
|
|
|
fwrite(data, w * 3, h, f_pFrameDump);
|
|
|
|
fflush(f_pFrameDump);
|
|
|
|
}
|
|
|
|
s_bLastFrameDumped = true;
|
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-03-28 21:07:16 +00:00
|
|
|
free(data);
|
|
|
|
s_criticalScreenshot.Leave();
|
2010-08-10 01:08:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (s_bLastFrameDumped && f_pFrameDump != NULL)
|
|
|
|
{
|
2009-03-28 21:07:16 +00:00
|
|
|
fclose(f_pFrameDump);
|
|
|
|
f_pFrameDump = NULL;
|
|
|
|
}
|
|
|
|
s_bLastFrameDumped = false;
|
|
|
|
}
|
|
|
|
#endif
|
2010-09-30 15:24:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Finish up the current frame, print some stats
|
2010-05-19 03:15:36 +00:00
|
|
|
OpenGL_Update(); // just updates the render window position and the backbuffer size
|
|
|
|
bool xfbchanged = false;
|
2010-09-28 02:15:02 +00:00
|
|
|
|
|
|
|
if (s_XFB_width != fbWidth || s_XFB_height != fbHeight)
|
2010-05-19 03:15:36 +00:00
|
|
|
{
|
|
|
|
xfbchanged = true;
|
|
|
|
s_XFB_width = fbWidth;
|
|
|
|
s_XFB_height = fbHeight;
|
2010-09-28 02:15:02 +00:00
|
|
|
if (s_XFB_width < 1) s_XFB_width = MAX_XFB_WIDTH;
|
|
|
|
if (s_XFB_width > MAX_XFB_WIDTH) s_XFB_width = MAX_XFB_WIDTH;
|
|
|
|
if (s_XFB_height < 1) s_XFB_height = MAX_XFB_HEIGHT;
|
|
|
|
if (s_XFB_height > MAX_XFB_HEIGHT) s_XFB_height = MAX_XFB_HEIGHT;
|
2010-05-19 03:15:36 +00:00
|
|
|
}
|
2010-09-28 02:15:02 +00:00
|
|
|
|
2010-05-19 03:15:36 +00:00
|
|
|
bool WindowResized = false;
|
2010-09-30 15:24:34 +00:00
|
|
|
int W = (int)OpenGL_GetBackbufferWidth();
|
|
|
|
int H = (int)OpenGL_GetBackbufferHeight();
|
|
|
|
if (W != s_backbuffer_width || H != s_backbuffer_height || s_LastEFBScale != g_ActiveConfig.iEFBScale)
|
2010-05-19 03:15:36 +00:00
|
|
|
{
|
|
|
|
WindowResized = true;
|
2010-09-28 02:15:02 +00:00
|
|
|
s_backbuffer_width = W;
|
|
|
|
s_backbuffer_height = H;
|
2010-09-30 15:24:34 +00:00
|
|
|
s_LastEFBScale = g_ActiveConfig.iEFBScale;
|
2010-05-19 03:15:36 +00:00
|
|
|
}
|
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
if (xfbchanged || WindowResized)
|
2010-06-05 01:38:22 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, false, &dst_rect);
|
2010-05-19 03:15:36 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
if(g_ActiveConfig.bUseRealXFB)
|
2010-05-19 03:15:36 +00:00
|
|
|
{
|
2010-09-30 15:24:34 +00:00
|
|
|
xScale = 1.0f;
|
|
|
|
yScale = 1.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
xScale = (float)(dst_rect.right - dst_rect.left) / (float)s_XFB_width;
|
|
|
|
yScale = (float)(dst_rect.bottom - dst_rect.top) / (float)s_XFB_height;
|
2010-05-19 03:15:36 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
switch(s_LastEFBScale)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
EFBxScale = xScale;
|
|
|
|
EFByScale = yScale;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
EFBxScale = ceilf(xScale);
|
|
|
|
EFByScale = ceilf(yScale);
|
|
|
|
break;
|
|
|
|
default:
|
2010-10-01 11:03:37 +00:00
|
|
|
EFBxScale = (float)(g_ActiveConfig.iEFBScale - 1);
|
2010-09-30 15:24:34 +00:00
|
|
|
EFByScale = EFBxScale;
|
|
|
|
break;
|
|
|
|
};
|
2010-05-19 03:15:36 +00:00
|
|
|
|
|
|
|
int m_newFrameBufferWidth = EFB_WIDTH * EFBxScale;
|
|
|
|
int m_newFrameBufferHeight = EFB_HEIGHT * EFByScale;
|
2010-09-28 02:15:02 +00:00
|
|
|
if(m_newFrameBufferWidth != s_target_width ||
|
|
|
|
m_newFrameBufferHeight != s_target_height )
|
2010-05-19 03:15:36 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
s_target_width = m_newFrameBufferWidth;
|
|
|
|
s_target_height = m_newFrameBufferHeight;
|
2010-05-19 03:15:36 +00:00
|
|
|
|
2010-09-30 15:24:34 +00:00
|
|
|
s_Fulltarget_width = s_target_width;
|
|
|
|
s_Fulltarget_height = s_target_height;
|
|
|
|
|
2010-05-19 03:15:36 +00:00
|
|
|
g_framebufferManager.Shutdown();
|
2010-09-28 02:15:02 +00:00
|
|
|
g_framebufferManager.Init(s_target_width, s_target_height,
|
|
|
|
s_MSAASamples, s_MSAACoverageSamples);
|
2010-05-19 03:15:36 +00:00
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-23 07:23:34 +00:00
|
|
|
// Place messages on the picture, then copy it to the screen
|
2009-06-07 12:06:15 +00:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Count FPS.
|
2009-09-08 16:07:13 +00:00
|
|
|
// -------------
|
2009-06-07 12:06:15 +00:00
|
|
|
static int fpscount = 0;
|
2010-08-01 14:43:07 +00:00
|
|
|
static unsigned long lasttime = 0;
|
|
|
|
if (Common::Timer::GetTimeMs() - lasttime >= 1000)
|
2010-06-05 01:38:22 +00:00
|
|
|
{
|
|
|
|
lasttime = Common::Timer::GetTimeMs();
|
2010-08-01 14:43:07 +00:00
|
|
|
s_fps = fpscount;
|
2010-06-05 01:38:22 +00:00
|
|
|
fpscount = 0;
|
|
|
|
}
|
2010-09-28 02:15:02 +00:00
|
|
|
if (XFBWrited)
|
|
|
|
++fpscount;
|
2009-06-07 12:06:15 +00:00
|
|
|
// ---------------------------------------------------------------------
|
2010-06-05 01:38:22 +00:00
|
|
|
GL_REPORT_ERRORD();
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-06-07 12:06:15 +00:00
|
|
|
DrawDebugText();
|
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
GL_REPORT_ERRORD();
|
2009-06-13 22:08:01 +00:00
|
|
|
|
2009-09-03 20:37:35 +00:00
|
|
|
// Get the status of the Blend mode
|
|
|
|
GLboolean blend_enabled = glIsEnabled(GL_BLEND);
|
|
|
|
glDisable(GL_BLEND);
|
2009-06-07 12:06:15 +00:00
|
|
|
OSD::DrawMessages();
|
2009-09-03 20:37:35 +00:00
|
|
|
if (blend_enabled)
|
|
|
|
glEnable(GL_BLEND);
|
2010-06-05 01:38:22 +00:00
|
|
|
GL_REPORT_ERRORD();
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-06-07 12:06:15 +00:00
|
|
|
#if defined(DVPROFILE)
|
2010-08-10 01:08:22 +00:00
|
|
|
if (g_bWriteProfile)
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
//g_bWriteProfile = 0;
|
|
|
|
static int framenum = 0;
|
|
|
|
const int UPDATE_FRAMES = 8;
|
2010-08-10 01:08:22 +00:00
|
|
|
if (++framenum >= UPDATE_FRAMES)
|
|
|
|
{
|
2010-06-05 01:38:22 +00:00
|
|
|
DVProfWrite("prof.txt", UPDATE_FRAMES);
|
|
|
|
DVProfClear();
|
|
|
|
framenum = 0;
|
|
|
|
}
|
|
|
|
}
|
2009-06-07 12:06:15 +00:00
|
|
|
#endif
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
// Copy the rendered frame to the real window
|
2009-06-07 12:06:15 +00:00
|
|
|
OpenGL_SwapBuffers();
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-06-13 22:08:01 +00:00
|
|
|
GL_REPORT_ERRORD();
|
2009-08-08 01:39:56 +00:00
|
|
|
|
2009-06-08 20:07:20 +00:00
|
|
|
// Clear framebuffer
|
2010-09-23 02:17:48 +00:00
|
|
|
if(!g_ActiveConfig.bAnaglyphStereo)
|
|
|
|
{
|
|
|
|
glClearColor(0, 0, 0, 0);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
}
|
2009-06-07 12:06:15 +00:00
|
|
|
|
2009-08-08 01:39:56 +00:00
|
|
|
GL_REPORT_ERRORD();
|
2009-06-07 12:06:15 +00:00
|
|
|
|
2010-01-17 17:44:09 +00:00
|
|
|
// Clean out old stuff from caches. It's not worth it to clean out the shader caches.
|
2009-08-09 11:03:58 +00:00
|
|
|
DLCache::ProgressiveCleanup();
|
2010-09-28 02:15:02 +00:00
|
|
|
TextureCache::Cleanup();
|
2009-06-07 12:06:15 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
frameCount++;
|
2009-06-07 12:06:15 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// Begin new frame
|
|
|
|
// Set default viewport and scissor, for the clear to work correctly
|
2010-06-05 01:38:22 +00:00
|
|
|
// New frame
|
|
|
|
stats.ResetFrame();
|
2009-06-07 12:06:15 +00:00
|
|
|
|
|
|
|
// Render to the framebuffer.
|
2009-09-03 20:37:35 +00:00
|
|
|
g_framebufferManager.SetFramebuffer(0);
|
2009-06-07 12:06:15 +00:00
|
|
|
|
|
|
|
GL_REPORT_ERRORD();
|
2009-02-23 07:23:34 +00:00
|
|
|
|
2010-06-05 01:38:22 +00:00
|
|
|
RestoreAPIState();
|
2009-09-13 17:46:33 +00:00
|
|
|
|
|
|
|
GL_REPORT_ERRORD();
|
2010-06-05 01:38:22 +00:00
|
|
|
g_Config.iSaveTargetId = 0;
|
2009-09-13 17:46:33 +00:00
|
|
|
|
2010-02-08 23:23:04 +00:00
|
|
|
bool last_copy_efb_to_Texture = g_ActiveConfig.bCopyEFBToTexture;
|
2009-09-13 17:46:33 +00:00
|
|
|
UpdateActiveConfig();
|
2010-02-08 23:23:04 +00:00
|
|
|
if (last_copy_efb_to_Texture != g_ActiveConfig.bCopyEFBToTexture)
|
2010-09-28 02:15:02 +00:00
|
|
|
TextureCache::ClearRenderTargets();
|
2009-09-13 17:46:33 +00:00
|
|
|
|
|
|
|
// For testing zbuffer targets.
|
2010-06-05 01:38:22 +00:00
|
|
|
// Renderer::SetZBufferRender();
|
2010-08-10 01:08:22 +00:00
|
|
|
// SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_ARB, s_FakeZTarget,
|
|
|
|
// GetTargetWidth(), GetTargetHeight());
|
|
|
|
g_VideoInitialize.pCopiedToXFB(XFBWrited || g_ActiveConfig.bUseRealXFB);
|
2010-03-25 22:01:33 +00:00
|
|
|
XFBWrited = false;
|
2009-09-13 17:46:33 +00:00
|
|
|
}
|
2009-06-07 12:06:15 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
|
|
|
|
void Renderer::ResetAPIState()
|
2009-02-23 07:23:34 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
// Gets us to a reasonably sane state where it's possible to do things like
|
|
|
|
// image copies with textured quads, etc.
|
|
|
|
VertexShaderCache::DisableShader();
|
|
|
|
PixelShaderCache::DisableShader();
|
|
|
|
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-02-23 07:23:34 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::RestoreAPIState()
|
|
|
|
{
|
|
|
|
// Gets us back into a more game-like state.
|
2009-03-17 06:04:57 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
UpdateViewport();
|
2009-03-16 07:54:44 +00:00
|
|
|
|
2010-09-28 02:15:02 +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-03-16 07:54:44 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
SetScissorRect();
|
|
|
|
SetColorMask();
|
|
|
|
SetBlendMode(true);
|
2009-03-16 07:54:44 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
VertexShaderCache::SetCurrentShader(0);
|
|
|
|
PixelShaderCache::SetCurrentShader(0);
|
|
|
|
}
|
2009-03-16 07:54:44 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetGenerationMode()
|
|
|
|
{
|
|
|
|
// none, ccw, cw, ccw
|
|
|
|
if (bpmem.genMode.cullmode > 0)
|
|
|
|
{
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glFrontFace(bpmem.genMode.cullmode == 2 ? GL_CCW : GL_CW);
|
2009-03-16 07:54:44 +00:00
|
|
|
}
|
2010-09-28 02:15:02 +00:00
|
|
|
else
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
}
|
2009-03-16 07:54:44 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetDepthMode()
|
|
|
|
{
|
|
|
|
if (bpmem.zmode.testenable)
|
2009-09-13 08:21:35 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(bpmem.zmode.updateenable ? GL_TRUE : GL_FALSE);
|
|
|
|
glDepthFunc(glCmpFuncs[bpmem.zmode.func]);
|
2009-09-13 08:21:35 +00:00
|
|
|
}
|
2010-09-28 02:15:02 +00:00
|
|
|
else
|
2009-06-08 02:48:38 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
// if the test is disabled write is disabled too
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
}
|
|
|
|
}
|
2009-10-22 20:22:50 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetLogicOpMode()
|
|
|
|
{
|
|
|
|
if (bpmem.blendmode.logicopenable && bpmem.blendmode.logicmode != 3)
|
|
|
|
{
|
|
|
|
glEnable(GL_COLOR_LOGIC_OP);
|
|
|
|
glLogicOp(glLogicOpCodes[bpmem.blendmode.logicmode]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glDisable(GL_COLOR_LOGIC_OP);
|
|
|
|
}
|
|
|
|
}
|
2009-10-22 20:22:50 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetDitherMode()
|
|
|
|
{
|
|
|
|
if (bpmem.blendmode.dither)
|
|
|
|
glEnable(GL_DITHER);
|
|
|
|
else
|
|
|
|
glDisable(GL_DITHER);
|
|
|
|
}
|
2009-10-22 20:22:50 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetLineWidth()
|
|
|
|
{
|
|
|
|
float fratio = xfregs.rawViewport[0] != 0 ?
|
|
|
|
((float)Renderer::GetTargetWidth() / EFB_WIDTH) : 1.0f;
|
|
|
|
if (bpmem.lineptwidth.linesize > 0)
|
|
|
|
// scale by ratio of widths
|
|
|
|
glLineWidth((float)bpmem.lineptwidth.linesize * fratio / 6.0f);
|
|
|
|
if (bpmem.lineptwidth.pointsize > 0)
|
|
|
|
glPointSize((float)bpmem.lineptwidth.pointsize * fratio / 6.0f);
|
|
|
|
}
|
2009-10-22 20:22:50 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetSamplerState(int stage, int texindex)
|
|
|
|
{
|
|
|
|
// TODO
|
2009-02-28 16:33:59 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::SetInterlacingMode()
|
2009-02-23 07:23:34 +00:00
|
|
|
{
|
2010-09-28 02:15:02 +00:00
|
|
|
// TODO
|
2009-02-23 07:23:34 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2010-09-28 02:15:02 +00:00
|
|
|
void Renderer::FlipImageData(u8 *data, int w, int h)
|
|
|
|
{
|
|
|
|
// 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-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;
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-07-29 03:11:35 +00:00
|
|
|
// 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.
|
2010-01-13 21:11:02 +00:00
|
|
|
if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH)
|
2009-07-29 03:11:35 +00:00
|
|
|
{
|
2010-01-13 21:11:02 +00:00
|
|
|
bool use16_9 = g_VideoInitialize.bAutoAspectIs16_9;
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-01-13 21:11:02 +00:00
|
|
|
// Check for force-settings and override.
|
|
|
|
if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_16_9)
|
|
|
|
use16_9 = true;
|
|
|
|
else if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_4_3)
|
|
|
|
use16_9 = false;
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-01-13 21:11:02 +00:00
|
|
|
float Ratio = (FloatW / FloatH) / (!use16_9 ? (4.0f / 3.0f) : (16.0f / 9.0f));
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-07-29 03:11:35 +00:00
|
|
|
// 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;
|
2010-08-10 01:08:22 +00:00
|
|
|
|
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
|
2010-08-10 01:08:22 +00:00
|
|
|
threadStruct->img->SaveFile(wxString::FromAscii(threadStruct->filename.c_str()),
|
2010-09-28 02:15:02 +00:00
|
|
|
wxBITMAP_TYPE_PNG);
|
2009-07-29 03:11:35 +00:00
|
|
|
threadStruct->img->Destroy();
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-07-29 03:11:35 +00:00
|
|
|
// Show success messages
|
2010-08-10 01:08:22 +00:00
|
|
|
OSD::AddMessage(StringFromFormat("Saved %i x %i %s", (int)FloatW, (int)FloatH,
|
2010-09-28 02:15:02 +00:00
|
|
|
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
|
|
|
|
2010-07-09 20:56:16 +00:00
|
|
|
bool Renderer::SaveRenderTarget(const char *filename, TargetRectangle back_rc)
|
2009-02-27 03:56:34 +00:00
|
|
|
{
|
2010-07-09 20:56:16 +00:00
|
|
|
u32 W = back_rc.GetWidth();
|
|
|
|
u32 H = back_rc.GetHeight();
|
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);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2010-07-09 20:56:16 +00:00
|
|
|
glReadPixels(back_rc.left, back_rc.bottom, W, H, GL_RGB, GL_UNSIGNED_BYTE, data);
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-06-06 17:34:54 +00:00
|
|
|
// Show failure message
|
2010-08-03 10:48:49 +00:00
|
|
|
if (GL_REPORT_ERROR() != GL_NO_ERROR)
|
2009-06-06 17:34:54 +00:00
|
|
|
{
|
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;
|
|
|
|
}
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-06-06 13:36:33 +00:00
|
|
|
// Turn image upside down
|
|
|
|
FlipImageData(data, W, H);
|
2010-08-10 01:08:22 +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);
|
2010-08-10 01:08:22 +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-06-07 21:10:02 +00:00
|
|
|
}
|
2010-08-10 01:08:22 +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;
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-07-29 03:11:35 +00:00
|
|
|
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;
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-07-29 03:11:35 +00:00
|
|
|
OSD::AddMessage("Saving Screenshot... ", 2000);
|
2010-08-10 01:08:22 +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
|
2010-08-10 01:08:22 +00:00
|
|
|
|
2009-03-28 21:07:16 +00:00
|
|
|
return result;
|
|
|
|
}
|