// 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 #include #include #include "GLUtil.h" #include #include #ifdef _WIN32 #include #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 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::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 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 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"); */ }