dolphin/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp
John Peterson d4f8f0d3ae Added the StretchToFit option to the config menu in the OpenGL plugin. This fixes the blackness in SSBM. I also added a keep aspect ratio option, it will keep your aspect ratio at 4:3, but then SSBM will have the blackness problem again. You find the options under the Enhancements window in the OpenGL configuration.
I also added a wx debugging window for the OpenGL plugin. I connected it to the old console window that was in the plugin. Other than that it doesn't do anything at the moment but it could be useful to show all the current important information and parameter statuses and so on.

Again there's a problem with wx windows collisions.  Show() can't be used because then DLL_PROCESS_DETACH is called immediately after the window is opened, and if we open it with ShowModal() before we have loaded a game the main video window will be blocked. And we can't pass on any variables from a DllDebugger() that is called when Dolphin is started because the dll is reloaded and lose all variables sometime before a game is loaded. So we can't auto open the window that way. So I made the debugging window open as a game is loaded if it is enabled in the ini, the downside is that the ini setting will open the window even if we are not opening Dolphin with the -d flag. However, this will only affect people that have used the debugger at least once so in my opinion this is the most convenient solution. But feel free to come up with a better solution. Preferably some solution to how to use Show() and preventing DLL_PROCESS_DETACH to be called.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@812 8ced0084-cf51-0410-be5f-012b33b47a6e
2008-10-09 18:47:53 +00:00

920 lines
30 KiB
C++

// Copyright (C) 2003-2008 Dolphin Project.
// 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"
#include <list>
#ifdef _WIN32
#include <mmsystem.h>
#endif
#include "GLInit.h"
#include "Profiler.h"
#include "Render.h"
#include "OpcodeDecoding.h"
#include "BPStructs.h"
#include "TextureMngr.h"
#include "rasterfont.h"
#include "VertexShader.h"
#include "PixelShaderManager.h"
#include "VertexLoader.h"
#include "XFB.h"
#include "Debugger/Debugger.h" // for the CDebugger class
#include "Logging/Logging.h" // for Logging()
#ifdef _WIN32
#include "OS/Win32.h"
#else
#endif
struct MESSAGE
{
MESSAGE() {}
MESSAGE(const char* p, u32 dw) { strcpy(str, p); dwTimeStamp = dw; }
char str[255];
u32 dwTimeStamp;
};
CGcontext g_cgcontext;
CGprofile g_cgvProf, g_cgfProf;
extern CDebugger* m_frame; // the debugging class
static int g_MaxTexWidth = 0, g_MaxTexHeight = 0;
static RasterFont* s_pfont = NULL;
static std::list<MESSAGE> s_listMsgs;
static bool s_bFullscreen = false;
static bool s_bOutputCgErrors = true;
static int nZBufferRender = 0; // if > 0, then using zbuffer render
static u32 s_uFramebuffer = 0;
static u32 s_RenderTargets[1] = {0}, s_DepthTarget = 0, s_ZBufferTarget = 0;
static bool s_bATIDrawBuffers = false, s_bHaveStencilBuffer = false;
static Renderer::RenderMode s_RenderMode = Renderer::RM_Normal;
static int s_nCurTarget = 0;
bool g_bBlendLogicOp = false;
void HandleCgError(CGcontext ctx, CGerror err, void* appdata);
bool Renderer::Create2()
{
bool bSuccess = true;
GLenum err = GL_NO_ERROR;
g_cgcontext = cgCreateContext();
cgGetError();
cgSetErrorHandler(HandleCgError, NULL);
// fill the opengl extension map
const char* ptoken = (const char*)glGetString( GL_EXTENSIONS );
if (ptoken == NULL) return false;
__Log("Supported OpenGL Extensions:\n");
__Log(ptoken); // write to the log file
__Log("\n");
if( strstr(ptoken, "GL_EXT_blend_logic_op") != NULL )
g_bBlendLogicOp = true;
if( strstr(ptoken, "ATI_draw_buffers") != NULL && strstr(ptoken, "ARB_draw_buffers") == NULL)
//Checks if it ONLY has the ATI_draw_buffers extension, some have both
s_bATIDrawBuffers = true;
s_bFullscreen = g_Config.bFullscreen;
if (glewInit() != GLEW_OK) {
ERROR_LOG("glewInit() failed!\n");
return false;
}
if (!GLEW_EXT_framebuffer_object) {
ERROR_LOG("*********\nGPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets\nGPU: *********\n");
bSuccess = false;
}
if (!GLEW_EXT_secondary_color) {
ERROR_LOG("*********\nGPU: OGL ERROR: Need GL_EXT_secondary_color\nGPU: *********\n");
bSuccess = false;
}
int numvertexattribs=0;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, (GLint *)&numvertexattribs);
if (numvertexattribs < 11) {
ERROR_LOG("*********\nGPU: OGL ERROR: Number of attributes %d not enough\nGPU: *********\n", numvertexattribs);
bSuccess = false;
}
if (!bSuccess)
return false;
#ifdef _WIN32
if (WGLEW_EXT_swap_control)
wglSwapIntervalEXT(0);
else
ERROR_LOG("no support for SwapInterval (framerate clamped to monitor refresh rate)\n");
#else
#ifdef __linux__
if (glXSwapIntervalSGI)
glXSwapIntervalSGI(0);
else
ERROR_LOG("no support for SwapInterval (framerate clamped to monitor refresh rate)\n");
#else
//TODO
#endif
#endif
// check the max texture width and height
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&g_MaxTexWidth);
g_MaxTexHeight = g_MaxTexWidth;
GL_REPORT_ERROR();
if (err != GL_NO_ERROR) bSuccess = false;
if (glDrawBuffers == NULL && !GLEW_ARB_draw_buffers)
glDrawBuffers = glDrawBuffersARB;
glGenFramebuffersEXT( 1, (GLuint *)&s_uFramebuffer);
if (s_uFramebuffer == 0) {
ERROR_LOG("failed to create the renderbuffer\n");
}
_assert_( glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT );
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, s_uFramebuffer );
// create the framebuffer targets
glGenTextures(ARRAYSIZE(s_RenderTargets), (GLuint *)s_RenderTargets);
for(u32 i = 0; i < ARRAYSIZE(s_RenderTargets); ++i) {
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTargets[i]);
// initialize to default
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, nBackbufferWidth, nBackbufferHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
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);
}
s_nCurTarget = 0;
GL_REPORT_ERROR();
int nMaxMRT = 0;
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, (GLint *)&nMaxMRT);
if( nMaxMRT > 1 ) {
// create zbuffer target
glGenTextures(1, (GLuint *)&s_ZBufferTarget);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, nBackbufferWidth, nBackbufferHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
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);
}
// create the depth buffer
glGenRenderbuffersEXT( 1, (GLuint *)&s_DepthTarget);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_DepthTarget);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, nBackbufferWidth, nBackbufferHeight);
if( glGetError() != GL_NO_ERROR ) {
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, nBackbufferWidth, nBackbufferHeight);
s_bHaveStencilBuffer = false;
}
else s_bHaveStencilBuffer = true;
GL_REPORT_ERROR();
// set as render targets
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, s_RenderTargets[s_nCurTarget], 0 );
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, s_DepthTarget );
GL_REPORT_ERROR();
if( s_ZBufferTarget != 0 ) {
// test to make sure it works
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget, 0);
bool bFailed = glGetError() != GL_NO_ERROR || glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT;
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
if( bFailed ) {
glDeleteTextures(1, (GLuint *)&s_ZBufferTarget);
s_ZBufferTarget = 0;
}
}
if( s_ZBufferTarget == 0 )
ERROR_LOG("disabling ztarget mrt feature (max mrt=%d)\n", nMaxMRT);
//glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, s_DepthTarget );
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
nZBufferRender = 0;
GL_REPORT_ERROR();
if (err != GL_NO_ERROR) bSuccess = false;
s_pfont = new RasterFont();
SetAA(g_Config.iMultisampleMode);
GL_REPORT_ERROR();
// load the effect, find the best profiles (if any)
if (cgGLIsProfileSupported(CG_PROFILE_ARBVP1) != CG_TRUE) {
ERROR_LOG("arbvp1 not supported\n");
return false;
}
if (cgGLIsProfileSupported(CG_PROFILE_ARBFP1) != CG_TRUE) {
ERROR_LOG("arbfp1 not supported\n");
return false;
}
g_cgvProf = cgGLGetLatestProfile(CG_GL_VERTEX);
g_cgfProf = cgGLGetLatestProfile(CG_GL_FRAGMENT);//CG_PROFILE_ARBFP1;
//cgGLSetOptimalOptions(g_cgvProf);
//cgGLSetOptimalOptions(g_cgfProf);
//ERROR_LOG("max buffer sizes: %d %d\n", cgGetProgramBufferMaxSize(g_cgvProf), cgGetProgramBufferMaxSize(g_cgfProf));
int nenvvertparams, nenvfragparams, naddrregisters[2];
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]);
__Log("max program env parameters: vert=%d, frag=%d\n", nenvvertparams, nenvfragparams);
__Log("max program address register parameters: vert=%d, frag=%d\n", naddrregisters[0], naddrregisters[1]);
if( nenvvertparams < 238 )
ERROR_LOG("not enough vertex shader environment constants!!\n");
#ifndef _DEBUG
cgGLSetDebugMode(GL_FALSE);
#endif
if( cgGetError() != CG_NO_ERROR ) {
ERROR_LOG("cg error\n");
return false;
}
//glEnable(GL_POLYGON_OFFSET_FILL);
//glEnable(GL_POLYGON_OFFSET_LINE);
//glPolygonOffset(0, 1);
if (!Initialize())
return false;
XFB_Init();
return glGetError() == GL_NO_ERROR && bSuccess;
}
void Renderer::Shutdown(void)
{
delete s_pfont;
s_pfont = 0;
XFB_Shutdown();
if (g_cgcontext != 0) {
cgDestroyContext(g_cgcontext);
g_cgcontext = 0;
}
if (s_RenderTargets[0]) {
glDeleteTextures(ARRAYSIZE(s_RenderTargets), (GLuint *)s_RenderTargets);
memset(s_RenderTargets, 0, sizeof(s_RenderTargets));
}
if (s_DepthTarget) {
glDeleteRenderbuffersEXT(1, (GLuint *)&s_DepthTarget); s_DepthTarget = 0;
}
if (s_uFramebuffer != 0) {
glDeleteFramebuffersEXT( 1, (GLuint *)&s_uFramebuffer);
s_uFramebuffer = 0;
}
}
bool Renderer::Initialize()
{
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);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // perspective correct interpolation of colors and tex coords
// setup the default vertex declaration
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
glScissor(0,0,nBackbufferWidth,nBackbufferHeight);
glBlendColorEXT(0, 0, 0, 0.5f);
glClearDepth(1.0f);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//Renderer::SetZBufferRender();
// legacy multitexturing: select texture channel only
glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
s_RenderMode = Renderer::RM_Normal;
GLenum err = GL_NO_ERROR;
GL_REPORT_ERROR();
return err == GL_NO_ERROR;
}
void Renderer::AddMessage(const char* pstr, u32 ms)
{
s_listMsgs.push_back(MESSAGE(pstr, timeGetTime() + ms));
}
void Renderer::ProcessMessages()
{
GLboolean wasEnabled = glIsEnabled(GL_BLEND);
if(!wasEnabled) glEnable(GL_BLEND);
if (s_listMsgs.size() > 0) {
int left = 25, top = 15;
list<MESSAGE>::iterator it = s_listMsgs.begin();
while( it != s_listMsgs.end() )
{
int time_left = (int)(it->dwTimeStamp - timeGetTime());
int alpha = 255;
if(time_left<1024)
{
alpha=time_left>>2;
if(time_left<0) alpha=0;
}
alpha<<=24;
RenderText(it->str, left+1, top+1, 0x000000|alpha);
RenderText(it->str, left, top, 0xffff30|alpha);
top += 15;
if (time_left <= 0)
it = s_listMsgs.erase(it);
else ++it;
}
}
if(!wasEnabled) glDisable(GL_BLEND);
}
void Renderer::RenderText(const char* pstr, int left, int top, u32 color)
{
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);
}
void Renderer::SetAA(int aa)
{
}
void Renderer::ReinitView(int nNewWidth, int nNewHeight)
{
int oldscreen = s_bFullscreen;
OpenGL_Shutdown();
int oldwidth = nBackbufferWidth, oldheight = nBackbufferHeight;
if (!OpenGL_Create(g_VideoInitialize, nNewWidth, nNewHeight)) {//nNewWidth&~7, nNewHeight&~7) ) {
ERROR_LOG("Failed to recreate, reverting to old settings\n");
if (!OpenGL_Create(g_VideoInitialize, oldwidth, oldheight)) {
g_VideoInitialize.pSysMessage("Failed to revert, exiting...\n");
// TODO - don't takedown the entire emu
exit(0);
}
}
OpenGL_MakeCurrent();
if (oldscreen && !g_Config.bFullscreen) { // if transitioning from full screen
#ifdef _WIN32
RECT rc;
rc.left = 0; rc.top = 0;
rc.right = nNewWidth; rc.bottom = nNewHeight;
AdjustWindowRect(&rc, EmuWindow::g_winstyle, FALSE);
RECT rcdesktop;
GetWindowRect(GetDesktopWindow(), &rcdesktop);
SetWindowLong( EmuWindow::GetWnd(), GWL_STYLE, EmuWindow::g_winstyle );
SetWindowPos(EmuWindow::GetWnd(), HWND_TOP, ((rcdesktop.right-rcdesktop.left)-(rc.right-rc.left))/2,
((rcdesktop.bottom-rcdesktop.top)-(rc.bottom-rc.top))/2,
rc.right-rc.left, rc.bottom-rc.top, SWP_SHOWWINDOW);
UpdateWindow(EmuWindow::GetWnd());
#else // linux
#endif
}
nBackbufferWidth = nNewWidth > 16 ? nNewWidth : 16;
nBackbufferHeight = nNewHeight > 16 ? nNewHeight : 16;
}
int Renderer::GetTargetWidth()
{
if(g_Config.bStretchToFit)
return 640;
else
return nBackbufferWidth; // return the actual window width
}
int Renderer::GetTargetHeight()
{
if(g_Config.bStretchToFit)
return 480;
else
return nBackbufferHeight; // return the actual window height
}
bool Renderer::CanBlendLogicOp()
{
return g_bBlendLogicOp;
}
void Renderer::SetRenderTarget(u32 targ)
{
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, targ!=0?targ:s_RenderTargets[s_nCurTarget], 0 );
}
void Renderer::SetDepthTarget(u32 targ)
{
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, targ != 0 ? targ : s_DepthTarget );
}
void Renderer::SetFramebuffer(u32 fb)
{
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fb != 0 ? fb : s_uFramebuffer );
}
u32 Renderer::GetRenderTarget()
{
return s_RenderTargets[s_nCurTarget];
}
void Renderer::ResetGLState()
{
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
glDepthMask(GL_FALSE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable( GL_VERTEX_PROGRAM_ARB );
glDisable( GL_FRAGMENT_PROGRAM_ARB );
}
void Renderer::RestoreGLState()
{
glEnable(GL_SCISSOR_TEST);
if (bpmem.genMode.cullmode>0) glEnable(GL_CULL_FACE);
if (bpmem.zmode.testenable) glEnable(GL_DEPTH_TEST);
if (bpmem.blendmode.blendenable) glEnable(GL_BLEND);
if(bpmem.zmode.updateenable) glDepthMask(GL_TRUE);
glEnable( GL_VERTEX_PROGRAM_ARB );
glEnable( GL_FRAGMENT_PROGRAM_ARB );
SetColorMask();
}
bool Renderer::IsUsingATIDrawBuffers()
{
return s_bATIDrawBuffers;
}
bool Renderer::HaveStencilBuffer()
{
return s_bHaveStencilBuffer;
}
void Renderer::SetZBufferRender()
{
nZBufferRender = 10; // give it 10 frames
GLenum s_drawbuffers[2] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
glDrawBuffers(2, s_drawbuffers);
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget, 0);
_assert_(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT);
}
void Renderer::FlushZBufferAlphaToTarget()
{
ResetGLState();
SetRenderTarget(0);
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glViewport(0, 0, GetTargetWidth(), GetTargetHeight());
// texture map s_RenderTargets[s_curtarget] onto the main buffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget);
TextureMngr::EnableTexRECT(0);
// disable all other stages
for(int i = 1; i < 8; ++i) TextureMngr::DisableStage(i);
GL_REPORT_ERRORD();
if(g_Config.bStretchToFit)
{
//TODO: Do Correctly in a bit
float FactorW = (float)640 / (float)nBackbufferWidth;
float FactorH = (float)480 / (float)nBackbufferHeight;
float Max = (FactorW < FactorH) ? FactorH : FactorW;
float Temp = 1 / Max;
FactorW *= Temp;
FactorH *= Temp;
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(-FactorW,-FactorH);
glTexCoord2f(0, (float)GetTargetHeight()); glVertex2f(-FactorW,FactorH);
glTexCoord2f((float)GetTargetWidth(), (float)GetTargetHeight()); glVertex2f(FactorW,FactorH);
glTexCoord2f((float)GetTargetWidth(), 0); glVertex2f(FactorW,-FactorH);
__Log("%d, %d", FactorW, FactorH);
}
else
{
// setup the stencil to only accept pixels that have been written
glStencilFunc(GL_EQUAL, 1, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(-1,-1);
glTexCoord2f(0, (float)(GetTargetHeight())); glVertex2f(-1,1);
glTexCoord2f((float)(GetTargetWidth()), (float)(GetTargetHeight())); glVertex2f(1,1);
glTexCoord2f((float)(GetTargetWidth()), 0); glVertex2f(1,-1);
}
glEnd();
GL_REPORT_ERRORD();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
RestoreGLState();
}
void Renderer::SetRenderMode(RenderMode mode)
{
if( !s_bHaveStencilBuffer && mode == RM_ZBufferAlpha )
mode = RM_ZBufferOnly;
if( s_RenderMode == mode )
return;
if( mode == RM_Normal ) {
// flush buffers
if( s_RenderMode == RM_ZBufferAlpha ) {
FlushZBufferAlphaToTarget();
glDisable(GL_STENCIL_TEST);
}
SetColorMask();
SetRenderTarget(0);
SetZBufferRender();
GL_REPORT_ERRORD();
}
else if( s_RenderMode == RM_Normal ) {
// setup buffers
_assert_(GetZBufferTarget() && bpmem.zmode.updateenable);
if( mode == RM_ZBufferAlpha ) {
glEnable(GL_STENCIL_TEST);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 1, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
}
glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
GL_REPORT_ERRORD();
}
else {
_assert_(GetZBufferTarget());
_assert_(s_bHaveStencilBuffer);
if( mode == RM_ZBufferOnly ) {
// flush and remove stencil
_assert_(s_RenderMode==RM_ZBufferAlpha);
FlushZBufferAlphaToTarget();
glDisable(GL_STENCIL_TEST);
SetRenderTarget(s_ZBufferTarget);
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
GL_REPORT_ERRORD();
}
else {
_assert_(mode == RM_ZBufferAlpha&&s_RenderMode==RM_ZBufferOnly);
// setup stencil
glEnable(GL_STENCIL_TEST);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 1, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
}
}
s_RenderMode = mode;
}
Renderer::RenderMode Renderer::GetRenderMode()
{
return s_RenderMode;
}
u32 Renderer::GetZBufferTarget()
{
return nZBufferRender > 0 ? s_ZBufferTarget : 0;
}
void Renderer::Swap(const TRectangle& rc)
{
OpenGL_Update(); // just updates the render window position and the backbuffer size
DVProfileFunc _pf("Renderer::Swap");
Renderer::SetRenderMode(Renderer::RM_Normal);
// render to the real buffer now
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch to the backbuffer
glViewport(nXoff, nYoff, nBackbufferWidth, nBackbufferHeight);
ResetGLState();
// texture map s_RenderTargets[s_curtarget] onto the main buffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTargets[s_nCurTarget]);
TextureMngr::EnableTexRECT(0);
// disable all other stages
for(int i = 1; i < 8; ++i) TextureMngr::DisableStage(i);
GL_REPORT_ERRORD();
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(-1,-1);
glTexCoord2f(0, (float)GetTargetHeight()); glVertex2f(-1,1);
glTexCoord2f((float)GetTargetWidth(), (float)GetTargetHeight()); glVertex2f(1,1);
glTexCoord2f((float)GetTargetWidth(), 0); glVertex2f(1,-1);
glEnd();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
TextureMngr::DisableStage(0);
static int fpscount;
static int s_fps;
static unsigned long lasttime;
++fpscount;
if( timeGetTime() - lasttime > 1000 )
{
lasttime = timeGetTime();
s_fps = fpscount;
fpscount = 0;
}
// ---------------------------------------------------------------------------------------
// Write logging data to debugger
// -----------------
if(m_frame)
{
Logging(0);
}
if (g_Config.bOverlayStats) {
char st[2048];
char *p = st;
if(g_Config.bShowFPS)
p+=sprintf(p, "FPS: %d\n", s_fps); // So it shows up before the stats and doesn't make anyting ugly
p+=sprintf(p,"Num textures created: %i\n",stats.numTexturesCreated);
p+=sprintf(p,"Num textures alive: %i\n",stats.numTexturesAlive);
p+=sprintf(p,"Num pshaders created: %i\n",stats.numPixelShadersCreated);
p+=sprintf(p,"Num pshaders alive: %i\n",stats.numPixelShadersAlive);
p+=sprintf(p,"Num vshaders created: %i\n",stats.numVertexShadersCreated);
p+=sprintf(p,"Num vshaders alive: %i\n",stats.numVertexShadersAlive);
p+=sprintf(p,"Num dlists called: %i\n",stats.numDListsCalled);
p+=sprintf(p,"Num dlists called (frame): %i\n",stats.thisFrame.numDListsCalled);
// not used.
//p+=sprintf(p,"Num dlists created: %i\n",stats.numDListsCreated);
//p+=sprintf(p,"Num dlists alive: %i\n",stats.numDListsAlive);
//p+=sprintf(p,"Num strip joins: %i\n",stats.numJoins);
p+=sprintf(p,"Num primitives: %i\n",stats.thisFrame.numPrims);
p+=sprintf(p,"Num primitives (DL): %i\n",stats.thisFrame.numDLPrims);
p+=sprintf(p,"Num bad commands: %i%s\n",stats.thisFrame.numBadCommands,stats.thisFrame.numBadCommands?"!!!":"");
p+=sprintf(p,"Num XF loads: %i\n",stats.thisFrame.numXFLoads);
p+=sprintf(p,"Num XF loads (DL): %i\n",stats.thisFrame.numXFLoadsInDL);
p+=sprintf(p,"Num CP loads: %i\n",stats.thisFrame.numCPLoads);
p+=sprintf(p,"Num CP loads (DL): %i\n",stats.thisFrame.numCPLoadsInDL);
p+=sprintf(p,"Num BP loads: %i\n",stats.thisFrame.numBPLoads);
p+=sprintf(p,"Num BP loads (DL): %i\n",stats.thisFrame.numBPLoadsInDL);
Renderer::RenderText(st, 20, 20, 0xFF00FFFF);
}
else
{
if(g_Config.bShowFPS)
{
char strfps[25];
sprintf(strfps, "%d\n", s_fps);
Renderer::RenderText(strfps, 20, 20, 0xFF00FFFF);
}
}
Renderer::ProcessMessages();
#if defined(DVPROFILE)
if (g_bWriteProfile) {
//g_bWriteProfile = 0;
static int framenum = 0;
const int UPDATE_FRAMES = 8;
if (++framenum >= UPDATE_FRAMES) {
DVProfWrite("prof.txt", UPDATE_FRAMES);
DVProfClear();
framenum = 0;
}
}
#endif
// copy the rendered from to the real window
OpenGL_SwapBuffers();
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
GL_REPORT_ERRORD();
//clean out old stuff from caches
frameCount++;
PixelShaderMngr::Cleanup();
TextureMngr::Cleanup();
// New frame
stats.ResetFrame();
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, s_uFramebuffer );
// s_nCurTarget = !s_nCurTarget;
// SetRenderTarget(0);
if( nZBufferRender > 0 ) {
if( --nZBufferRender == 0 ) {
// turn off
nZBufferRender = 0;
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
Renderer::SetRenderMode(RM_Normal); // turn off any zwrites
}
}
RestoreGLState();
GL_REPORT_ERRORD();
g_Config.iSaveTargetId = 0;
// for testing zbuffer targets
//Renderer::SetZBufferRender();
//SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget, GetTargetWidth(), GetTargetHeight());
}
bool Renderer::SaveRenderTarget(const char* filename, int jpeg)
{
bool bflip = true;
vector<u32> data(nBackbufferWidth*nBackbufferHeight);
glReadPixels(0, 0, nBackbufferWidth, nBackbufferHeight, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]);
if (glGetError() != GL_NO_ERROR)
return false;
if (bflip) {
// swap scanlines
vector<u32> scanline(nBackbufferWidth);
for(int i = 0; i < nBackbufferHeight/2; ++i) {
memcpy(&scanline[0], &data[i*nBackbufferWidth], nBackbufferWidth*4);
memcpy(&data[i*nBackbufferWidth], &data[(nBackbufferHeight-i-1)*nBackbufferWidth], nBackbufferWidth*4);
memcpy(&data[(nBackbufferHeight-i-1)*nBackbufferWidth], &scanline[0], nBackbufferWidth*4);
}
}
return SaveTGA(filename, nBackbufferWidth, nBackbufferHeight, &data[0]);
}
void Renderer::SetCgErrorOutput(bool bOutput)
{
s_bOutputCgErrors = bOutput;
}
void HandleGLError()
{
const GLubyte* pstr = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
if (pstr != NULL && pstr[0] != 0 ) {
GLint loc=0;
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &loc);
ERROR_LOG("program error at %d: ", loc);
ERROR_LOG((char*)pstr);
ERROR_LOG("\n");
}
// check the error status of this framebuffer */
GLenum error = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
// if error != GL_FRAMEBUFFER_COMPLETE_EXT, there's an error of some sort
if (error != 0) {
int w, h;
GLint fmt;
glGetRenderbufferParameterivEXT(GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_INTERNAL_FORMAT_EXT, &fmt);
glGetRenderbufferParameterivEXT(GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_WIDTH_EXT, (GLint *)&w);
glGetRenderbufferParameterivEXT(GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_HEIGHT_EXT, (GLint *)&h);
switch(error)
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
ERROR_LOG("Error! missing a required image/buffer attachment!\n");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
ERROR_LOG("Error! has no images/buffers attached!\n");
break;
// case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
// ERROR_LOG("Error! has an image/buffer attached in multiple locations!\n");
// break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
ERROR_LOG("Error! has mismatched image/buffer dimensions!\n");
break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
ERROR_LOG("Error! colorbuffer attachments have different types!\n");
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
ERROR_LOG("Error! trying to draw to non-attached color buffer!\n");
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
ERROR_LOG("Error! trying to read from a non-attached color buffer!\n");
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
ERROR_LOG("Error! format is not supported by current graphics card/driver!\n");
break;
default:
ERROR_LOG("*UNKNOWN ERROR* reported from glCheckFramebufferStatusEXT()!\n");
break;
}
}
}
void HandleCgError(CGcontext ctx, CGerror err, void* appdata)
{
if( s_bOutputCgErrors ) {
ERROR_LOG("Cg error: %s\n", cgGetErrorString(err));
const char* listing = cgGetLastListing(g_cgcontext);
if (listing != NULL) {
ERROR_LOG(" last listing: %s\n", listing);
}
// glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &loc);
// printf("pos: %d\n", loc);
}
}