diff --git a/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp b/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp index a64f2737f6..dd3361ee24 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp @@ -1,773 +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); - } - - // 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]); - } - } -} +// 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]); + } + } +}