dolphin/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp

1345 lines
44 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>
#include <vector>
#include <cmath>
#include "GLUtil.h"
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#ifdef _WIN32
#include <mmsystem.h>
#endif
#include "Config.h"
#include "Profiler.h"
#include "Statistics.h"
#include "ImageWrite.h"
#include "Render.h"
#include "OpcodeDecoding.h"
#include "BPStructs.h"
#include "TextureMngr.h"
#include "rasterfont.h"
#include "VertexShaderGen.h"
#include "PixelShaderCache.h"
#include "PixelShaderManager.h"
#include "VertexLoaderManager.h"
#include "VertexLoader.h"
#include "XFB.h"
#include "Timer.h"
#include "main.h" // Local
#ifdef _WIN32
#include "OS/Win32.h"
#endif
#if defined(HAVE_WX) && HAVE_WX
#include "Debugger/Debugger.h" // for the CDebugger class
#endif
#ifdef _WIN32
#include "Win32Window.h" // warning: crapcode
#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;
CGprofile g_cgfProf;
#if defined(HAVE_WX) && HAVE_WX
extern CDebugger* m_frame; // the debugging class
#endif
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 use zbuffer render, and count down.
// A framebuffer is a set of render targets: a color and a z buffer. They can be either RenderBuffers or Textures.
static GLuint s_uFramebuffer = 0;
// The size of these should be a (not necessarily even) multiple of the EFB size, 640x528, but isn'.t
static GLuint s_RenderTarget = 0;
static GLuint s_DepthTarget = 0;
static GLuint s_ZBufferTarget = 0;
static bool s_bATIDrawBuffers = false;
static bool s_bHaveStencilBuffer = false;
static Renderer::RenderMode s_RenderMode = Renderer::RM_Normal;
bool g_bBlendSeparate = false;
int frameCount;
void HandleCgError(CGcontext ctx, CGerror err, void *appdata);
/////////////////////////////
static const GLenum glSrcFactors[8] =
{
GL_ZERO,
GL_ONE,
GL_DST_COLOR,
GL_ONE_MINUS_DST_COLOR,
GL_SRC_ALPHA,
GL_ONE_MINUS_SRC_ALPHA,
GL_DST_ALPHA,
GL_ONE_MINUS_DST_ALPHA
};
static const GLenum glDestFactors[8] = {
GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA
};
static u32 s_blendMode;
bool Renderer::Init()
{
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;
INFO_LOG("Supported OpenGL Extensions:\n");
INFO_LOG(ptoken); // write to the log file
INFO_LOG("\n");
if (strstr(ptoken, "GL_EXT_blend_func_separate") != NULL && strstr(ptoken, "GL_EXT_blend_equation_separate") != NULL)
g_bBlendSeparate = 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");
#elif defined(HAVE_X11) && HAVE_X11
if (glXSwapIntervalSGI)
glXSwapIntervalSGI(0);
else
ERROR_LOG("no support for SwapInterval (framerate clamped to monitor refresh rate)\n");
#else
//TODO
#endif
// check the max texture width and height
GLint max_texture_size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&max_texture_size);
if (max_texture_size < 1024) {
ERROR_LOG("GL_MAX_TEXTURE_SIZE too small at %i - must be at least 1024", max_texture_size);
}
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);
// The size of the framebuffer targets should really NOT be the size of the OpenGL viewport.
// The EFB is larger than 640x480 - in fact, it's 640x528, give or take a couple of lines.
// So the below is wrong.
int nBackbufferWidth = (int)OpenGL_GetWidth();
int nBackbufferHeight = (int)OpenGL_GetHeight();
// Create the framebuffer target
glGenTextures(1, (GLuint *)&s_RenderTarget);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget);
// 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);
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_RenderTarget, 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();
// 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);
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]);
DEBUG_LOG("max program env parameters: vert=%d, frag=%d\n", nenvvertparams, nenvfragparams);
DEBUG_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;
}
s_RenderMode = Renderer::RM_Normal;
if (!InitializeGL())
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) {
cgDestroyContext(g_cgcontext);
g_cgcontext = 0;
}
if (s_RenderTarget) {
glDeleteTextures(1, &s_RenderTarget);
s_RenderTarget = 0;
}
if (s_DepthTarget) {
glDeleteRenderbuffersEXT(1, &s_DepthTarget);
s_DepthTarget = 0;
}
if (s_uFramebuffer) {
glDeleteFramebuffersEXT(1, &s_uFramebuffer);
s_uFramebuffer = 0;
}
}
bool Renderer::InitializeGL()
{
glStencilFunc(GL_ALWAYS, 0, 0);
glBlendFunc(GL_ONE, GL_ONE);
glViewport(0, 0, GetTargetWidth(), GetTargetHeight()); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDepthFunc(GL_LEQUAL);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // perspective correct interpolation of colors and tex coords
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
// Do we really need to set this initial glScissor() value? Wont it be called all the time while the game is running?
//glScissor(0, 0, (int)OpenGL_GetWidth(), (int)OpenGL_GetHeight());
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);
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;
std::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)
{
int nBackbufferWidth = (int)OpenGL_GetWidth();
int nBackbufferHeight = (int)OpenGL_GetHeight();
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::ReinitView(int nNewWidth, int nNewHeight)
{
int oldscreen = s_bFullscreen;
OpenGL_Shutdown();
int oldwidth = (int)OpenGL_GetWidth(),
oldheight = (int)OpenGL_GetHeight();
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
}
OpenGL_SetSize(nNewWidth > 16 ? nNewWidth : 16,
nNewHeight > 16 ? nNewHeight : 16);
}
//////////////////////////////////////////////////////////////////////////////////////
// Return the rendering window width and height
// ------------------------
int Renderer::GetTargetWidth()
{
return (g_Config.bStretchToFit ? 640 : (int)OpenGL_GetWidth());
}
int Renderer::GetTargetHeight()
{
return (g_Config.bStretchToFit ? 480 : (int)OpenGL_GetHeight());
}
/////////////////////////////
void Renderer::SetRenderTarget(GLuint targ)
{
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB,
targ != 0 ? targ : s_RenderTarget, 0);
}
void Renderer::SetDepthTarget(GLuint targ)
{
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT,
targ != 0 ? targ : s_DepthTarget);
}
void Renderer::SetFramebuffer(GLuint fb)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,
fb != 0 ? fb : s_uFramebuffer);
}
GLuint Renderer::GetRenderTarget()
{
return s_RenderTarget;
}
GLuint Renderer::GetZBufferTarget()
{
return nZBufferRender > 0 ? s_ZBufferTarget : 0;
}
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.zmode.updateenable) glDepthMask(GL_TRUE);
glEnable(GL_VERTEX_PROGRAM_ARB);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
SetColorMask();
SetBlendMode(true);
}
void Renderer::SetColorMask()
{
if (bpmem.blendmode.alphaupdate && bpmem.blendmode.colorupdate)
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
else if (bpmem.blendmode.alphaupdate)
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_TRUE);
else if (bpmem.blendmode.colorupdate)
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_FALSE);
}
void Renderer::SetBlendMode(bool forceUpdate)
{
// blend mode bit mask
// 0 - blend enable
// 1 - dst alpha enable
// 2 - reverse subtract enable (else add)
// 3-5 - srcRGB function
// 6-8 - dstRGB function
// 9-16 - dstAlpha
u32 newval = bpmem.blendmode.subtract << 2;
if (g_bBlendSeparate) {
newval |= bpmem.dstalpha.enable ? 3 : 0;
newval |= bpmem.dstalpha.alpha << 9;
}
if (bpmem.blendmode.blendenable) {
newval |= 1;
if (bpmem.blendmode.subtract) {
newval |= 0x0048; // src 1 dst 1
} else {
newval |= bpmem.blendmode.srcfactor << 3;
newval |= bpmem.blendmode.dstfactor << 6;
}
} else {
newval |= 0x0008; // src 1 dst 0
}
u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode;
if (changes & 1) {
newval & 1 ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
}
bool dstAlphaEnable = g_bBlendSeparate && newval & 2;
if (changes & 6) {
// dst alpha enable or subtract enable change
if (dstAlphaEnable)
glBlendEquationSeparate(newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD, GL_FUNC_ADD);
else
glBlendEquation(newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD);
}
if (changes & 0x1FA) {
// dst alpha enable or blend RGB change
if (dstAlphaEnable)
glBlendFuncSeparate(glSrcFactors[(newval >> 3) & 7], glDestFactors[(newval >> 6) & 7], GL_CONSTANT_ALPHA, GL_ZERO);
else
glBlendFunc(glSrcFactors[(newval >> 3) & 7], glDestFactors[(newval >> 6) & 7]);
}
if (dstAlphaEnable && changes & 0x1FE00) {
// dst alpha color change
glBlendColor(0, 0, 0, (float)bpmem.dstalpha.alpha / 255.0f);
}
s_blendMode = newval;
}
////////////////////////////////////////////////////////////////////////////////////////////
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
// case 0x52 > SetScissorRect()
// ---------------
// This function handles the OpenGL glScissor() function
// ---------------
// bpmem.scissorTL.x, y = 342x342
// bpmem.scissorBR.x, y = 981x821
// 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()
{
int xoff = bpmem.scissorOffset.x * 2 - 342;
int yoff = bpmem.scissorOffset.y * 2 - 342;
float MValueX = OpenGL_GetXmax();
float MValueY = OpenGL_GetYmax();
float rc_left = bpmem.scissorTL.x - xoff - 342; // left = 0
rc_left *= MValueX;
if (rc_left < 0) rc_left = 0;
float rc_top = bpmem.scissorTL.y - yoff - 342; // right = 0
rc_top *= MValueY;
if (rc_top < 0) rc_top = 0;
float rc_right = bpmem.scissorBR.x - xoff - 341; // right = 640
rc_right *= MValueX;
if (rc_right > 640 * MValueX) rc_right = 640 * MValueX;
float rc_bottom = bpmem.scissorBR.y - yoff - 341; // bottom = 480
rc_bottom *= MValueY;
if (rc_bottom > 480 * MValueY) rc_bottom = 480 * MValueY;
/*LOG("Scissor: lt=(%d,%d), rb=(%d,%d,%i), off=(%d,%d)\n",
rc_left, rc_top,
rc_right, rc_bottom, Renderer::GetTargetHeight(),
xoff, yoff
);*/
// Check that the coordinates are good
if (rc_right >= rc_left && rc_bottom >= rc_top)
{
/* I don't know how this works with other options so I'm limiting it to this to test it, if you want to add support for other modes
or make this solution more general please do */
if(g_Config.bStretchToFit && !g_Config.bKeepAR)
{
int WidthDifference = 640 - (int)(rc_right - rc_left);
int HeightDifference = 480 - (int)(rc_bottom - rc_top);
GLScissorX = (int)rc_left; GLScissorY = -(Renderer::GetTargetHeight() - (int)(rc_bottom));
GLScissorW = Renderer::GetTargetWidth() + WidthDifference; GLScissorH = Renderer::GetTargetHeight() + HeightDifference;
}
else
{
GLScissorX = (int)rc_left; GLScissorY = Renderer::GetTargetHeight() - (int)(rc_bottom);
GLScissorW = (int)(rc_right - rc_left); GLScissorH = (int)(rc_bottom - rc_top);
}
glScissor(
GLScissorX, // x = 0
GLScissorY, // y = 0
GLScissorW, // width = 640 for example
GLScissorH // height = 480 for example
);
return true;
}
return false;
}
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();
// setup the stencil to only accept pixels that have been written
glStencilFunc(GL_EQUAL, 1, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// TODO: This code should not have to bother with stretchtofit checking -
// all necessary scale initialization should be done elsewhere.
// TODO: Investigate BlitFramebufferEXT.
if (g_Config.bStretchToFit)
{
//TODO: Do Correctly in a bit
float FactorW = 640.f / (float)OpenGL_GetWidth();
float FactorH = 480.f / (float)OpenGL_GetHeight();
float Max = (FactorW < FactorH) ? FactorH : FactorW;
float Temp = 1.0f / 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);
glEnd();
}
else
{
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;
}
void Renderer::Swap(const TRectangle& rc)
{
OpenGL_Update(); // just updates the render window position and the backbuffer size
DVSTARTPROFILE();
Renderer::SetRenderMode(Renderer::RM_Normal);
#if 0
// Not working?
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
glBlitFramebufferEXT(0, 0, 640, 480, 0, 0, OpenGL_GetWidth(), OpenGL_GetHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
#else
// render to the real buffer now
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the backbuffer
glViewport(OpenGL_GetXoff(), OpenGL_GetYoff(), (int)OpenGL_GetWidth(), (int)OpenGL_GetHeight());
ResetGLState();
// texture map s_RenderTargets[s_curtarget] onto the main buffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget);
TextureMngr::EnableTexRECT(0);
// disable all other stages
for (int i = 1; i < 8; ++i)
TextureMngr::DisableStage(i);
GL_REPORT_ERRORD();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
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();
if (g_Config.bWireFrame)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
TextureMngr::DisableStage(0);
SwapBuffers();
RestoreGLState();
#endif
GL_REPORT_ERRORD();
g_Config.iSaveTargetId = 0;
// for testing zbuffer targets
//Renderer::SetZBufferRender();
//SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget, GetTargetWidth(), GetTargetHeight());
}
void Renderer::SwapBuffers()
{
static int fpscount;
static int s_fps;
static unsigned long lasttime;
char debugtext_buffer[8192];
char *p = debugtext_buffer;
p[0] = 0;
++fpscount;
if (timeGetTime() - lasttime > 1000)
{
lasttime = timeGetTime();
s_fps = fpscount;
fpscount = 0;
}
if (g_Config.bShowFPS)
{
p+=sprintf(p, "%d\n", s_fps);
}
if (g_Config.bOverlayStats) {
p+=sprintf(p,"textures created: %i\n",stats.numTexturesCreated);
p+=sprintf(p,"textures alive: %i\n",stats.numTexturesAlive);
p+=sprintf(p,"pshaders created: %i\n",stats.numPixelShadersCreated);
p+=sprintf(p,"pshaders alive: %i\n",stats.numPixelShadersAlive);
p+=sprintf(p,"vshaders created: %i\n",stats.numVertexShadersCreated);
p+=sprintf(p,"vshaders alive: %i\n",stats.numVertexShadersAlive);
p+=sprintf(p,"dlists called: %i\n",stats.numDListsCalled);
p+=sprintf(p,"dlists called(f): %i\n",stats.thisFrame.numDListsCalled);
// not used.
//p+=sprintf(p,"dlists created: %i\n",stats.numDListsCreated);
//p+=sprintf(p,"dlists alive: %i\n",stats.numDListsAlive);
//p+=sprintf(p,"strip joins: %i\n",stats.numJoins);
p+=sprintf(p,"primitives: %i\n",stats.thisFrame.numPrims);
p+=sprintf(p,"primitive joins: %i\n",stats.thisFrame.numPrimitiveJoins);
p+=sprintf(p,"buffer splits: %i\n",stats.thisFrame.numBufferSplits);
p+=sprintf(p,"draw calls: %i\n",stats.thisFrame.numDrawCalls);
p+=sprintf(p,"primitives (DL): %i\n",stats.thisFrame.numDLPrims);
p+=sprintf(p,"XF loads: %i\n",stats.thisFrame.numXFLoads);
p+=sprintf(p,"XF loads (DL): %i\n",stats.thisFrame.numXFLoadsInDL);
p+=sprintf(p,"CP loads: %i\n",stats.thisFrame.numCPLoads);
p+=sprintf(p,"CP loads (DL): %i\n",stats.thisFrame.numCPLoadsInDL);
p+=sprintf(p,"BP loads: %i\n",stats.thisFrame.numBPLoads);
p+=sprintf(p,"BP loads (DL): %i\n",stats.thisFrame.numBPLoadsInDL);
p+=sprintf(p,"vertex loaders: %i\n",stats.numVertexLoaders);
std::string text1;
VertexLoaderManager::AppendListToString(&text1);
p+=sprintf(p,"%s",text1.c_str());
}
if (g_Config.bOverlayBlendStats)
{
p+=sprintf(p,"LogicOp Mode: %i\n", stats.logicOpMode);
p+=sprintf(p,"Source Factor: %i\n", stats.srcFactor);
p+=sprintf(p,"Destination Factor: %i\n", stats.dstFactor);
p+=sprintf(p,"Dithering: %s\n", stats.dither==1 ? "Enabled" : "Disabled");
p+=sprintf(p,"Color Update: %s\n", stats.colorUpdate==1 ? "Enabled" : "Disabled");
p+=sprintf(p,"Alpha Update: %s\n", stats.alphaUpdate==1 ? "Enabled" : "Disabled");
p+=sprintf(p,"Dst Alpha Enabled: %s\n", stats.dstAlphaEnable==1 ? "Enabled" : "Disabled");
p+=sprintf(p,"Dst Alpha: %08x\n", stats.dstAlpha);
}
if (g_Config.bOverlayProjStats)
{
p+=sprintf(p,"Projection #: X for Raw 6=0 (X for Raw 6!=0)\n\n");
p+=sprintf(p,"Projection 0: %f (%f) Raw 0: %f\n", stats.gproj_0, stats.g2proj_0, stats.proj_0);
p+=sprintf(p,"Projection 1: %f (%f)\n", stats.gproj_1, stats.g2proj_1);
p+=sprintf(p,"Projection 2: %f (%f) Raw 1: %f\n", stats.gproj_2, stats.g2proj_2, stats.proj_1);
p+=sprintf(p,"Projection 3: %f (%f)\n\n", stats.gproj_3, stats.g2proj_3);
p+=sprintf(p,"Projection 4: %f (%f)\n", stats.gproj_4, stats.g2proj_4);
p+=sprintf(p,"Projection 5: %f (%f) Raw 2: %f\n", stats.gproj_5, stats.g2proj_5, stats.proj_2);
p+=sprintf(p,"Projection 6: %f (%f) Raw 3: %f\n", stats.gproj_6, stats.g2proj_6, stats.proj_3);
p+=sprintf(p,"Projection 7: %f (%f)\n\n", stats.gproj_7, stats.g2proj_7);
p+=sprintf(p,"Projection 8: %f (%f)\n", stats.gproj_8, stats.g2proj_8);
p+=sprintf(p,"Projection 9: %f (%f)\n", stats.gproj_9, stats.g2proj_9);
p+=sprintf(p,"Projection 10: %f (%f) Raw 4: %f\n\n", stats.gproj_10, stats.g2proj_10, stats.proj_4);
p+=sprintf(p,"Projection 11: %f (%f) Raw 5: %f\n\n", stats.gproj_11, stats.g2proj_11, stats.proj_5);
p+=sprintf(p,"Projection 12: %f (%f)\n", stats.gproj_12, stats.g2proj_12);
p+=sprintf(p,"Projection 13: %f (%f)\n", stats.gproj_13, stats.g2proj_13);
p+=sprintf(p,"Projection 14: %f (%f)\n", stats.gproj_14, stats.g2proj_14);
p+=sprintf(p,"Projection 15: %f (%f)\n", stats.gproj_15, stats.g2proj_15);
}
// Render a shadow, and then the text.
Renderer::RenderText(debugtext_buffer, 21, 21, 0xDD000000);
Renderer::RenderText(debugtext_buffer, 20, 20, 0xFF00FFFF);
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 frame 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
PixelShaderCache::Cleanup();
TextureMngr::Cleanup();
frameCount++;
// New frame
stats.ResetFrame();
// Render to the framebuffer.
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
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
}
}
}
bool Renderer::SaveRenderTarget(const char* filename, int jpeg)
{
bool bflip = true;
int nBackbufferHeight = (int)OpenGL_GetHeight();
int nBackbufferWidth = (int)OpenGL_GetWidth();
std::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
std::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)
return;
// 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);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// 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
/*INFO_LOG("view: topleft=(%f,%f), wh=(%f,%f), z=(%f,%f)\n",
rawViewport[3]-rawViewport[0]-342, rawViewport[4]+rawViewport[1]-342,
2 * rawViewport[0], 2 * rawViewport[1],
(rawViewport[5] - rawViewport[2]) / 16777215.0f, rawViewport[5] / 16777215.0f);*/
// -----------------------------------------------------------------------
// Keep aspect ratio at 4:3
// ------------------
// rawViewport[0] = 320, rawViewport[1] = -240
int scissorXOff = bpmem.scissorOffset.x * 2 - 342;
int scissorYOff = bpmem.scissorOffset.y * 2 - 342;
float fourThree = 4.0f / 3.0f;
float wAdj, hAdj;
float actualRatiow, actualRatioh;
int overfl;
int xoffs = 0, yoffs = 0;
int wid, hei, actualWid, actualHei;
// The rendering window width and height
int winw = OpenGL_GetWidth();
int winh = OpenGL_GetHeight();
// The rendering window aspect ratio
float ratio = (float)winw / (float)winh / fourThree;
if (g_Config.bKeepAR)
{
// Check if height or width is the limiting factor. If ratio > 1 the picture is to wide and have to limit the width.
if (ratio > 1)
{
wAdj = ratio;
hAdj = 1;
wid = ceil(fabs(2 * xfregs.rawViewport[0]) / wAdj);
hei = ceil(fabs(2 * xfregs.rawViewport[1]) / hAdj);
actualWid = ceil((float)winw / ratio);
actualRatiow = (float)actualWid / (float)wid; // the picture versus the screen
overfl = (winw - actualWid) / actualRatiow;
xoffs = overfl / 2;
}
// The window is to high, we have to limit the height
else
{
ratio = 1 / ratio;
wAdj = 1;
hAdj = ratio;
wid = ceil(fabs(2 * xfregs.rawViewport[0]) / wAdj);
hei = ceil(fabs(2 * xfregs.rawViewport[1]) / hAdj);
actualHei = ceil((float)winh / ratio);
actualRatioh = (float)actualHei / (float)hei; // the picture versus the screen
overfl = (winh - actualHei) / actualRatioh;
yoffs = overfl / 2;
}
}
else
{
wid = ceil(fabs(2 * xfregs.rawViewport[0]));
hei = ceil(fabs(2 * xfregs.rawViewport[1]));
}
// -------------------------------------
// -----------------------------------------------------------------------
// GLViewPort variables
// ------------------
int GLWidth, GLHeight, GLx, GLy;
// -------------------------------------
// -----------------------------------------------------------------------
// Stretch picture while keeping the native resolution
// ------------------
if (g_Config.bStretchToFit)
{
GLx = (int)(xfregs.rawViewport[3]-xfregs.rawViewport[0]-342-scissorXOff) + xoffs;
GLy = Renderer::GetTargetHeight() - ((int)(xfregs.rawViewport[4]-xfregs.rawViewport[1]-342-scissorYOff)) + yoffs;
GLWidth = wid; // width
GLHeight = hei; // height
}
else
{
float MValueX = OpenGL_GetXmax();
float MValueY = OpenGL_GetYmax();
GLx = (int)(xfregs.rawViewport[3]-xfregs.rawViewport[0]-342-scissorXOff) * MValueX;
GLy = Renderer::GetTargetHeight()-((int)(xfregs.rawViewport[4]-xfregs.rawViewport[1]-342-scissorYOff)) * MValueY;
GLWidth = abs((int)(2 * xfregs.rawViewport[0])) * MValueX;
GLHeight = abs((int)(2 * xfregs.rawViewport[1])) * MValueY;
}
// -------------------------------------
// -----------------------------------------------------------------------
// Adjust the screen size. If someone figures out how to correctly adjust the screen size as the GC or Wii does, this may not be needed.
// ------------------
if (g_Config.bScreenSize)
{
// Calculate width and height as a fraction of the current
float Width = (float)GLWidth * ((float)g_Config.iScreenWidth / 100.0);
float Height = (float)GLHeight * ((float)g_Config.iScreenHeight / 100.0);
// Adjust X and Y
GLx = GLx + g_Config.iScreenLeft;
GLy = GLy - g_Config.iScreenTop;
GLWidth = (int)Width;
GLHeight = (int)Height;
//Console::Print("W:%i H:%i W:%f H:%f Wid:%i Hei:%i x:%i y:%i\n", g_Config.iScreenWidth, g_Config.iScreenHeight, Width, Height, GLWidth, GLHeight, GLx, GLy);
}
// -------------------------------------
// I'm limiting it to this modes to test it
if(g_Config.bStretchToFit && !g_Config.bKeepAR)
{
GLWidth = GLWidth + (GLScissorW - 640);
GLHeight = GLHeight + (GLScissorH - 480);
GLy = 0;
GLx = 0;
}
glViewport(
GLx, GLy,
GLWidth, GLHeight
);
// -----------------------------------------------------------------------
// GLDepthRange
// ------------------
double GLNear = (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777215.0f;
double GLFar = xfregs.rawViewport[5] / 16777215.0f;
glDepthRange(GLNear, GLFar);
// -------------------------------------
// Logging
/*
RECT RcTop, RcParent, RcChild;
HWND Child = EmuWindow::GetWnd();
HWND Parent = GetParent(Child);
HWND Top = GetParent(Parent);
GetWindowRect(Top, &RcTop);
GetWindowRect(Parent, &RcParent);
GetWindowRect(Child, &RcChild);
Console::ClearScreen();
Console::Print("----------------------------------------------------------------\n");
Console::Print("Top window: X:%03i Y:%03i Width:%03i Height:%03i\n", RcTop.left, RcTop.top, RcTop.right - RcTop.left, RcTop.bottom - RcTop.top);
Console::Print("Parent window: X:%03i Y:%03i Width:%03i Height:%03i\n", RcParent.left, RcParent.top, RcParent.right - RcParent.left, RcParent.bottom - RcParent.top);
Console::Print("Child window: X:%03i Y:%03i Width:%03i Height:%03i\n", RcChild.left, RcChild.top, RcChild.right - RcChild.left, RcChild.bottom - RcChild.top);
Console::Print("----------------------------------------------------------------\n");
Console::Print("Res. MValue: X:%f Y:%f XOffs:%f YOffs:%f\n", OpenGL_GetXmax(), OpenGL_GetYmax(), OpenGL_GetXoff(), OpenGL_GetYoff());
Console::Print("GLViewPort: X:%03i Y:%03i Width:%03i Height:%03i\n", GLx, GLy, GLWidth, GLHeight);
Console::Print("GLDepthRange: Near:%f Far:%f\n", GLNear, GLFar);
Console::Print("GLScissor: X:%03i Y:%03i Width:%03i Height:%03i\n", GLScissorX, GLScissorY, GLScissorW, GLScissorH);
Console::Print("----------------------------------------------------------------\n");
*/
}