From 5ddb28a370b745c7367c27bbf679eec3b8503c9d Mon Sep 17 00:00:00 2001 From: John Peterson Date: Mon, 23 Feb 2009 07:12:18 +0000 Subject: [PATCH] OpenGL Keep Aspect Ratio: Removed the remains of old pictures that are sometimes shown in the bottom black border, if you use this option. So now both the upper and lower border is black. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2386 8ced0084-cf51-0410-be5f-012b33b47a6e --- .../Plugins/Plugin_VideoOGL/Src/BPStructs.cpp | 1544 +++++----- Source/Plugins/Plugin_VideoOGL/Src/Render.cpp | 2609 +++++++++-------- 2 files changed, 2108 insertions(+), 2045 deletions(-) diff --git a/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp b/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp index 5440047e7d..a64f2737f6 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp @@ -1,771 +1,773 @@ -// 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/ - -// --------------------------------------------------------------------------------------- -// includes -// ------------- -#include "Globals.h" -#include "Profiler.h" -#include "Config.h" -#include "Statistics.h" - -#include "VertexLoader.h" -#include "VertexManager.h" - -#include "BPStructs.h" -#include "Render.h" -#include "OpcodeDecoding.h" -#include "TextureMngr.h" -#include "TextureDecoder.h" -#include "TextureConverter.h" -#include "VertexShaderManager.h" -#include "PixelShaderManager.h" -#include "XFB.h" -#include "main.h" - - -// --------------------------------------------------------------------------------------- -// State translation lookup tables -// ------------- - -static const GLenum glCmpFuncs[8] = { - GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS -}; - -static const GLenum glLogicOpCodes[16] = { - GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP, GL_XOR, - GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE, GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET -}; - -void BPInit() -{ - memset(&bpmem, 0, sizeof(bpmem)); - bpmem.bpMask = 0xFFFFFF; -} - -////////////////////////////////////////////////////////////////////////////////////// -// Write to bpmem -/* ------------------ - Called: - At the end of every: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg - TODO: - Turn into function table. The (future) DL jit can then call the functions directly, - getting rid of dynamic dispatch. Unfortunately, few games use DLs properly - most\ - just stuff geometry in them and don't put state changes there. -// ------------------ */ -void BPWritten(int addr, int changes, int newval) -{ - switch (addr) - { - case BPMEM_GENMODE: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PRIM_LOG("genmode: texgen=%d, col=%d, ms_en=%d, tev=%d, culmode=%d, ind=%d, zfeeze=%d\n", - bpmem.genMode.numtexgens, bpmem.genMode.numcolchans, - bpmem.genMode.ms_en, bpmem.genMode.numtevstages+1, bpmem.genMode.cullmode, - bpmem.genMode.numindstages, bpmem.genMode.zfreeze); - - // none, ccw, cw, ccw - if (bpmem.genMode.cullmode > 0) { - glEnable(GL_CULL_FACE); - glFrontFace(bpmem.genMode.cullmode == 2 ? GL_CCW : GL_CW); - } - else if (glIsEnabled(GL_CULL_FACE) == GL_TRUE) - glDisable(GL_CULL_FACE); - - PixelShaderManager::SetGenModeChanged(); - } - break; - - case BPMEM_IND_MTX+0: - case BPMEM_IND_MTX+1: - case BPMEM_IND_MTX+2: - case BPMEM_IND_MTX+3: - case BPMEM_IND_MTX+4: - case BPMEM_IND_MTX+5: - case BPMEM_IND_MTX+6: - case BPMEM_IND_MTX+7: - case BPMEM_IND_MTX+8: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetIndMatrixChanged((addr-BPMEM_IND_MTX)/3); - } - break; - case BPMEM_RAS1_SS0: - case BPMEM_RAS1_SS1: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetIndTexScaleChanged(); - } - break; - case BPMEM_ZMODE: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PRIM_LOG("zmode: test=%d, func=%d, upd=%d\n", bpmem.zmode.testenable, bpmem.zmode.func, - bpmem.zmode.updateenable); - - if (bpmem.zmode.testenable) { - glEnable(GL_DEPTH_TEST); - glDepthMask(bpmem.zmode.updateenable?GL_TRUE:GL_FALSE); - glDepthFunc(glCmpFuncs[bpmem.zmode.func]); - } - else { - // if the test is disabled write is disabled too - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - } - - if (!bpmem.zmode.updateenable) - Renderer::SetRenderMode(Renderer::RM_Normal); - } - break; - - case BPMEM_ALPHACOMPARE: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PRIM_LOG("alphacmp: ref0=%d, ref1=%d, comp0=%d, comp1=%d, logic=%d\n", bpmem.alphaFunc.ref0, - bpmem.alphaFunc.ref1, bpmem.alphaFunc.comp0, bpmem.alphaFunc.comp1, bpmem.alphaFunc.logic); - PixelShaderManager::SetAlpha(bpmem.alphaFunc); - } - break; - - case BPMEM_CONSTANTALPHA: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PRIM_LOG("constalpha: alp=%d, en=%d\n", bpmem.dstalpha.alpha, bpmem.dstalpha.enable); - SETSTAT(stats.dstAlphaEnable, bpmem.dstalpha.enable); - SETSTAT_UINT(stats.dstAlpha, bpmem.dstalpha.alpha); - Renderer::SetBlendMode(false); - } - break; - - case BPMEM_LINEPTWIDTH: - { - float fratio = xfregs.rawViewport[0] != 0 ? (float)Renderer::GetTargetWidth() / 640.0f : 1.0f; - if (bpmem.lineptwidth.linesize > 0) - glLineWidth((float)bpmem.lineptwidth.linesize * fratio / 6.0f); // scale by ratio of widths - if (bpmem.lineptwidth.pointsize > 0) - glPointSize((float)bpmem.lineptwidth.pointsize * fratio / 6.0f); - break; - } - - case BPMEM_PE_CONTROL: // GXSetZCompLoc, GXPixModeSync - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - } - break; - - case BPMEM_BLENDMODE: - if (changes & 0xFFFF) { - - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - - PRIM_LOG("blendmode: en=%d, open=%d, colupd=%d, alphaupd=%d, dst=%d, src=%d, sub=%d, mode=%d\n", - bpmem.blendmode.blendenable, bpmem.blendmode.logicopenable, bpmem.blendmode.colorupdate, bpmem.blendmode.alphaupdate, - bpmem.blendmode.dstfactor, bpmem.blendmode.srcfactor, bpmem.blendmode.subtract, bpmem.blendmode.logicmode); - - /* - Logic Operation Blend Modes - -------------------- - 0: GL_CLEAR - 1: GL_AND - 2: GL_AND_REVERSE - 3: GL_COPY [Super Smash. Bro. Melee, NES Zelda I, NES Zelda II] - 4: GL_AND_INVERTED - 5: GL_NOOP - 6: GL_XOR - 7: GL_OR [Zelda: TP] - 8: GL_NOR - 9: GL_EQUIV - 10: GL_INVERT - 11: GL_OR_REVERSE - 12: GL_COPY_INVERTED - 13: GL_OR_INVERTED - 14: GL_NAND - 15: GL_SET - */ - - // LogicOp Blending - if (changes & 2) { - SETSTAT(stats.logicOpMode, bpmem.blendmode.logicopenable != 0 ? bpmem.blendmode.logicmode : stats.logicOpMode); - if (bpmem.blendmode.logicopenable) - { - glEnable(GL_COLOR_LOGIC_OP); - // PanicAlert("Logic Op Blend : %i", bpmem.blendmode.logicmode); - glLogicOp(glLogicOpCodes[bpmem.blendmode.logicmode]); - } - else - glDisable(GL_COLOR_LOGIC_OP); - } - - // Dithering - if (changes & 4) { - SETSTAT(stats.dither, bpmem.blendmode.dither); - if (bpmem.blendmode.dither) glEnable(GL_DITHER); - else glDisable(GL_DITHER); - } - - // Blending - if (changes & 0xFE1) - { - SETSTAT(stats.srcFactor, bpmem.blendmode.srcfactor); - SETSTAT(stats.dstFactor, bpmem.blendmode.dstfactor); - Renderer::SetBlendMode(false); - } - - // Color Mask - if (changes & 0x18) - { - SETSTAT(stats.alphaUpdate, bpmem.blendmode.alphaupdate); - SETSTAT(stats.colorUpdate, bpmem.blendmode.colorupdate); - Renderer::SetColorMask(); - } - } - break; - - case BPMEM_FOGRANGE: - if (changes) { - // TODO(XK): Fog range format - //glFogi(GL_FOG_START, ... - //glFogi(GL_FOG_END, ... - } - break; - - case BPMEM_FOGPARAM0: - case BPMEM_FOGBEXPONENT: - case BPMEM_FOGBMAGNITUDE: - case BPMEM_FOGPARAM3: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetFogParamChanged(); - } - break; - - case BPMEM_FOGCOLOR: - if (changes) - { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetFogColorChanged(); - } - break; - - case BPMEM_TEXINVALIDATE: - break; - - case BPMEM_SCISSOROFFSET: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - Renderer::SetScissorRect(); - } - break; - - case BPMEM_SCISSORTL: - case BPMEM_SCISSORBR: - - if (changes) - { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - if (!Renderer::SetScissorRect()) - { - if (addr == BPMEM_SCISSORBR) { - ERROR_LOG("bad scissor!\n"); - } - } - } - break; - case BPMEM_ZTEX1: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PRIM_LOG("ztex bias=0x%x\n", bpmem.ztex1.bias); - PixelShaderManager::SetZTextureBias(bpmem.ztex1.bias); - } - break; - case BPMEM_ZTEX2: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - if (changes & 3) { - PixelShaderManager::SetZTextureTypeChanged(); - } -#if defined(_DEBUG) || defined(DEBUGFAST) - const char* pzop[] = {"DISABLE", "ADD", "REPLACE", "?"}; - const char* pztype[] = {"Z8", "Z16", "Z24", "?"}; - PRIM_LOG("ztex op=%s, type=%s\n", pzop[bpmem.ztex2.op], pztype[bpmem.ztex2.type]); -#endif - } - break; - - case 0xf6: // ksel0 - case 0xf7: // ksel1 - case 0xf8: // ksel2 - case 0xf9: // ksel3 - case 0xfa: // ksel4 - case 0xfb: // ksel5 - case 0xfc: // ksel6 - case 0xfd: // ksel7 - if (changes) - { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetTevKSelChanged(addr-0xf6); - } - break; - - case BPMEM_SETDRAWDONE: - VertexManager::Flush(); - switch (newval & 0xFF) - { - case 0x02: - g_VideoInitialize.pSetPEFinish(); // may generate interrupt - DebugLog("GXSetDrawDone SetPEFinish (value: 0x%02X)", (newval & 0xFFFF)); - break; - - default: - DebugLog("GXSetDrawDone ??? (value 0x%02X)", (newval & 0xFFFF)); - break; - } - break; - - case BPMEM_PE_TOKEN_ID: - g_VideoInitialize.pSetPEToken(static_cast(newval & 0xFFFF), FALSE); - DebugLog("SetPEToken 0x%04x", (newval & 0xFFFF)); - break; - - case BPMEM_PE_TOKEN_INT_ID: - g_VideoInitialize.pSetPEToken(static_cast(newval & 0xFFFF), TRUE); - DebugLog("SetPEToken + INT 0x%04x", (newval & 0xFFFF)); - break; - - case BPMEM_SETGPMETRIC: // Set gp metric? - break; - - // =============================================================== - // This case writes to bpmem.triggerEFBCopy and may apparently prompt us to update glScissor() - // ------------------------ - case BPMEM_TRIGGER_EFB_COPY: - { - DVSTARTSUBPROFILE("LoadBPReg:swap"); - VertexManager::Flush(); - - ((u32*)&bpmem)[addr] = newval; - // The bottom right is within the rectangle - // The values in bpmem.copyTexSrcXY and bpmem.copyTexSrcWH are updated in case 0x49 and 0x4a in this function - TRectangle rc = { - (int)(bpmem.copyTexSrcXY.x), - (int)(bpmem.copyTexSrcXY.y), - (int)((bpmem.copyTexSrcXY.x + bpmem.copyTexSrcWH.x + 1)), - (int)((bpmem.copyTexSrcXY.y + bpmem.copyTexSrcWH.y + 1)) - }; - float MValueX = OpenGL_GetXmax(); - float MValueY = OpenGL_GetYmax(); - // Need another rc here to get it to scale. - // Here the bottom right is the out of the rectangle. - TRectangle multirc = { - (int)(bpmem.copyTexSrcXY.x * MValueX), - (int)(bpmem.copyTexSrcXY.y * MValueY), - (int)((bpmem.copyTexSrcXY.x * MValueX + (bpmem.copyTexSrcWH.x + 1) * MValueX)), - (int)((bpmem.copyTexSrcXY.y * MValueY + (bpmem.copyTexSrcWH.y + 1) * MValueY)) - }; - - UPE_Copy PE_copy; - PE_copy.Hex = bpmem.triggerEFBCopy; - - // -------------------------------------------------------- - // Check if we should copy the picture to XFB or not - // -------------------------- - if (PE_copy.copy_to_xfb == 0) - { - // EFB to texture - // for some reason it sets bpmem.zcontrol.pixel_format to PIXELFMT_Z24 every time a zbuffer format is given as a dest to GXSetTexCopyDst - if (g_Config.bEFBCopyDisable) - { - /* We already have this in Render.cpp that we call when (PE_copy.clear) is true. But we need a separate one - here because UpdateViewport() is not run when this otion is set? */ - glViewport(rc.left,rc.bottom, rc.right,rc.top); - glScissor(rc.left,rc.bottom, rc.right,rc.top); - // Logging - GLScissorX = rc.left; - GLScissorY = rc.bottom; - GLScissorW = rc.right; - GLScissorH = rc.top; - } - else if (g_Config.bCopyEFBToRAM) - { - TextureConverter::EncodeToRam(bpmem.copyTexDest<<5, bpmem.zcontrol.pixel_format==PIXELFMT_Z24, PE_copy.intensity_fmt>0, - (PE_copy.target_pixel_format/2)+((PE_copy.target_pixel_format&1)*8), PE_copy.half_scale>0, rc); - } - else - { - TextureMngr::CopyRenderTargetToTexture(bpmem.copyTexDest<<5, bpmem.zcontrol.pixel_format==PIXELFMT_Z24, PE_copy.intensity_fmt>0, - (PE_copy.target_pixel_format/2)+((PE_copy.target_pixel_format&1)*8), PE_copy.half_scale>0, &rc); - } - } - else - { - // EFB to XFB - if (g_Config.bUseXFB) - { - // the number of lines copied is determined by the y scale * source efb height - float yScale = bpmem.dispcopyyscale / 256.0f; - float xfbLines = bpmem.copyTexSrcWH.y + 1.0f * yScale; - u8* pXFB = Memory_GetPtr(bpmem.copyTexDest << 5); - u32 dstWidth = (bpmem.copyMipMapStrideChannels << 4); - u32 dstHeight = (u32)xfbLines; - XFB_Write(pXFB, multirc, dstWidth, dstHeight); - // FIXME: we draw XFB from here in DC mode. - // Bad hack since we can have multiple EFB to XFB copy before a draw. - // Plus we should use width and height from VI regs (see VI->Update()). - // Dixit donkopunchstania for the info. - //DebugLog("(EFB to XFB->XFB_Draw): ptr: %08x | %ix%i", (u32)pXFB, dstWidth, dstHeight); - if (g_VideoInitialize.bUseDualCore) - XFB_Draw(pXFB, dstWidth, dstHeight, 0); - } - else - { - Renderer::Swap(multirc); - } - g_VideoInitialize.pCopiedToXFB(); - } - - // -------------------------------------------------------- - // Clear the picture after it's done and submitted, to prepare for the next picture - // -------------------------- - if (PE_copy.clear) - { - // Clear color - Renderer::SetRenderMode(Renderer::RM_Normal); - // Clear Z-Buffer target - u32 nRestoreZBufferTarget = Renderer::GetZBufferTarget(); - - // ----------------------------------------------------------------- - // Update the view port for clearing the picture - // ----------------------- - glViewport(0, 0, Renderer::GetTargetWidth(), Renderer::GetTargetHeight()); - - // Always set the scissor in case it was set by the game and has not been reset - /* We will also do this at the end of this section, in SetScissorRect(), but that is for creating the new picture - this is for clearing the picture */ - glScissor(multirc.left, (Renderer::GetTargetHeight() - multirc.bottom), - (multirc.right - multirc.left), (multirc.bottom - multirc.top)); - // --------------------------- - - VertexShaderManager::SetViewportChanged(); - - // Since clear operations use the source rectangle, we have to do - // regular renders (glClear clears the entire buffer) - if (bpmem.blendmode.colorupdate || bpmem.blendmode.alphaupdate || bpmem.zmode.updateenable) - { - GLbitfield bits = 0; - if (bpmem.blendmode.colorupdate || bpmem.blendmode.alphaupdate) { - u32 clearColor = (bpmem.clearcolorAR << 16) | bpmem.clearcolorGB; - glClearColor(((clearColor>>16) & 0xff)*(1/255.0f), - - ((clearColor>>8 ) & 0xff)*(1/255.0f), - ((clearColor>>0 ) & 0xff)*(1/255.0f), - ((clearColor>>24) & 0xff)*(1/255.0f)); - bits |= GL_COLOR_BUFFER_BIT; - } - if (bpmem.zmode.updateenable) - { - glClearDepth((float)(bpmem.clearZValue&0xFFFFFF) / float(0xFFFFFF)); - bits |= GL_DEPTH_BUFFER_BIT; - } - if (nRestoreZBufferTarget ) - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); // don't clear ztarget here - glClear(bits); - } - - if (bpmem.zmode.updateenable && nRestoreZBufferTarget) { // have to clear the target zbuffer - glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT); - GL_REPORT_ERRORD(); - glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); - - // red should probably be the LSB - glClearColor(((bpmem.clearZValue>>0)&0xff)*(1/255.0f), - ((bpmem.clearZValue>>8)&0xff)*(1/255.0f), - ((bpmem.clearZValue>>16)&0xff)*(1/255.0f), 0); - glClear(GL_COLOR_BUFFER_BIT); - Renderer::SetColorMask(); - GL_REPORT_ERRORD(); - } - - if (nRestoreZBufferTarget) - { - // restore target - GLenum s_drawbuffers[2] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT}; - glDrawBuffers(2, s_drawbuffers); - } - - Renderer::SetScissorRect(); // reset the scissor rectangle - } - // ------------------------------ - } - break; - // ================================== - - case BPMEM_LOADTLUT: - { - DVSTARTSUBPROFILE("LoadBPReg:GXLoadTlut"); - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - - u32 tlutTMemAddr = (newval & 0x3FF) << 9; - u32 tlutXferCount = (newval & 0x1FFC00) >> 5; - - u8 *ptr = 0; - - // TODO - figure out a cleaner way. - if (g_VideoInitialize.bWii) - ptr = g_VideoInitialize.pGetMemoryPointer(bpmem.tlutXferSrc << 5); - else - ptr = g_VideoInitialize.pGetMemoryPointer((bpmem.tlutXferSrc & 0xFFFFF) << 5); - - if (ptr) - memcpy_gc(texMem + tlutTMemAddr, ptr, tlutXferCount); - else - PanicAlert("Invalid palette pointer %08x %08x %08x", bpmem.tlutXferSrc, bpmem.tlutXferSrc << 5, (bpmem.tlutXferSrc & 0xFFFFF)<< 5); - - // TODO(ector) : kill all textures that use this palette - // Not sure if it's a good idea, though. For now, we hash texture palettes - } - break; - - default: - switch(addr & 0xFC) //texture sampler filter - { - case 0x28: // tevorder 0-3 - case 0x2C: // tevorder 4-7 - if (changes) - { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetTevOrderChanged(addr - 0x28); - } - break; - - case 0x80: // TEX MODE 0 - case 0xA0: - if (changes) - { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - } - break; - case 0x84://TEX MODE 1 - case 0xA4: - break; - case 0x88://TEX IMAGE 0 - case 0xA8: - if (changes) - { - //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - } - break; - case 0x8C://TEX IMAGE 1 - case 0xAC: - if (changes) - { - //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - } - break; - case 0x90://TEX IMAGE 2 - case 0xB0: - if (changes) - { - //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - } - break; - case 0x94://TEX IMAGE 3 - case 0xB4: - if (changes) - { - //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - } - break; - case 0x98://TEX TLUT - case 0xB8: - if (changes) - { - //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - } - break; - case 0x9C://TEX UNKNOWN - case 0xBC: - //ERROR_LOG("texunknown%x = %x\n", addr, newval); - ((u32*)&bpmem)[addr] = newval; - break; - case 0xE0: - case 0xE4: - if (addr&1) { // don't compare with changes! - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - int num = (addr>>1)&0x3; - PixelShaderManager::SetColorChanged(bpmem.tevregs[num].high.type, num); - } - else - ((u32*)&bpmem)[addr] = newval; - break; - - default: - switch(addr&0xF0) { - case 0x10: // tevindirect 0-15 - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetTevIndirectChanged(addr-0x10); - } - break; - - case 0x30: - if (changes) { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetTexDimsChanged((addr>>1)&0x7); - } - break; - - case 0xC0: - case 0xD0: - if (changes) - { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - PixelShaderManager::SetTevCombinerChanged((addr&0x1f)/2); - } - break; - - case 0x20: - case 0x80: - case 0x90: - case 0xA0: - case 0xB0: - - // Just update the bpmem struct, don't do anything else - default: - if (changes) - { - VertexManager::Flush(); - ((u32*)&bpmem)[addr] = newval; - /*switch(addr) { - case 0x01: - case 0x02: - case 0x03: - case 0x04: break; // copy filter values - case 0x0f: break; // mask - case 0x27: break; // tev ind order - case 0x44: break; // field mask - case 0x45: break; // draw done - case 0x46: break; // clock - case 0x49: - case 0x4a: break; // copy tex src - case 0x4b: break; // copy tex dest - case 0x4d: break; // copyMipMapStrideChannels - case 0x4e: break; // disp copy scale - case 0x4f: break; // clear color - case 0x50: break; // clear color - case 0x51: break; // casez - case 0x52: break; // trigger efb copy - case 0x53: - case 0x54: break; // more copy filters - case 0x55: - case 0x56: break; // bounding box - case 0x64: - case 0x65: break; // tlut src dest - case 0xe8: break; // fog range - case 0xe9: - case 0xea: - case 0xeb: - case 0xec: - case 0xed: break; // fog - case 0xfe: break; // mask - default: - // 0x58 = 0xf - // 0x69 = 0x49e - ERROR_LOG("bp%.2x = %x\n", addr, newval); - }*/ - } - break; - } - break; - - } - break; - } -} - -// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg() -void LoadBPReg(u32 value0) -{ - DVSTARTPROFILE(); - // Handle the mask register - int opcode = value0 >> 24; - int oldval = ((u32*)&bpmem)[opcode]; - int newval = (oldval & ~bpmem.bpMask) | (value0 & bpmem.bpMask); - // Check if it's a new value - int changes = (oldval ^ newval) & 0xFFFFFF; - //reset the mask register - if (opcode != 0xFE) - bpmem.bpMask = 0xFFFFFF; - // Notify the video handling so it can update render states - BPWritten(opcode, changes, newval); -} - -// Called when loading a saved state. -// Needs more testing though. -void BPReload() -{ - for (int i = 0; i < 254; i++) - { - switch (i) { - - case 0x41: - case 0x45: //GXSetDrawDone - case 0x52: - case 0x65: - case 0x67: // set gp metric? - case BPMEM_PE_TOKEN_ID: - case BPMEM_PE_TOKEN_INT_ID: - // Cases in which we DON'T want to reload the BP - continue; - default: - BPWritten(i, 0xFFFFFF, ((u32*)&bpmem)[i]); - } - } -} +// 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/ + +// --------------------------------------------------------------------------------------- +// includes +// ------------- +#include "Globals.h" +#include "Profiler.h" +#include "Config.h" +#include "Statistics.h" + +#include "VertexLoader.h" +#include "VertexManager.h" + +#include "BPStructs.h" +#include "Render.h" +#include "OpcodeDecoding.h" +#include "TextureMngr.h" +#include "TextureDecoder.h" +#include "TextureConverter.h" +#include "VertexShaderManager.h" +#include "PixelShaderManager.h" +#include "XFB.h" +#include "main.h" + + +// --------------------------------------------------------------------------------------- +// State translation lookup tables +// ------------- + +static const GLenum glCmpFuncs[8] = { + GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS +}; + +static const GLenum glLogicOpCodes[16] = { + GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP, GL_XOR, + GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE, GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET +}; + +void BPInit() +{ + memset(&bpmem, 0, sizeof(bpmem)); + bpmem.bpMask = 0xFFFFFF; +} + +////////////////////////////////////////////////////////////////////////////////////// +// Write to bpmem +/* ------------------ + Called: + At the end of every: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg + TODO: + Turn into function table. The (future) DL jit can then call the functions directly, + getting rid of dynamic dispatch. Unfortunately, few games use DLs properly - most\ + just stuff geometry in them and don't put state changes there. +// ------------------ */ +void BPWritten(int addr, int changes, int newval) +{ + switch (addr) + { + case BPMEM_GENMODE: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PRIM_LOG("genmode: texgen=%d, col=%d, ms_en=%d, tev=%d, culmode=%d, ind=%d, zfeeze=%d\n", + bpmem.genMode.numtexgens, bpmem.genMode.numcolchans, + bpmem.genMode.ms_en, bpmem.genMode.numtevstages+1, bpmem.genMode.cullmode, + bpmem.genMode.numindstages, bpmem.genMode.zfreeze); + + // none, ccw, cw, ccw + if (bpmem.genMode.cullmode > 0) { + glEnable(GL_CULL_FACE); + glFrontFace(bpmem.genMode.cullmode == 2 ? GL_CCW : GL_CW); + } + else if (glIsEnabled(GL_CULL_FACE) == GL_TRUE) + glDisable(GL_CULL_FACE); + + PixelShaderManager::SetGenModeChanged(); + } + break; + + case BPMEM_IND_MTX+0: + case BPMEM_IND_MTX+1: + case BPMEM_IND_MTX+2: + case BPMEM_IND_MTX+3: + case BPMEM_IND_MTX+4: + case BPMEM_IND_MTX+5: + case BPMEM_IND_MTX+6: + case BPMEM_IND_MTX+7: + case BPMEM_IND_MTX+8: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetIndMatrixChanged((addr-BPMEM_IND_MTX)/3); + } + break; + case BPMEM_RAS1_SS0: + case BPMEM_RAS1_SS1: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetIndTexScaleChanged(); + } + break; + case BPMEM_ZMODE: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PRIM_LOG("zmode: test=%d, func=%d, upd=%d\n", bpmem.zmode.testenable, bpmem.zmode.func, + bpmem.zmode.updateenable); + + if (bpmem.zmode.testenable) { + glEnable(GL_DEPTH_TEST); + glDepthMask(bpmem.zmode.updateenable?GL_TRUE:GL_FALSE); + glDepthFunc(glCmpFuncs[bpmem.zmode.func]); + } + else { + // if the test is disabled write is disabled too + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + } + + if (!bpmem.zmode.updateenable) + Renderer::SetRenderMode(Renderer::RM_Normal); + } + break; + + case BPMEM_ALPHACOMPARE: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PRIM_LOG("alphacmp: ref0=%d, ref1=%d, comp0=%d, comp1=%d, logic=%d\n", bpmem.alphaFunc.ref0, + bpmem.alphaFunc.ref1, bpmem.alphaFunc.comp0, bpmem.alphaFunc.comp1, bpmem.alphaFunc.logic); + PixelShaderManager::SetAlpha(bpmem.alphaFunc); + } + break; + + case BPMEM_CONSTANTALPHA: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PRIM_LOG("constalpha: alp=%d, en=%d\n", bpmem.dstalpha.alpha, bpmem.dstalpha.enable); + SETSTAT(stats.dstAlphaEnable, bpmem.dstalpha.enable); + SETSTAT_UINT(stats.dstAlpha, bpmem.dstalpha.alpha); + Renderer::SetBlendMode(false); + } + break; + + case BPMEM_LINEPTWIDTH: + { + float fratio = xfregs.rawViewport[0] != 0 ? (float)Renderer::GetTargetWidth() / 640.0f : 1.0f; + if (bpmem.lineptwidth.linesize > 0) + glLineWidth((float)bpmem.lineptwidth.linesize * fratio / 6.0f); // scale by ratio of widths + if (bpmem.lineptwidth.pointsize > 0) + glPointSize((float)bpmem.lineptwidth.pointsize * fratio / 6.0f); + break; + } + + case BPMEM_PE_CONTROL: // GXSetZCompLoc, GXPixModeSync + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + } + break; + + case BPMEM_BLENDMODE: + if (changes & 0xFFFF) { + + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + + PRIM_LOG("blendmode: en=%d, open=%d, colupd=%d, alphaupd=%d, dst=%d, src=%d, sub=%d, mode=%d\n", + bpmem.blendmode.blendenable, bpmem.blendmode.logicopenable, bpmem.blendmode.colorupdate, bpmem.blendmode.alphaupdate, + bpmem.blendmode.dstfactor, bpmem.blendmode.srcfactor, bpmem.blendmode.subtract, bpmem.blendmode.logicmode); + + /* + Logic Operation Blend Modes + -------------------- + 0: GL_CLEAR + 1: GL_AND + 2: GL_AND_REVERSE + 3: GL_COPY [Super Smash. Bro. Melee, NES Zelda I, NES Zelda II] + 4: GL_AND_INVERTED + 5: GL_NOOP + 6: GL_XOR + 7: GL_OR [Zelda: TP] + 8: GL_NOR + 9: GL_EQUIV + 10: GL_INVERT + 11: GL_OR_REVERSE + 12: GL_COPY_INVERTED + 13: GL_OR_INVERTED + 14: GL_NAND + 15: GL_SET + */ + + // LogicOp Blending + if (changes & 2) { + SETSTAT(stats.logicOpMode, bpmem.blendmode.logicopenable != 0 ? bpmem.blendmode.logicmode : stats.logicOpMode); + if (bpmem.blendmode.logicopenable) + { + glEnable(GL_COLOR_LOGIC_OP); + // PanicAlert("Logic Op Blend : %i", bpmem.blendmode.logicmode); + glLogicOp(glLogicOpCodes[bpmem.blendmode.logicmode]); + } + else + glDisable(GL_COLOR_LOGIC_OP); + } + + // Dithering + if (changes & 4) { + SETSTAT(stats.dither, bpmem.blendmode.dither); + if (bpmem.blendmode.dither) glEnable(GL_DITHER); + else glDisable(GL_DITHER); + } + + // Blending + if (changes & 0xFE1) + { + SETSTAT(stats.srcFactor, bpmem.blendmode.srcfactor); + SETSTAT(stats.dstFactor, bpmem.blendmode.dstfactor); + Renderer::SetBlendMode(false); + } + + // Color Mask + if (changes & 0x18) + { + SETSTAT(stats.alphaUpdate, bpmem.blendmode.alphaupdate); + SETSTAT(stats.colorUpdate, bpmem.blendmode.colorupdate); + Renderer::SetColorMask(); + } + } + break; + + case BPMEM_FOGRANGE: + if (changes) { + // TODO(XK): Fog range format + //glFogi(GL_FOG_START, ... + //glFogi(GL_FOG_END, ... + } + break; + + case BPMEM_FOGPARAM0: + case BPMEM_FOGBEXPONENT: + case BPMEM_FOGBMAGNITUDE: + case BPMEM_FOGPARAM3: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetFogParamChanged(); + } + break; + + case BPMEM_FOGCOLOR: + if (changes) + { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetFogColorChanged(); + } + break; + + case BPMEM_TEXINVALIDATE: + break; + + case BPMEM_SCISSOROFFSET: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + Renderer::SetScissorRect(); + } + break; + + case BPMEM_SCISSORTL: + case BPMEM_SCISSORBR: + + if (changes) + { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + if (!Renderer::SetScissorRect()) + { + if (addr == BPMEM_SCISSORBR) { + ERROR_LOG("bad scissor!\n"); + } + } + } + break; + case BPMEM_ZTEX1: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PRIM_LOG("ztex bias=0x%x\n", bpmem.ztex1.bias); + PixelShaderManager::SetZTextureBias(bpmem.ztex1.bias); + } + break; + case BPMEM_ZTEX2: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + if (changes & 3) { + PixelShaderManager::SetZTextureTypeChanged(); + } +#if defined(_DEBUG) || defined(DEBUGFAST) + const char* pzop[] = {"DISABLE", "ADD", "REPLACE", "?"}; + const char* pztype[] = {"Z8", "Z16", "Z24", "?"}; + PRIM_LOG("ztex op=%s, type=%s\n", pzop[bpmem.ztex2.op], pztype[bpmem.ztex2.type]); +#endif + } + break; + + case 0xf6: // ksel0 + case 0xf7: // ksel1 + case 0xf8: // ksel2 + case 0xf9: // ksel3 + case 0xfa: // ksel4 + case 0xfb: // ksel5 + case 0xfc: // ksel6 + case 0xfd: // ksel7 + if (changes) + { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetTevKSelChanged(addr-0xf6); + } + break; + + case BPMEM_SETDRAWDONE: + VertexManager::Flush(); + switch (newval & 0xFF) + { + case 0x02: + g_VideoInitialize.pSetPEFinish(); // may generate interrupt + DebugLog("GXSetDrawDone SetPEFinish (value: 0x%02X)", (newval & 0xFFFF)); + break; + + default: + DebugLog("GXSetDrawDone ??? (value 0x%02X)", (newval & 0xFFFF)); + break; + } + break; + + case BPMEM_PE_TOKEN_ID: + g_VideoInitialize.pSetPEToken(static_cast(newval & 0xFFFF), FALSE); + DebugLog("SetPEToken 0x%04x", (newval & 0xFFFF)); + break; + + case BPMEM_PE_TOKEN_INT_ID: + g_VideoInitialize.pSetPEToken(static_cast(newval & 0xFFFF), TRUE); + DebugLog("SetPEToken + INT 0x%04x", (newval & 0xFFFF)); + break; + + case BPMEM_SETGPMETRIC: // Set gp metric? + break; + + // =============================================================== + // This case writes to bpmem.triggerEFBCopy and may apparently prompt us to update glScissor() + // ------------------------ + case BPMEM_TRIGGER_EFB_COPY: + { + DVSTARTSUBPROFILE("LoadBPReg:swap"); + VertexManager::Flush(); + + ((u32*)&bpmem)[addr] = newval; + // The bottom right is within the rectangle + // The values in bpmem.copyTexSrcXY and bpmem.copyTexSrcWH are updated in case 0x49 and 0x4a in this function + TRectangle rc = { + (int)(bpmem.copyTexSrcXY.x), + (int)(bpmem.copyTexSrcXY.y), + (int)((bpmem.copyTexSrcXY.x + bpmem.copyTexSrcWH.x + 1)), + (int)((bpmem.copyTexSrcXY.y + bpmem.copyTexSrcWH.y + 1)) + }; + float MValueX = OpenGL_GetXmax(); + float MValueY = OpenGL_GetYmax(); + // Need another rc here to get it to scale. + // Here the bottom right is the out of the rectangle. + TRectangle multirc = { + (int)(bpmem.copyTexSrcXY.x * MValueX), + (int)(bpmem.copyTexSrcXY.y * MValueY), + (int)((bpmem.copyTexSrcXY.x * MValueX + (bpmem.copyTexSrcWH.x + 1) * MValueX)), + (int)((bpmem.copyTexSrcXY.y * MValueY + (bpmem.copyTexSrcWH.y + 1) * MValueY)) + }; + + UPE_Copy PE_copy; + PE_copy.Hex = bpmem.triggerEFBCopy; + + // -------------------------------------------------------- + // Check if we should copy the picture to XFB or not + // -------------------------- + if (PE_copy.copy_to_xfb == 0) + { + // EFB to texture + // for some reason it sets bpmem.zcontrol.pixel_format to PIXELFMT_Z24 every time a zbuffer format is given as a dest to GXSetTexCopyDst + if (g_Config.bEFBCopyDisable) + { + /* We already have this in Render.cpp that we call when (PE_copy.clear) is true. But we need a separate one + here because UpdateViewport() is not run when this otion is set? */ + glViewport(rc.left,rc.bottom, rc.right,rc.top); + glScissor(rc.left,rc.bottom, rc.right,rc.top); + // Logging + GLScissorX = rc.left; + GLScissorY = rc.bottom; + GLScissorW = rc.right; + GLScissorH = rc.top; + } + else if (g_Config.bCopyEFBToRAM) + { + TextureConverter::EncodeToRam(bpmem.copyTexDest<<5, bpmem.zcontrol.pixel_format==PIXELFMT_Z24, PE_copy.intensity_fmt>0, + (PE_copy.target_pixel_format/2)+((PE_copy.target_pixel_format&1)*8), PE_copy.half_scale>0, rc); + } + else + { + TextureMngr::CopyRenderTargetToTexture(bpmem.copyTexDest<<5, bpmem.zcontrol.pixel_format==PIXELFMT_Z24, PE_copy.intensity_fmt>0, + (PE_copy.target_pixel_format/2)+((PE_copy.target_pixel_format&1)*8), PE_copy.half_scale>0, &rc); + } + } + else + { + // EFB to XFB + if (g_Config.bUseXFB) + { + // the number of lines copied is determined by the y scale * source efb height + float yScale = bpmem.dispcopyyscale / 256.0f; + float xfbLines = bpmem.copyTexSrcWH.y + 1.0f * yScale; + u8* pXFB = Memory_GetPtr(bpmem.copyTexDest << 5); + u32 dstWidth = (bpmem.copyMipMapStrideChannels << 4); + u32 dstHeight = (u32)xfbLines; + XFB_Write(pXFB, multirc, dstWidth, dstHeight); + // FIXME: we draw XFB from here in DC mode. + // Bad hack since we can have multiple EFB to XFB copy before a draw. + // Plus we should use width and height from VI regs (see VI->Update()). + // Dixit donkopunchstania for the info. + //DebugLog("(EFB to XFB->XFB_Draw): ptr: %08x | %ix%i", (u32)pXFB, dstWidth, dstHeight); + if (g_VideoInitialize.bUseDualCore) + XFB_Draw(pXFB, dstWidth, dstHeight, 0); + } + else + { + Renderer::Swap(multirc); + } + g_VideoInitialize.pCopiedToXFB(); + } + + // -------------------------------------------------------- + // Clear the picture after it's done and submitted, to prepare for the next picture + // -------------------------- + if (PE_copy.clear) + { + // Clear color + Renderer::SetRenderMode(Renderer::RM_Normal); + // Clear Z-Buffer target + u32 nRestoreZBufferTarget = Renderer::GetZBufferTarget(); + + // ----------------------------------------------------------------- + // Update the view port for clearing the picture + // ----------------------- + glViewport(0, 0, Renderer::GetTargetWidth(), Renderer::GetTargetHeight()); + + // Always set the scissor in case it was set by the game and has not been reset + /* We will also do this at the end of this section, in SetScissorRect(), but that is for creating the new picture + this is for clearing the picture */ + glScissor(multirc.left, (Renderer::GetTargetHeight() - multirc.bottom), + (multirc.right - multirc.left), (multirc.bottom - multirc.top)); + // --------------------------- + + VertexShaderManager::SetViewportChanged(); + + // Since clear operations use the source rectangle, we have to do + // regular renders (glClear clears the entire buffer) + if (bpmem.blendmode.colorupdate || bpmem.blendmode.alphaupdate || bpmem.zmode.updateenable) + { + GLbitfield bits = 0; + if (bpmem.blendmode.colorupdate || bpmem.blendmode.alphaupdate) + { + u32 clearColor = (bpmem.clearcolorAR << 16) | bpmem.clearcolorGB; + glClearColor(((clearColor>>16) & 0xff)*(1/255.0f), + ((clearColor>>8 ) & 0xff)*(1/255.0f), + ((clearColor>>0 ) & 0xff)*(1/255.0f), + ((clearColor>>24) & 0xff)*(1/255.0f)); + bits |= GL_COLOR_BUFFER_BIT; + } + if (bpmem.zmode.updateenable) + { + glClearDepth((float)(bpmem.clearZValue&0xFFFFFF) / float(0xFFFFFF)); + bits |= GL_DEPTH_BUFFER_BIT; + } + if (nRestoreZBufferTarget ) + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); // don't clear ztarget here + glClear(bits); + } + + // Have to clear the target zbuffer + if (bpmem.zmode.updateenable && nRestoreZBufferTarget) + { + glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT); + GL_REPORT_ERRORD(); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + // red should probably be the LSB + glClearColor(((bpmem.clearZValue>>0)&0xff)*(1/255.0f), + ((bpmem.clearZValue>>8)&0xff)*(1/255.0f), + ((bpmem.clearZValue>>16)&0xff)*(1/255.0f), 0); + glClear(GL_COLOR_BUFFER_BIT); + Renderer::SetColorMask(); + GL_REPORT_ERRORD(); + } + + if (nRestoreZBufferTarget) + { + // restore target + GLenum s_drawbuffers[2] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT}; + glDrawBuffers(2, s_drawbuffers); + } + + Renderer::SetScissorRect(); // reset the scissor rectangle + } + // ------------------------------ + } + break; + // ================================== + + case BPMEM_LOADTLUT: + { + DVSTARTSUBPROFILE("LoadBPReg:GXLoadTlut"); + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + + u32 tlutTMemAddr = (newval & 0x3FF) << 9; + u32 tlutXferCount = (newval & 0x1FFC00) >> 5; + + u8 *ptr = 0; + + // TODO - figure out a cleaner way. + if (g_VideoInitialize.bWii) + ptr = g_VideoInitialize.pGetMemoryPointer(bpmem.tlutXferSrc << 5); + else + ptr = g_VideoInitialize.pGetMemoryPointer((bpmem.tlutXferSrc & 0xFFFFF) << 5); + + if (ptr) + memcpy_gc(texMem + tlutTMemAddr, ptr, tlutXferCount); + else + PanicAlert("Invalid palette pointer %08x %08x %08x", bpmem.tlutXferSrc, bpmem.tlutXferSrc << 5, (bpmem.tlutXferSrc & 0xFFFFF)<< 5); + + // TODO(ector) : kill all textures that use this palette + // Not sure if it's a good idea, though. For now, we hash texture palettes + } + break; + + default: + switch(addr & 0xFC) //texture sampler filter + { + case 0x28: // tevorder 0-3 + case 0x2C: // tevorder 4-7 + if (changes) + { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetTevOrderChanged(addr - 0x28); + } + break; + + case 0x80: // TEX MODE 0 + case 0xA0: + if (changes) + { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + } + break; + case 0x84://TEX MODE 1 + case 0xA4: + break; + case 0x88://TEX IMAGE 0 + case 0xA8: + if (changes) + { + //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + } + break; + case 0x8C://TEX IMAGE 1 + case 0xAC: + if (changes) + { + //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + } + break; + case 0x90://TEX IMAGE 2 + case 0xB0: + if (changes) + { + //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + } + break; + case 0x94://TEX IMAGE 3 + case 0xB4: + if (changes) + { + //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + } + break; + case 0x98://TEX TLUT + case 0xB8: + if (changes) + { + //textureChanged[((addr&0xE0)==0xA0)*4+(addr&3)] = true; + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + } + break; + case 0x9C://TEX UNKNOWN + case 0xBC: + //ERROR_LOG("texunknown%x = %x\n", addr, newval); + ((u32*)&bpmem)[addr] = newval; + break; + case 0xE0: + case 0xE4: + if (addr&1) { // don't compare with changes! + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + int num = (addr>>1)&0x3; + PixelShaderManager::SetColorChanged(bpmem.tevregs[num].high.type, num); + } + else + ((u32*)&bpmem)[addr] = newval; + break; + + default: + switch(addr&0xF0) { + case 0x10: // tevindirect 0-15 + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetTevIndirectChanged(addr-0x10); + } + break; + + case 0x30: + if (changes) { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetTexDimsChanged((addr>>1)&0x7); + } + break; + + case 0xC0: + case 0xD0: + if (changes) + { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + PixelShaderManager::SetTevCombinerChanged((addr&0x1f)/2); + } + break; + + case 0x20: + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + + // Just update the bpmem struct, don't do anything else + default: + if (changes) + { + VertexManager::Flush(); + ((u32*)&bpmem)[addr] = newval; + /*switch(addr) { + case 0x01: + case 0x02: + case 0x03: + case 0x04: break; // copy filter values + case 0x0f: break; // mask + case 0x27: break; // tev ind order + case 0x44: break; // field mask + case 0x45: break; // draw done + case 0x46: break; // clock + case 0x49: + case 0x4a: break; // copy tex src + case 0x4b: break; // copy tex dest + case 0x4d: break; // copyMipMapStrideChannels + case 0x4e: break; // disp copy scale + case 0x4f: break; // clear color + case 0x50: break; // clear color + case 0x51: break; // casez + case 0x52: break; // trigger efb copy + case 0x53: + case 0x54: break; // more copy filters + case 0x55: + case 0x56: break; // bounding box + case 0x64: + case 0x65: break; // tlut src dest + case 0xe8: break; // fog range + case 0xe9: + case 0xea: + case 0xeb: + case 0xec: + case 0xed: break; // fog + case 0xfe: break; // mask + default: + // 0x58 = 0xf + // 0x69 = 0x49e + ERROR_LOG("bp%.2x = %x\n", addr, newval); + }*/ + } + break; + } + break; + + } + break; + } +} + +// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg() +void LoadBPReg(u32 value0) +{ + DVSTARTPROFILE(); + // Handle the mask register + int opcode = value0 >> 24; + int oldval = ((u32*)&bpmem)[opcode]; + int newval = (oldval & ~bpmem.bpMask) | (value0 & bpmem.bpMask); + // Check if it's a new value + int changes = (oldval ^ newval) & 0xFFFFFF; + //reset the mask register + if (opcode != 0xFE) + bpmem.bpMask = 0xFFFFFF; + // Notify the video handling so it can update render states + BPWritten(opcode, changes, newval); +} + +// Called when loading a saved state. +// Needs more testing though. +void BPReload() +{ + for (int i = 0; i < 254; i++) + { + switch (i) { + + case 0x41: + case 0x45: //GXSetDrawDone + case 0x52: + case 0x65: + case 0x67: // set gp metric? + case BPMEM_PE_TOKEN_ID: + case BPMEM_PE_TOKEN_INT_ID: + // Cases in which we DON'T want to reload the BP + continue; + default: + BPWritten(i, 0xFFFFFF, ((u32*)&bpmem)[i]); + } + } +} diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp index 510463e393..158cc6b657 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp @@ -1,1274 +1,1335 @@ -// 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 "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 "OnScreenDisplay.h" -#include "Timer.h" - -#include "main.h" // Local -#ifdef _WIN32 -#include "OS/Win32.h" -#endif - -#ifdef _WIN32 - #include "Win32Window.h" // warning: crapcode -#else -#endif - - -CGcontext g_cgcontext; -CGprofile g_cgvProf; -CGprofile g_cgfProf; - -RasterFont* s_pfont = NULL; - -static bool s_bFullscreen = false; - -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; - int numvertexattribs = 0; - 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 (GLEW_EXT_blend_func_separate && GLEW_EXT_blend_equation_separate) - g_bBlendSeparate = true; - - //Checks if it ONLY has the ATI_draw_buffers extension, some have both - if (GLEW_ATI_draw_buffers && !GLEW_ARB_draw_buffers) - s_bATIDrawBuffers = true; - - s_bFullscreen = g_Config.bFullscreen; - - if (glewInit() != GLEW_OK) { - ERROR_LOG("glewInit() failed!\nDoes your video card support OpenGL 2.x?"); - return false; - } - - if (!GLEW_EXT_framebuffer_object) { - ERROR_LOG("*********\nGPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets\nGPU: *********\nDoes your video card support OpenGL 2.x?"); - bSuccess = false; - } - - if (!GLEW_EXT_secondary_color) { - ERROR_LOG("*********\nGPU: OGL ERROR: Need GL_EXT_secondary_color\nGPU: *********\nDoes your video card support OpenGL 2.x?"); - bSuccess = false; - } - - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, (GLint *)&numvertexattribs); - - if (numvertexattribs < 11) { - ERROR_LOG("*********\nGPU: OGL ERROR: Number of attributes %d not enough\nGPU: *********\nDoes your video card support OpenGL 2.x?", 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)\nDoes your video card support OpenGL 2.x?"); -#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\nDoes your video card support OpenGL 2.x?"); - } - - _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); - - // Setup the texture params - // 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); - - // Why is this left here? - //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; -} - - -////////////////////////////////////////////////////////////////////////////////////// -// 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 = (float)bpmem.scissorTL.x - xoff - 342; // left = 0 - rc_left *= MValueX; - if (rc_left < 0) rc_left = 0; - - float rc_top = (float)bpmem.scissorTL.y - yoff - 342; // right = 0 - rc_top *= MValueY; - if (rc_top < 0) rc_top = 0; - - float rc_right = (float)bpmem.scissorBR.x - xoff - 341; // right = 640 - rc_right *= MValueX; - if (rc_right > 640 * MValueX) rc_right = 640 * MValueX; - - float rc_bottom = (float)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) - { - glScissor( - (int)rc_left, // x = 0 for example - Renderer::GetTargetHeight() - (int)(rc_bottom), // y = 0 for example - (int)(rc_right-rc_left), // width = 640 for example - (int)(rc_bottom-rc_top) // 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; -} - - -////////////////////////////////////////////////////////////////////////////////////// -// This function has the final picture if the XFB functions are not used. We adjust the aspect ratio -// here. -// ---------------------- -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 - // Blit the FBO to do Anti-Aliasing - // 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 variables - // ------------------ - /* Work with float values for the XFB supplement and aspect ratio functions. These are default - values that are used if the XFB supplement and the keep aspect ratio function are unused */ - float FloatGLWidth = (float)OpenGL_GetWidth(); - float FloatGLHeight = (float)OpenGL_GetHeight(); - float FloatXOffset = 0, FloatYOffset = 0; - // ------------------------------------- - - - // ----------------------------------------------------------------------- - // XFB supplement, fix the black borders problem - // ------------------ - /* I'm limiting it to the stretch to fit option because I don't know how the other mode works. The reason - I don't allow this option together with UseXFB is that they are supplements and the XFB function - should be able to produce the same result */ - if(g_Config.bStretchToFit && !g_Config.bUseXFB) - { - // The rendering window size - float WinWidth = (float)OpenGL_GetWidth(); - float WinHeight = (float)OpenGL_GetHeight(); - - // The fraction of the screen that the image occupies - // Rc.right and rc.bottom is the original picture pixel size - /* There is a +1 in Rc (earlier called multirc, the input to this function), but these - adjustments seems to work better without it. */ - float WidthRatio = (float)(rc.right - 1) / 640.0f; - float HeightRatio = (float)(rc.bottom - 1) / 480.0f; - - // The pixel size of the image on the screen, adjusted for the actual window size - float OldWidth = WidthRatio * (float)WinWidth; - float OldHeight = HeightRatio * (float)WinHeight; - - // The adjusted width and height - FloatGLWidth = ceil((float)WinWidth / WidthRatio); - FloatGLHeight = ceil((float)WinHeight / HeightRatio); - - // The width and height deficit in actual pixels - float WidthDeficit = (float)WinWidth - OldWidth; - float HeightDeficit = (float)WinHeight - OldHeight; - - // The picture will be drawn from the bottom so we need this YOffset - // The X-axis needs no adjustment because the picture begins from the left - FloatYOffset = -HeightDeficit / HeightRatio; - - // ----------------------------------------------------------------------- - // Logging - // ------------------ - /* - Console::ClearScreen(); - Console::Print("Bpmem L:%i T:%i X:%i Y:%i\n", bpmem.copyTexSrcXY.x, bpmem.copyTexSrcXY.y, bpmem.copyTexSrcWH.x, bpmem.copyTexSrcWH.y); - Console::Print("Config Left:%i Top:%i Width:%i Height:%i\n", g_Config.iScreenLeft, g_Config.iScreenTop, g_Config.iScreenWidth, g_Config.iScreenHeight); - Console::Print("Input Left:%i Top:%i Right:%i Bottom:%i\n", rc.left, rc.top, rc.right, rc.bottom); - Console::Print("Old picture: Width[%1.2f]:%4.0f Height[%1.2f]:%4.0f\n", WidthRatio, OldWidth, HeightRatio, OldHeight); - Console::Print("New picture: Width[%1.2f]:%4.0f Height[%1.2f]:%4.0f YOffset:%4.0f YDeficit:%4.0f\n", WidthRatio, WinWidth, HeightRatio, WinHeight, FloatYOffset, HeightDeficit); - Console::Print("----------------------------------------------------------------\n"); - */ - // ------------------------------ - } - // ------------------------------ - - - - // ----------------------------------------------------------------------- - /* Keep aspect ratio at 4:3. This may be interesting if you for example have a 5:4 screen but don't like - the stretching, and would rather have a letterbox. */ - // Output: GLWidth, GLHeight, XOffset, YOffset - // ------------------ - - // The rendering window size - float WinWidth = (float)OpenGL_GetWidth(); - float WinHeight = (float)OpenGL_GetHeight(); - // The rendering window aspect ratio as a fraction of the 4:3 ratio - float Ratio = WinWidth / WinHeight / (4.0f / 3.0f); - float wAdj, hAdj; - // Actual pixel size of the picture after adjustment - float PictureWidth = WinWidth, PictureHeight = WinHeight; - - // This function currently only works together with the Stretch To Fit option - if (g_Config.bKeepAR && g_Config.bStretchToFit) - { - // 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) - { - // ------------------------------------------------ - // Calculate the new width and height for glViewport, this is not the actual size of either the picture or the screen - // ---------------- - wAdj = Ratio; - hAdj = 1.0; - FloatGLWidth = FloatGLWidth / wAdj; - FloatGLHeight = FloatGLHeight / hAdj; - // -------------------- - - // ------------------------------------------------ - // Calculate the new X offset - // ---------------- - // The picture width - PictureWidth = WinWidth / Ratio; - // Move the left of the picture to the middle of the screen - FloatXOffset = FloatXOffset + WinWidth / 2.0f; - // Then remove half the picture height to move it to the horizontal center - FloatXOffset = FloatXOffset - PictureWidth / 2.0f; - // -------------------- - } - // The window is to high, we have to limit the height - else - { - // ------------------------------------------------ - // Calculate the new width and height for glViewport, this is not the actual size of either the picture or the screen - // ---------------- - // Invert the ratio to make it > 1 - Ratio = 1.0f / Ratio; - wAdj = 1.0f; - hAdj = Ratio; - FloatGLWidth = FloatGLWidth / wAdj; - FloatGLHeight = FloatGLHeight / hAdj; - // -------------------- - - // ------------------------------------------------ - // Calculate the new Y offset - // ---------------- - // The picture height - PictureHeight = WinHeight / Ratio; - // Keep the picture on the bottom of the screen, this is needed because YOffset may not be 0 here - FloatYOffset = FloatYOffset / hAdj; - // Move the bottom of the picture to the middle of the screen - FloatYOffset = FloatYOffset + WinHeight / 2.0f; - // Then remove half the picture height to move it to the vertical center - FloatYOffset = FloatYOffset - PictureHeight / 2.0f; - // -------------------- - } - - // ----------------------------------------------------------------------- - // Logging - // ------------------ - /* - Console::Print("Screen Width:%4.0f Height:%4.0f Ratio:%1.2f\n", WinWidth, WinHeight, Ratio); - Console::Print("GL Width:%4.1f Height:%4.1f\n", FloatGLWidth, FloatGLHeight); - Console::Print("Picture Width:%4.1f Height:%4.1f YOffset:%4.0f\n", PictureWidth, PictureHeight, FloatYOffset); - Console::Print("----------------------------------------------------------------\n"); - */ - // ------------------------------ - } - // ------------------------------------- - - // ----------------------------------------------------------------------- - /* Adjustments to - FloatGLWidth - FloatGLHeight - XOffset - YOffset - are done. Calculate the new new windows width and height. */ - // -------------------- - int GLx = OpenGL_GetXoff(); // These two are zero - int GLy = OpenGL_GetYoff(); - int GLWidth = ceil(FloatGLWidth); - int GLHeight = ceil(FloatGLHeight); - - // Because there is no round() function we use round(float) = floor(float + 0.5) instead - int YOffset = floor(FloatYOffset + 0.5); - int XOffset = floor(FloatXOffset+ 0.5); - // ------------------------------------- - - - // Update GLViewPort - glViewport( - GLx + XOffset, - GLy + YOffset, - GLWidth, - GLHeight - ); - - // Reset GL state - 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(); - - // Wireframe - 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, "FPS: %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); - - OSD::DrawMessages(); - -#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::ProgressiveCleanup(); - - 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 - } - } -} - -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); -} - -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]); -} - -////////////////////////////////////////////////////////////////////////////////////// -// Function: This function does not have the final picture. Use Renderer::Swap() to adjust the final picture. -// Call schedule: Called from VertexShaderManager -// ---------------------- -void UpdateViewport() -{ - // ----------------------------------------------------------------------- - // Logging - // ------------------ - // 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);*/ - // -------------------------- - - - // ----------------------------------------------------------------------- - // GLViewPort variables - // ------------------ - int GLWidth, GLHeight, GLx, GLy; - float FloatGLWidth = fabs(2 * xfregs.rawViewport[0]); - float FloatGLHeight = fabs(2 * xfregs.rawViewport[1]); - - // rawViewport[0] = 320, rawViewport[1] = -240 - int scissorXOff = bpmem.scissorOffset.x * 2 - 342; - int scissorYOff = bpmem.scissorOffset.y * 2 - 342; - // ------------------------------------- - - - // ----------------------------------------------------------------------- - // Stretch picture while keeping the native resolution - // ------------------ - if (g_Config.bStretchToFit) - { - GLx = (int)(xfregs.rawViewport[3] - xfregs.rawViewport[0] - 342 - scissorXOff); - GLy = Renderer::GetTargetHeight() - ((int)(xfregs.rawViewport[4] - xfregs.rawViewport[1] - 342 - scissorYOff)); - // Round up to the nearest integer - GLWidth = (int)ceil(FloatGLWidth); - GLHeight = (int)ceil(FloatGLHeight); - } - // ----------------------------------------------------------------------- - // Stretch picture with increased internal resolution - // ------------------ - else - { - float MValueX = OpenGL_GetXmax(); - float MValueY = OpenGL_GetYmax(); - - GLx = (int)ceil((xfregs.rawViewport[3] - xfregs.rawViewport[0] - 342 - scissorXOff) * MValueX); - GLy = (int)ceil(Renderer::GetTargetHeight() - ((int)(xfregs.rawViewport[4] - xfregs.rawViewport[1] - 342 - scissorYOff)) * MValueY); - GLWidth = (int)ceil(abs((int)(2 * xfregs.rawViewport[0])) * MValueX); - GLHeight = (int)ceil(abs((int)(2 * xfregs.rawViewport[1])) * MValueY); - } - // ------------------------------------- - - - // Update the view port - 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"); - */ -} +// 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 "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 "OnScreenDisplay.h" +#include "Timer.h" + +#include "main.h" // Local +#ifdef _WIN32 +#include "OS/Win32.h" +#endif + +#ifdef _WIN32 + #include "Win32Window.h" // warning: crapcode +#else +#endif + + +CGcontext g_cgcontext; +CGprofile g_cgvProf; +CGprofile g_cgfProf; + +RasterFont* s_pfont = NULL; + +static bool s_bFullscreen = false; + +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; + int numvertexattribs = 0; + 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 (GLEW_EXT_blend_func_separate && GLEW_EXT_blend_equation_separate) + g_bBlendSeparate = true; + + //Checks if it ONLY has the ATI_draw_buffers extension, some have both + if (GLEW_ATI_draw_buffers && !GLEW_ARB_draw_buffers) + s_bATIDrawBuffers = true; + + s_bFullscreen = g_Config.bFullscreen; + + if (glewInit() != GLEW_OK) { + ERROR_LOG("glewInit() failed!\nDoes your video card support OpenGL 2.x?"); + return false; + } + + if (!GLEW_EXT_framebuffer_object) { + ERROR_LOG("*********\nGPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets\nGPU: *********\nDoes your video card support OpenGL 2.x?"); + bSuccess = false; + } + + if (!GLEW_EXT_secondary_color) { + ERROR_LOG("*********\nGPU: OGL ERROR: Need GL_EXT_secondary_color\nGPU: *********\nDoes your video card support OpenGL 2.x?"); + bSuccess = false; + } + + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, (GLint *)&numvertexattribs); + + if (numvertexattribs < 11) { + ERROR_LOG("*********\nGPU: OGL ERROR: Number of attributes %d not enough\nGPU: *********\nDoes your video card support OpenGL 2.x?", 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)\nDoes your video card support OpenGL 2.x?"); +#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\nDoes your video card support OpenGL 2.x?"); + } + + _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); + + // Setup the texture params + // 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); + + // Why is this left here? + //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; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// 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 = (float)bpmem.scissorTL.x - xoff - 342; // left = 0 + rc_left *= MValueX; + if (rc_left < 0) rc_left = 0; + + float rc_top = (float)bpmem.scissorTL.y - yoff - 342; // right = 0 + rc_top *= MValueY; + if (rc_top < 0) rc_top = 0; + + float rc_right = (float)bpmem.scissorBR.x - xoff - 341; // right = 640 + rc_right *= MValueX; + if (rc_right > 640 * MValueX) rc_right = 640 * MValueX; + + float rc_bottom = (float)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) + { + glScissor( + (int)rc_left, // x = 0 for example + Renderer::GetTargetHeight() - (int)(rc_bottom), // y = 0 for example + (int)(rc_right-rc_left), // width = 640 for example + (int)(rc_bottom-rc_top) // 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; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// This function has the final picture if the XFB functions are not used. We adjust the aspect ratio here. +// ---------------------- +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 + // Blit the FBO to do Anti-Aliasing + // 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 variables + // ------------------ + /* Work with float values for the XFB supplement and aspect ratio functions. These are default + values that are used if the XFB supplement and the keep aspect ratio function are unused */ + float FloatGLWidth = (float)OpenGL_GetWidth(); + float FloatGLHeight = (float)OpenGL_GetHeight(); + float FloatXOffset = 0, FloatYOffset = 0; + // ------------------------------------- + + + // ----------------------------------------------------------------------- + // XFB supplement, fix the black borders problem + // ------------------ + /* I'm limiting it to the stretch to fit option because I don't know how the other mode works. The reason + I don't allow this option together with UseXFB is that they are supplements and the XFB function + should be able to produce the same result */ + if(g_Config.bStretchToFit && !g_Config.bUseXFB) + { + // The rendering window size + float WinWidth = (float)OpenGL_GetWidth(); + float WinHeight = (float)OpenGL_GetHeight(); + + // The fraction of the screen that the image occupies + // Rc.right and rc.bottom is the original picture pixel size + /* There is a +1 in Rc (earlier called multirc, the input to this function), but these + adjustments seems to work better without it. */ + float WidthRatio = (float)(rc.right - 1) / 640.0f; + float HeightRatio = (float)(rc.bottom - 1) / 480.0f; + + // The pixel size of the image on the screen, adjusted for the actual window size + float OldWidth = WidthRatio * (float)WinWidth; + float OldHeight = HeightRatio * (float)WinHeight; + + // The adjusted width and height + FloatGLWidth = ceil((float)WinWidth / WidthRatio); + FloatGLHeight = ceil((float)WinHeight / HeightRatio); + + // The width and height deficit in actual pixels + float WidthDeficit = (float)WinWidth - OldWidth; + float HeightDeficit = (float)WinHeight - OldHeight; + + // The picture will be drawn from the bottom so we need this YOffset + // The X-axis needs no adjustment because the picture begins from the left + FloatYOffset = -HeightDeficit / HeightRatio; + + // ----------------------------------------------------------------------- + // Logging + // ------------------ + /* + Console::ClearScreen(); + Console::Print("Bpmem L:%i T:%i X:%i Y:%i\n", bpmem.copyTexSrcXY.x, bpmem.copyTexSrcXY.y, bpmem.copyTexSrcWH.x, bpmem.copyTexSrcWH.y); + Console::Print("Input Left:%i Top:%i Right:%i Bottom:%i\n", rc.left, rc.top, rc.right, rc.bottom); + Console::Print("Old picture: Width[%1.2f]:%4.0f Height[%1.2f]:%4.0f\n", WidthRatio, OldWidth, HeightRatio, OldHeight); + Console::Print("New picture: Width[%1.2f]:%4.0f Height[%1.2f]:%4.0f YOffset:%4.0f YDeficit:%4.0f\n", WidthRatio, WinWidth, HeightRatio, WinHeight, FloatYOffset, HeightDeficit); + Console::Print("----------------------------------------------------------------\n"); + */ + // ------------------------------ + } + // ------------------------------ + + + + // ----------------------------------------------------------------------- + /* Keep aspect ratio at 4:3. This may be interesting if you for example have a 5:4 screen but don't like + the stretching, and would rather have a letterbox. */ + // Output: GLWidth, GLHeight, XOffset, YOffset + // ------------------ + + // The rendering window size + float WinWidth = (float)OpenGL_GetWidth(); + float WinHeight = (float)OpenGL_GetHeight(); + // The rendering window aspect ratio as a fraction of the 4:3 ratio + float Ratio = WinWidth / WinHeight / (4.0f / 3.0f); + float wAdj, hAdj; + // Actual pixel size of the picture after adjustment + float PictureWidth = WinWidth, PictureHeight = WinHeight; + + // This function currently only works together with the Stretch To Fit option + if (g_Config.bKeepAR && g_Config.bStretchToFit) + { + // 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) + { + // ------------------------------------------------ + // Calculate the new width and height for glViewport, this is not the actual size of either the picture or the screen + // ---------------- + wAdj = Ratio; + hAdj = 1.0; + FloatGLWidth = FloatGLWidth / wAdj; + FloatGLHeight = FloatGLHeight / hAdj; + // -------------------- + + // ------------------------------------------------ + // Calculate the new X offset + // ---------------- + // The picture width + PictureWidth = WinWidth / Ratio; + // Move the left of the picture to the middle of the screen + FloatXOffset = FloatXOffset + WinWidth / 2.0f; + // Then remove half the picture height to move it to the horizontal center + FloatXOffset = FloatXOffset - PictureWidth / 2.0f; + // -------------------- + } + // The window is to high, we have to limit the height + else + { + // ------------------------------------------------ + // Calculate the new width and height for glViewport, this is not the actual size of either the picture or the screen + // ---------------- + // Invert the ratio to make it > 1 + Ratio = 1.0f / Ratio; + wAdj = 1.0f; + hAdj = Ratio; + FloatGLWidth = FloatGLWidth / wAdj; + FloatGLHeight = FloatGLHeight / hAdj; + // -------------------- + + // ------------------------------------------------ + // Calculate the new Y offset + // ---------------- + // The picture height + PictureHeight = WinHeight / Ratio; + // Keep the picture on the bottom of the screen, this is needed because YOffset may not be 0 here + FloatYOffset = FloatYOffset / hAdj; + // Move the bottom of the picture to the middle of the screen + FloatYOffset = FloatYOffset + WinHeight / 2.0f; + // Then remove half the picture height to move it to the vertical center + FloatYOffset = FloatYOffset - PictureHeight / 2.0f; + // -------------------- + } + + // ----------------------------------------------------------------------- + // Logging + // ------------------ + /* + Console::Print("Screen Width:%4.0f Height:%4.0f Ratio:%1.2f\n", WinWidth, WinHeight, Ratio); + Console::Print("GL Width:%4.1f Height:%4.1f\n", FloatGLWidth, FloatGLHeight); + Console::Print("Picture Width:%4.1f Height:%4.1f YOffset:%4.0f\n", PictureWidth, PictureHeight, FloatYOffset); + Console::Print("----------------------------------------------------------------\n"); + */ + // ------------------------------ + } + // ------------------------------------- + + // ----------------------------------------------------------------------- + /* Adjustments to + FloatGLWidth + FloatGLHeight + XOffset + YOffset + are done. Calculate the new new windows width and height. */ + // -------------------- + int GLx = OpenGL_GetXoff(); // These two are zero + int GLy = OpenGL_GetYoff(); + int GLWidth = ceil(FloatGLWidth); + int GLHeight = ceil(FloatGLHeight); + + // Because there is no round() function we use round(float) = floor(float + 0.5) instead + int YOffset = floor(FloatYOffset + 0.5); + int XOffset = floor(FloatXOffset+ 0.5); + // ------------------------------------- + + + // Update GLViewPort + glViewport( + GLx + XOffset, + GLy + YOffset, + GLWidth, + GLHeight + ); + + // ----------------------------------------------------------------------- + // Show the finished picture + // -------------------- + // Reset GL state + 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(); + + // Place the texture + 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(); + + // Wireframe + if (g_Config.bWireFrame) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + TextureMngr::DisableStage(0); + // ------------------------------------- + + + // ----------------------------------------------------------------------- + /* Blacken out the borders in the 4:3 or the coming 16:9 aspect ratio modes. Somewhere in BPStructs + 0x52 or elsewhere the area outside the actual picture, that we now show with the aspect ratio option + has been filled with either for example white, or have copies of old renderings on it. So we replace + that with blacknes. */ + // -------------------- + if(g_Config.bKeepAR) + { + // Set current drawing color to red + glColor3f(0.0, 0.0, 0.0); // Black + + /* This doesn't work + glRecti, glRectf( + (float)Left, + (float)Right, + (float)Top, + (float)Bottom); */ + + /* The glVertex3f() coordinates are: + Top: 1 to -1 from top to bottom + Left: 1 to -1 from right to left + Height and width is therefore 2.0, zero is the center of the screen + */ + // The fraction of the screen that the image occupies + /* It's hard to make a border that always exactly begin where the screen ends. The ( -1) here will + sometimes hide one pixel to many, but if we use (- 0) we will on the other hand sometimes show + one line of pixels that we should not show. But a -0.5 adjustment seemed just right, it never hid + one pixel to much, and never one to little. So I settle with a -0.5 adjustment here. */ + float HeightRatio = (((float)(rc.bottom)) - 0.5) / 480.0f; + + // Bottom border + float FLeft = 1.0, + FWidth = -2.0, + FHeight = 2.0 * (1.0 - HeightRatio), + FTop = -1.0 + FHeight; + + glBegin(GL_POLYGON); + glVertex3f (FLeft, FTop - FHeight, 0.0); + glVertex3f (FLeft + FWidth, FTop - FHeight, 0.0); + glVertex3f (FLeft + FWidth, FTop, 0.0); + glVertex3f (FLeft, FTop, 0.0); + glEnd(); + //Console::Print("Border Left:%1.3f Top:%1.3f Width:%1.3f Height:%1.3f\n", FLeft, FTop, FWidth, FHeight); + } + // ------------------------------------- + + + // Place messages on the picture, then copy it to the screen + 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()); +} +//////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// We can now draw whatever we want on top of the picture. Then we copy the final picture to the output. +// ---------------------- +void Renderer::SwapBuffers() +{ + // ----------------------------------------------------------------------- + /* Draw messages on the screen */ + // -------------------- + 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, "FPS: %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); + + OSD::DrawMessages(); + // ----------------------------- + + +#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::ProgressiveCleanup(); + + 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 + } + } +} + +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); +} + +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]); +} + +////////////////////////////////////////////////////////////////////////////////////// +// Function: This function does not have the final picture. Use Renderer::Swap() to adjust the final picture. +// Call schedule: Called from VertexShaderManager +// ---------------------- +void UpdateViewport() +{ + // ----------------------------------------------------------------------- + // Logging + // ------------------ + // 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);*/ + // -------------------------- + + + // ----------------------------------------------------------------------- + // GLViewPort variables + // ------------------ + int GLWidth, GLHeight, GLx, GLy; + float FloatGLWidth = fabs(2 * xfregs.rawViewport[0]); + float FloatGLHeight = fabs(2 * xfregs.rawViewport[1]); + + // rawViewport[0] = 320, rawViewport[1] = -240 + int scissorXOff = bpmem.scissorOffset.x * 2 - 342; + int scissorYOff = bpmem.scissorOffset.y * 2 - 342; + // ------------------------------------- + + + // ----------------------------------------------------------------------- + // Stretch picture while keeping the native resolution + // ------------------ + if (g_Config.bStretchToFit) + { + GLx = (int)(xfregs.rawViewport[3] - xfregs.rawViewport[0] - 342 - scissorXOff); + GLy = Renderer::GetTargetHeight() - ((int)(xfregs.rawViewport[4] - xfregs.rawViewport[1] - 342 - scissorYOff)); + // Round up to the nearest integer + GLWidth = (int)ceil(FloatGLWidth); + GLHeight = (int)ceil(FloatGLHeight); + } + // ----------------------------------------------------------------------- + // Stretch picture with increased internal resolution + // ------------------ + else + { + float MValueX = OpenGL_GetXmax(); + float MValueY = OpenGL_GetYmax(); + + GLx = (int)ceil((xfregs.rawViewport[3] - xfregs.rawViewport[0] - 342 - scissorXOff) * MValueX); + GLy = (int)ceil(Renderer::GetTargetHeight() - ((int)(xfregs.rawViewport[4] - xfregs.rawViewport[1] - 342 - scissorYOff)) * MValueY); + GLWidth = (int)ceil(abs((int)(2 * xfregs.rawViewport[0])) * MValueX); + GLHeight = (int)ceil(abs((int)(2 * xfregs.rawViewport[1])) * MValueY); + } + // ------------------------------------- + + + // Update the view port + 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"); + */ +}