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"); + */ +}