--- /dev/null
+/*
+Copyright (C) 2005 Rice1964
+
+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; either version 2
+of the License, or (at your option) any later version.
+
+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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// ===========================================================================
+
+#include <vector>
+
+#include "ConvertImage.h"
+#include "DeviceBuilder.h"
+#include "FrameBuffer.h"
+#include "UcodeDefs.h"
+#include "RSP_Parser.h"
+#include "Render.h"
+
+extern TMEMLoadMapInfo g_tmemLoadAddrMap[0x200]; // Totally 4KB TMEM;
+
+// 0 keeps the most recent CI info
+// 1 keeps the frame buffer CI info which is being displayed now
+// 2 keeps the older frame buffer CI info. This can be used if we are using triple buffer
+/* Overview of framebuffer implementation
+1) Check if backbuffer has changed, via different detection techniques
+2) If changed, we copy the GFX card's backbuffer to main RAM
+3) This is slow due to the reading process, not the writing
+*/
+
+RecentCIInfo g_RecentCIInfo[5];
+RecentCIInfo *g_uRecentCIInfoPtrs[5] =
+{
+ &g_RecentCIInfo[0],
+ &g_RecentCIInfo[1],
+ &g_RecentCIInfo[2],
+ &g_RecentCIInfo[3],
+ &g_RecentCIInfo[4],
+};
+
+int numOfRecentCIInfos = 5;
+
+RecentViOriginInfo g_RecentVIOriginInfo[5];
+uint32 dwBackBufferSavedAtFrame=0;
+
+RenderTextureInfo gRenderTextureInfos[20];
+int numOfTxtBufInfos = sizeof(gRenderTextureInfos)/sizeof(RenderTextureInfo);
+RenderTextureInfo *g_pRenderTextureInfo = NULL;
+
+FrameBufferManager* g_pFrameBufferManager = NULL;
+
+bool LastCIIsNewCI=false;
+
+FrameBufferManager::FrameBufferManager() :
+ m_isRenderingToTexture(false),
+ m_curRenderTextureIndex(-1),
+ m_lastTextureBufferIndex(-1)
+{
+}
+
+FrameBufferManager::~FrameBufferManager()
+{
+}
+
+void FrameBufferManager::CloseUp()
+{
+ for( int i=0; i<numOfTxtBufInfos; i++ )
+ {
+ SAFE_DELETE(gRenderTextureInfos[i].pRenderTexture);
+ }
+}
+
+void FrameBufferManager::Initialize()
+{
+ m_isRenderingToTexture = false;
+ m_lastTextureBufferIndex = -1;
+ m_curRenderTextureIndex = -1;
+
+ status.bCIBufferIsRendered = false;
+ status.bN64IsDrawingTextureBuffer = false;
+ status.bHandleN64RenderTexture = false;
+ status.bN64FrameBufferIsUsed = false;
+
+ memset(&gRenderTextureInfos[0], 0, sizeof(RenderTextureInfo)*numOfTxtBufInfos);
+}
+// ===========================================================================
+
+uint16 ConvertRGBATo555(uint8 r, uint8 g, uint8 b, uint8 a)
+{
+ uint8 ar = a>=0x20?1:0;
+ return ((r>>3)<<RGBA5551_RedShift) | ((g>>3)<<RGBA5551_GreenShift) | ((b>>3)<<RGBA5551_BlueShift) | ar;//(a>>7);
+}
+
+uint16 ConvertRGBATo555(uint32 color32)
+{
+ return (uint16)((((color32>>19)&0x1F)<<RGBA5551_RedShift) | (((color32>>11)&0x1F)<<RGBA5551_GreenShift) | (((color32>>3)&0x1F)<<RGBA5551_BlueShift) | ((color32>>31)));;
+}
+
+void FrameBufferManager::UpdateRecentCIAddr(SetImgInfo &ciinfo)
+{
+ if( ciinfo.dwAddr == g_uRecentCIInfoPtrs[0]->dwAddr )
+ return;
+
+ RecentCIInfo *temp;
+
+ int i;
+ for( i=1; i<numOfRecentCIInfos; i++ )
+ {
+ if( ciinfo.dwAddr == g_uRecentCIInfoPtrs[i]->dwAddr )
+ {
+ temp = g_uRecentCIInfoPtrs[i];
+
+ for( int j=i; j>0; j-- )
+ {
+ g_uRecentCIInfoPtrs[j] = g_uRecentCIInfoPtrs[j-1];
+ }
+ break;
+ }
+ }
+
+ if( i >= numOfRecentCIInfos )
+ {
+ temp = g_uRecentCIInfoPtrs[4];
+ g_uRecentCIInfoPtrs[4] = g_uRecentCIInfoPtrs[3];
+ g_uRecentCIInfoPtrs[3] = g_uRecentCIInfoPtrs[2];
+ g_uRecentCIInfoPtrs[2] = g_uRecentCIInfoPtrs[1];
+ g_uRecentCIInfoPtrs[1] = g_uRecentCIInfoPtrs[0];
+ temp->dwCopiedAtFrame = 0;
+ temp->bCopied = false;
+ }
+
+ g_uRecentCIInfoPtrs[0] = temp;
+
+ // Fix me here for Mario Tennis
+ temp->dwLastWidth = windowSetting.uViWidth;
+ temp->dwLastHeight = windowSetting.uViHeight;
+
+ temp->dwFormat = ciinfo.dwFormat;
+ temp->dwAddr = ciinfo.dwAddr;
+ temp->dwSize = ciinfo.dwSize;
+ temp->dwWidth = ciinfo.dwWidth;
+ temp->dwHeight = gRDP.scissor.bottom;
+ temp->dwMemSize = (temp->dwWidth*temp->dwHeight/2)<<temp->dwSize;
+ temp->bCopied = false;
+ temp->lastUsedFrame = status.gDlistCount;
+ temp->lastSetAtUcode = status.gUcodeCount;
+}
+
+
+/************************************************************************/
+/* Mark the ciinfo entry that the ciinfo is used by VI origin register */
+/* in another word, this is a real frame buffer, not a fake frame buffer*/
+/* Fake frame buffers are never really used by VI origin */
+/************************************************************************/
+void FrameBufferManager::SetAddrBeDisplayed(uint32 addr)
+{
+ uint32 viwidth = *g_GraphicsInfo.VI_WIDTH_REG;
+ addr &= (g_dwRamSize-1);
+
+ int i;
+ for( i=0; i<numOfRecentCIInfos; i++ )
+ {
+ if( g_uRecentCIInfoPtrs[i]->dwAddr+2*viwidth == addr )
+ {
+ g_uRecentCIInfoPtrs[i]->bUsedByVIAtFrame = status.gDlistCount;
+ }
+ else if( addr >= g_uRecentCIInfoPtrs[i]->dwAddr && addr < g_uRecentCIInfoPtrs[i]->dwAddr+0x1000 )
+ {
+ g_uRecentCIInfoPtrs[i]->bUsedByVIAtFrame = status.gDlistCount;
+ }
+ }
+
+ for( i=0; i<numOfRecentCIInfos; i++ )
+ {
+ if( g_RecentVIOriginInfo[i].addr == addr )
+ {
+ g_RecentVIOriginInfo[i].FrameCount = status.gDlistCount;
+ return;
+ }
+ }
+
+ for( i=0; i<numOfRecentCIInfos; i++ )
+ {
+ if( g_RecentVIOriginInfo[i].addr == 0 )
+ {
+ // Never used
+ g_RecentVIOriginInfo[i].addr = addr;
+ g_RecentVIOriginInfo[i].FrameCount = status.gDlistCount;
+ return;
+ }
+ }
+
+ int index=0;
+ uint32 minFrameCount = 0xffffffff;
+
+ for( i=0; i<numOfRecentCIInfos; i++ )
+ {
+ if( g_RecentVIOriginInfo[i].FrameCount < minFrameCount )
+ {
+ index = i;
+ minFrameCount = g_RecentVIOriginInfo[i].FrameCount;
+ }
+ }
+
+ g_RecentVIOriginInfo[index].addr = addr;
+ g_RecentVIOriginInfo[index].FrameCount = status.gDlistCount;
+}
+
+bool FrameBufferManager::HasAddrBeenDisplayed(uint32 addr, uint32 width)
+{
+ addr &= (g_dwRamSize-1);
+
+ int i;
+ for( i=0; i<numOfRecentCIInfos; i++ )
+ {
+ if( g_uRecentCIInfoPtrs[i]->dwAddr == 0 )
+ continue;
+
+ if( g_uRecentCIInfoPtrs[i]->dwAddr == addr )
+ {
+ if( status.gDlistCount-g_uRecentCIInfoPtrs[i]->bUsedByVIAtFrame < 20 )
+ //if( g_uRecentCIInfoPtrs[i]->bUsedByVIAtFrame != 0 )
+ {
+ return true;
+ }
+ else
+ {
+ TXTRBUF_DUMP(TRACE0("This is a new buffer address, the addr is never a displayed buffer"););
+ return false;
+ }
+ }
+ }
+
+ for( i=0; i<numOfRecentCIInfos; i++ )
+ {
+ if( g_RecentVIOriginInfo[i].addr != 0 )
+ {
+ if( g_RecentVIOriginInfo[i].addr > addr &&
+ (g_RecentVIOriginInfo[i].addr - addr)%width == 0 &&
+ (g_RecentVIOriginInfo[i].addr - addr)/width <= 4)
+ {
+ if( status.gDlistCount-g_RecentVIOriginInfo[i].FrameCount < 20 )
+ //if( g_RecentVIOriginInfo[i].FrameCount != 0 )
+ {
+ return true;
+ }
+ else
+ {
+ TXTRBUF_DUMP(DebuggerAppendMsg("This is a new buffer address, the addr is never a displayed buffer"););
+ return false;
+ }
+ }
+ }
+ }
+
+ if( status.gDlistCount > 20 )
+ return false;
+ else
+ {
+ TXTRBUF_DUMP({DebuggerAppendMsg("This is a new buffer address, the addr is never a displayed buffer");});
+ return true;
+ }
+}
+
+int FrameBufferManager::FindRecentCIInfoIndex(uint32 addr)
+{
+ for( int i=0; i<numOfRecentCIInfos; i++ )
+ {
+ if( g_uRecentCIInfoPtrs[i]->dwAddr <= addr && addr < g_uRecentCIInfoPtrs[i]->dwAddr+g_uRecentCIInfoPtrs[i]->dwMemSize )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool FrameBufferManager::IsDIaRenderTexture()
+{
+ // Knowing g_CI and g_ZI
+
+ //if( g_CI.dwWidth )
+
+ bool foundSetScissor=false;
+ bool foundFillRect=false;
+ bool foundSetFillColor=false;
+ bool foundSetCImg=false;
+ uint32 newFillColor = 0;
+
+ uint32 dwPC = gDlistStack[gDlistStackPointer].pc; // This points to the next instruction
+
+ for( int i=0; i<10; i++ )
+ {
+ uint32 w0 = *(uint32 *)(g_pRDRAMu8 + dwPC + i*8);
+ uint32 w1 = *(uint32 *)(g_pRDRAMu8 + dwPC + 4 + i*8);
+
+ if( (w0>>24) == RDP_SETSCISSOR )
+ {
+ foundSetScissor = true;
+ continue;
+ }
+
+ if( (w0>>24) == RDP_SETFILLCOLOR )
+ {
+ foundSetFillColor = true;
+ newFillColor = w1;
+ continue;
+ }
+
+ if( (w0>>24) == RDP_FILLRECT )
+ {
+ uint32 x0 = ((w1>>12)&0xFFF)/4;
+ uint32 y0 = ((w1>>0 )&0xFFF)/4;
+ uint32 x1 = ((w0>>12)&0xFFF)/4;
+
+ if( x0 == 0 && y0 == 0 )
+ {
+ if( x1 == g_CI.dwWidth )
+ {
+ foundFillRect = true;
+ continue;
+ }
+
+ if(x1 == (unsigned int)(g_CI.dwWidth-1))
+ {
+ foundFillRect = true;
+ continue;
+ }
+ }
+ }
+
+ if( (w0>>24) == RDP_TEXRECT )
+ {
+ break;
+ }
+
+ if( (w0>>24) == RDP_SETCIMG )
+ {
+ foundSetCImg = true;
+ break;
+ }
+ }
+
+ /*
+ bool foundSetScissor=false;
+ bool foundFillRect=false;
+ bool foundSetFillColor=false;
+ bool foundSetCImg=false;
+ bool foundTxtRect=false;
+ int ucodeLength=10;
+ uint32 newFillColor;
+ */
+
+ if( foundFillRect )
+ {
+ if( foundSetFillColor )
+ {
+ if( newFillColor != 0xFFFCFFFC )
+ return true; // this is a render_texture
+ else
+ return false;
+ }
+
+ if( gRDP.fillColor != 0x00FFFFF7 )
+ return true; // this is a render_texture
+ else
+ return false; // this is a normal ZImg
+ }
+ else if( foundSetFillColor && newFillColor == 0xFFFCFFFC && foundSetCImg )
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+
+
+ if( !foundSetCImg )
+ return true;
+
+ if( foundSetScissor )
+ return true;
+}
+
+int FrameBufferManager::CheckAddrInBackBuffers(uint32 addr, uint32 memsize, bool copyToRDRAM)
+{
+ int r = FindRecentCIInfoIndex(addr);
+
+ if( r >= 0 )
+ {
+ // Also check if the address is overwritten by a recent render_texture
+ //int t = CheckAddrInRenderTextures(addr,false);
+ int t =-1;
+ for( int i=0; i<numOfTxtBufInfos; i++ )
+ {
+ uint32 bufHeight = gRenderTextureInfos[i].knownHeight ? gRenderTextureInfos[i].N64Height : gRenderTextureInfos[i].maxUsedHeight;
+ uint32 bufMemSize = gRenderTextureInfos[i].CI_Info.dwSize*gRenderTextureInfos[i].N64Width*bufHeight;
+ if( addr >=gRenderTextureInfos[i].CI_Info.dwAddr && addr < gRenderTextureInfos[i].CI_Info.dwAddr+bufMemSize)
+ {
+ if( g_uRecentCIInfoPtrs[r]->lastSetAtUcode < gRenderTextureInfos[i].updateAtUcodeCount )
+ {
+ t = i;
+ break;
+ }
+ }
+ }
+
+ if( t >= 0 )
+ return -1;
+ }
+
+ if( r >= 0 && status.gDlistCount - g_uRecentCIInfoPtrs[r]->lastUsedFrame <= 3 && g_uRecentCIInfoPtrs[r]->bCopied == false )
+ {
+ DEBUGGER_IF_DUMP((logTextureBuffer&&r==1),TRACE2("Hit current front buffer at %08X, size=0x%X", addr, memsize));
+ DEBUGGER_IF_DUMP((logTextureBuffer&&r==0),TRACE2("Hit current back buffer at %08X, size=0x%X", addr, memsize));
+ DEBUGGER_IF_DUMP((logTextureBuffer&&r>1),TRACE2("Hit old back buffer at %08X, size=0x%X", addr, memsize));
+
+ SaveBackBuffer(r, NULL, true);
+ }
+
+ return r;
+}
+
+
+uint8 CIFindIndex(uint16 val)
+{
+ for( int i=0; i<=0xFF; i++ )
+ {
+ if( val == g_wRDPTlut[i] )
+ {
+ return (uint8)i;
+ }
+ }
+ return 0;
+}
+
+
+void TexRectToFrameBuffer_8b(uint32 dwXL, uint32 dwYL, uint32 dwXH, uint32 dwYH, float t0u0, float t0v0, float t0u1, float t0v1, uint32 dwTile)
+{
+ // Copy the framebuffer texture into the N64 framebuffer memory
+ // Used in Yoshi
+
+ /*
+ uint32 maxW = g_pRenderTextureInfo->CI_Info.dwWidth;
+ uint32 maxH = maxW*3/4;
+ if( status.dwTvSystem == TV_SYSTEM_PAL )
+ {
+ maxH = maxW*9/11;
+ }
+ */
+
+ uint32 maxW = g_pRenderTextureInfo->N64Width;
+ uint32 maxH = g_pRenderTextureInfo->N64Height;
+
+ uint32 maxOff = maxW*maxH;
+
+ TMEMLoadMapInfo &info = g_tmemLoadAddrMap[gRDP.tiles[dwTile].dwTMem];
+ uint32 dwWidth = dwXH-dwXL;
+ uint32 dwHeight = dwYH-dwYL;
+
+ float xScale = (t0u1-t0u0)/dwWidth;
+ float yScale = (t0v1-t0v0)/dwHeight;
+
+ uint8* dwSrc = g_pRDRAMu8 + info.dwLoadAddress;
+ uint8* dwDst = g_pRDRAMu8 + g_pRenderTextureInfo->CI_Info.dwAddr;
+
+ uint32 dwSrcPitch = gRDP.tiles[dwTile].dwPitch;
+ uint32 dwDstPitch = g_pRenderTextureInfo->CI_Info.dwWidth;
+
+ uint32 dwSrcOffX = gRDP.tiles[dwTile].hilite_sl;
+ uint32 dwSrcOffY = gRDP.tiles[dwTile].hilite_tl;
+
+ uint32 dwLeft = dwXL;
+ uint32 dwTop = dwYL;
+
+ dwWidth = min(dwWidth, maxW-dwLeft);
+ dwHeight = min(dwHeight, maxH-dwTop);
+
+ if( maxH <= dwTop )
+ return;
+
+ for (uint32 y = 0; y < dwHeight; y++)
+ {
+ uint32 dwByteOffset = (uint32)(((y*yScale+dwSrcOffY) * dwSrcPitch) + dwSrcOffX);
+
+ for (uint32 x = 0; x < dwWidth; x++)
+ {
+ if( (((y+dwTop)*dwDstPitch+x+dwLeft)^0x3) > maxOff )
+ {
+#ifdef DEBUGGER
+ TRACE0("Warning: Offset exceeds limit");
+#endif
+ continue;
+ }
+ dwDst[((y+dwTop)*dwDstPitch+x+dwLeft)^0x3] = dwSrc[(uint32)(dwByteOffset+x*xScale) ^ 0x3];
+ }
+ }
+
+ TXTRBUF_DUMP(DebuggerAppendMsg("TexRect To FrameBuffer: X0=%d, Y0=%d, X1=%d, Y1=%d,\n\t\tfS0=%f, fT0=%f, fS1=%f, fT1=%f ",
+ dwXL, dwYL, dwXH, dwYH, t0v0, t0v0, t0u1, t0v1););
+}
+
+void TexRectToN64FrameBuffer_16b(uint32 x0, uint32 y0, uint32 width, uint32 height, uint32 dwTile)
+{
+ // Copy the framebuffer texture into the N64 RDRAM framebuffer memory structure
+
+ DrawInfo srcInfo;
+ if( g_textures[dwTile].m_pCTexture->StartUpdate(&srcInfo) == false )
+ {
+ DebuggerAppendMsg("Fail to lock texture:TexRectToN64FrameBuffer_16b" );
+ return;
+ }
+
+ uint32 n64CIaddr = g_CI.dwAddr;
+ uint32 n64CIwidth = g_CI.dwWidth;
+
+ for (uint32 y = 0; y < height; y++)
+ {
+ uint32* pSrc = (uint32*)((uint8*)srcInfo.lpSurface + y * srcInfo.lPitch);
+ uint16* pN64Buffer = (uint16*)(g_pRDRAMu8+(n64CIaddr&(g_dwRamSize-1)))+(y+y0)*n64CIwidth;
+
+ for (uint32 x = 0; x < width; x++)
+ {
+ pN64Buffer[x+x0] = ConvertRGBATo555(pSrc[x]);
+ }
+ }
+
+ g_textures[dwTile].m_pCTexture->EndUpdate(&srcInfo);
+}
+
+#define FAST_CRC_CHECKING_INC_X 13
+#define FAST_CRC_CHECKING_INC_Y 11
+#define FAST_CRC_MIN_Y_INC 2
+#define FAST_CRC_MIN_X_INC 2
+#define FAST_CRC_MAX_X_INC 7
+#define FAST_CRC_MAX_Y_INC 3
+extern uint32 dwAsmHeight;
+extern uint32 dwAsmPitch;
+extern uint32 dwAsmdwBytesPerLine;
+extern uint32 dwAsmCRC;
+extern uint8* pAsmStart;
+
+uint32 CalculateRDRAMCRC(void *pPhysicalAddress, uint32 left, uint32 top, uint32 width, uint32 height, uint32 size, uint32 pitchInBytes )
+{
+ dwAsmCRC = 0;
+ dwAsmdwBytesPerLine = ((width<<size)+1)/2;
+
+ if( currentRomOptions.bFastTexCRC && !options.bLoadHiResTextures && (height>=32 || (dwAsmdwBytesPerLine>>2)>=16))
+ {
+ uint32 realWidthInDWORD = dwAsmdwBytesPerLine>>2;
+ uint32 xinc = realWidthInDWORD / FAST_CRC_CHECKING_INC_X;
+ if( xinc < FAST_CRC_MIN_X_INC )
+ {
+ xinc = min(FAST_CRC_MIN_X_INC, width);
+ }
+ if( xinc > FAST_CRC_MAX_X_INC )
+ {
+ xinc = FAST_CRC_MAX_X_INC;
+ }
+
+ uint32 yinc = height / FAST_CRC_CHECKING_INC_Y;
+ if( yinc < FAST_CRC_MIN_Y_INC )
+ {
+ yinc = min(FAST_CRC_MIN_Y_INC, height);
+ }
+ if( yinc > FAST_CRC_MAX_Y_INC )
+ {
+ yinc = FAST_CRC_MAX_Y_INC;
+ }
+
+ uint32 pitch = pitchInBytes>>2;
+ register uint32 *pStart = (uint32*)(pPhysicalAddress);
+ pStart += (top * pitch) + (((left<<size)+1)>>3);
+
+ // The original assembly code had a bug in it (it incremented pStart by 'pitch' in bytes, not in dwords)
+ // This C code implements the same algorithm as the ASM but without the bug
+ uint32 y = 0;
+ while (y < height)
+ {
+ uint32 x = 0;
+ while (x < realWidthInDWORD)
+ {
+ dwAsmCRC = (dwAsmCRC << 4) + ((dwAsmCRC >> 28) & 15);
+ dwAsmCRC += pStart[x];
+ x += xinc;
+ dwAsmCRC += x;
+ }
+ dwAsmCRC ^= y;
+ y += yinc;
+ pStart += pitch;
+ }
+ }
+ else
+ {
+ try
+ {
+ dwAsmdwBytesPerLine = ((width<<size)+1)/2;
+
+ pAsmStart = (uint8*)(pPhysicalAddress);
+ pAsmStart += (top * pitchInBytes) + (((left<<size)+1)>>1);
+
+ dwAsmHeight = height - 1;
+ dwAsmPitch = pitchInBytes;
+
+#if defined(NO_ASM)
+ uint32 pitch = pitchInBytes>>2;
+ uint32* pStart = (uint32*)pPhysicalAddress;
+ pStart += (top * pitch) + (((left<<size)+1)>>3);
+
+ int y = dwAsmHeight;
+
+ while(y >= 0)
+ {
+ uint32 esi = 0;
+ int x = dwAsmdwBytesPerLine - 4;
+ while(x >= 0)
+ {
+ esi = *(uint32*)(pAsmStart + x);
+ esi ^= x;
+
+ dwAsmCRC = (dwAsmCRC << 4) + ((dwAsmCRC >> 28) & 15);
+ dwAsmCRC += esi;
+ x-=4;
+ }
+ esi ^= y;
+ dwAsmCRC += esi;
+ pAsmStart += dwAsmPitch;
+ y--;
+ }
+
+#elif !defined(__GNUC__) // !defined(NO_ASM)
+ __asm
+ {
+ push eax
+ push ebx
+ push ecx
+ push edx
+ push esi
+
+ mov ecx, pAsmStart; // = pStart
+ mov edx, 0 // The CRC
+ mov eax, dwAsmHeight // = y
+l2: mov ebx, dwAsmdwBytesPerLine // = x
+ sub ebx, 4
+l1: mov esi, [ecx+ebx]
+ xor esi, ebx
+ rol edx, 4
+ add edx, esi
+ sub ebx, 4
+ jge l1
+ xor esi, eax
+ add edx, esi
+ add ecx, dwAsmPitch
+ dec eax
+ jge l2
+
+ mov dwAsmCRC, edx
+
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+ }
+#elif defined(__x86_64__) // defined(__GNUC__) && !defined(NO_ASM)
+ asm volatile(" xorl %k2, %k2 \n"
+ " movslq %k4, %q4 \n"
+ "0: \n"
+ " movslq %3, %%rbx \n"
+ " sub $4, %%rbx \n"
+ "1: \n"
+ " movl (%0,%%rbx,1), %%eax \n"
+ " xorl %%ebx, %%eax \n"
+ " roll $4, %k2 \n"
+ " addl %%eax, %k2 \n"
+ " sub $4, %%rbx \n"
+ " jge 1b \n"
+ " xorl %k1, %%eax \n"
+ " addl %%eax, %k2 \n"
+ " add %q4, %0 \n"
+ " decl %k1 \n"
+ " jge 0b \n"
+ : "+r"(pAsmStart), "+r"(dwAsmHeight), "=&r"(dwAsmCRC)
+ : "m"(dwAsmdwBytesPerLine), "r"(dwAsmPitch)
+ : "%rbx", "%rax", "memory", "cc"
+ );
+#elif !defined(__PIC__) // !defined(__x86_64__) && defined(__GNUC__) && !defined(NO_ASM)
+ asm volatile("pusha \n"
+ "mov %[pAsmStart], %%ecx \n" // = pStart
+ "mov $0, %%edx \n" // The CRC
+ "mov %[dwAsmHeight], %%eax \n" // = y
+ "0: \n" //l2:
+ "mov %[dwAsmdwBytesPerLine], %%ebx \n" // = x
+ "sub $4, %%ebx \n"
+ "1: \n" //l1:
+ "mov (%%ecx,%%ebx), %%esi \n"
+ "xor %%ebx, %%esi \n"
+ "rol $4, %%edx \n"
+ "add %%esi, %%edx \n"
+ "sub $4, %%ebx \n"
+ "jge 1b \n" //jge l1
+ "xor %%eax, %%esi \n"
+ "add %%esi, %%edx \n"
+ "add %[dwAsmPitch], %%ecx \n"
+ "dec %%eax \n"
+ "jge 0b \n" //jge l2
+
+ "mov %%edx, %[dwAsmCRC] \n"
+ "popa \n"
+ : [pAsmStart]"+m"(pAsmStart), [dwAsmHeight]"+m"(dwAsmHeight), [dwAsmCRC]"=m"(dwAsmCRC)
+ : [dwAsmdwBytesPerLine]"m"(dwAsmdwBytesPerLine), [dwAsmPitch]"m"(dwAsmPitch)
+ : "memory", "cc"
+ );
+#else // defined(__PIC__) && !defined(__x86_64__) && defined(__GNUC__) && !defined(NO_ASM)
+ unsigned int saveEBX;
+ unsigned int saveEAX;
+ unsigned int saveECX;
+ unsigned int saveEDX;
+ unsigned int saveESI;
+ unsigned int asmdwBytesPerLine = dwAsmdwBytesPerLine;
+ unsigned int asmPitch = dwAsmPitch;
+ unsigned int asmHeight = dwAsmHeight;
+ unsigned int asmCRC;
+ asm volatile("mov %%ebx, %2 \n"
+ "mov %%eax, %5 \n"
+ "mov %%ecx, %7 \n"
+ "mov %%edx, %8 \n"
+ "mov %%esi, %9 \n"
+ "mov %0, %%ecx \n" // = pStart
+ "mov $0, %%edx \n" // The CRC
+ "mov %1, %%eax \n" // = y
+ "0: \n" //l2:
+ "mov %3, %%ebx \n" // = x
+ "sub $4, %%ebx \n"
+ "1: \n" //l1:
+ "mov (%%ecx,%%ebx), %%esi \n"
+ "xor %%ebx, %%esi \n"
+ "rol $4, %%edx \n"
+ "add %%esi, %%edx \n"
+ "sub $4, %%ebx \n"
+ "jge 1b \n" //jge l1
+ "xor %%eax, %%esi \n"
+ "add %%esi, %%edx \n"
+ "add %4, %%ecx \n"
+ "dec %%eax \n"
+ "jge 0b \n" //jge l2
+
+ "mov %2, %%ebx \n"
+ "mov %%edx, %6 \n"
+ "mov %5, %%eax \n"
+ "mov %7, %%ecx \n"
+ "mov %8, %%edx \n"
+ "mov %9, %%esi \n"
+ :
+ : "m"(pAsmStart), "m"(asmHeight), "m"(saveEBX), "m"(asmdwBytesPerLine), "m"(asmPitch), "m"(saveEAX),
+ "m"(asmCRC), "m"(saveECX), "m"(saveEDX), "m"(saveESI)
+ : "memory", "cc"
+ );
+ dwAsmCRC = asmCRC;
+#endif
+ }
+ catch(...)
+ {
+ TRACE0("Exception in texture CRC calculation");
+ }
+ }
+ return dwAsmCRC;
+}
+unsigned char CalculateMaxCI(void *pPhysicalAddress, uint32 left, uint32 top, uint32 width, uint32 height, uint32 size, uint32 pitchInBytes )
+{
+ uint32 x, y;
+ unsigned char *buf;
+ unsigned char val = 0;
+
+ if( TXT_SIZE_8b == size )
+ {
+ for( y = 0; y<height; y++ )
+ {
+ buf = (unsigned char*)pPhysicalAddress + left + pitchInBytes * (y+top);
+ for( x=0; x<width; x++ )
+ {
+ if( buf[x] > val ) val = buf[x];
+ if( val == 0xFF )
+ return 0xFF;
+ }
+ }
+ }
+ else
+ {
+ unsigned char val1,val2;
+ left >>= 1;
+ width >>= 1;
+ for( y = 0; y<height; y++ )
+ {
+ buf = (unsigned char*)pPhysicalAddress + left + pitchInBytes * (y+top);
+ for( x=0; x<width; x++ )
+ {
+ val1 = buf[x]>>4;
+ val2 = buf[x]&0xF;
+ if( val1 > val ) val = val1;
+ if( val2 > val ) val = val2;
+ if( val == 0xF )
+ return 0xF;
+ }
+ }
+ }
+
+ return val;
+}
+
+bool FrameBufferManager::FrameBufferInRDRAMCheckCRC()
+{
+ RecentCIInfo &p = *(g_uRecentCIInfoPtrs[0]);
+ uint8 *pFrameBufferBase = (uint8*)(g_pRDRAMu8+p.dwAddr);
+ uint32 pitch = (p.dwWidth << p.dwSize ) >> 1;
+ uint32 crc = CalculateRDRAMCRC(pFrameBufferBase, 0, 0, p.dwWidth, p.dwHeight, p.dwSize, pitch);
+ if( crc != p.dwCRC )
+ {
+ p.dwCRC = crc;
+ TRACE0("Frame Buffer CRC mismitch, it is modified by CPU");
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+extern std::vector<uint32> frameWriteRecord;
+void FrameBufferManager::FrameBufferWriteByCPU(uint32 addr, uint32 size)
+{
+ if( !frameBufferOptions.bProcessCPUWrite ) return;
+ //WARNING(TRACE2("Frame Buffer Write, addr=%08X, CI Addr=%08X", addr, g_CI.dwAddr));
+ status.frameWriteByCPU = TRUE;
+ frameWriteRecord.push_back(addr&(g_dwRamSize-1));
+}
+
+extern RECT frameWriteByCPURect;
+extern std::vector<RECT> frameWriteByCPURects;
+extern RECT frameWriteByCPURectArray[20][20];
+extern bool frameWriteByCPURectFlag[20][20];
+#define FRAMEBUFFER_IN_BLOCK
+bool FrameBufferManager::ProcessFrameWriteRecord()
+{
+ int size = frameWriteRecord.size();
+ if( size == 0 ) return false;
+
+ int index = FindRecentCIInfoIndex(frameWriteRecord[0]);
+ if( index == -1 )
+ {
+ LOG_TEXTURE(TRACE1("Frame Buffer Write to non-record addr = %08X", frameWriteRecord[0]));
+ frameWriteRecord.clear();
+ return false;
+ }
+ else
+ {
+ uint32 base = g_uRecentCIInfoPtrs[index]->dwAddr;
+ uint32 uwidth = g_uRecentCIInfoPtrs[index]->dwWidth;
+ uint32 uheight = g_uRecentCIInfoPtrs[index]->dwHeight;
+ uint32 upitch = uwidth<<1;
+
+ frameWriteByCPURect.left=uwidth-1;
+ frameWriteByCPURect.top = uheight-1;
+
+ frameWriteByCPURect.right=0;
+ frameWriteByCPURect.bottom = 0;
+
+ int x, y, off;
+
+ for( int i=0; i<size; i++ )
+ {
+ off = frameWriteRecord[i]-base;
+ if( off < (int)g_uRecentCIInfoPtrs[index]->dwMemSize )
+ {
+ y = off/upitch;
+ x = (off - y*upitch)>>1;
+
+#ifdef FRAMEBUFFER_IN_BLOCK
+ int xidx=x/32;
+ int yidx=y/24;
+
+ RECT &rect = frameWriteByCPURectArray[xidx][yidx];
+
+ if( !frameWriteByCPURectFlag[xidx][yidx] )
+ {
+ rect.left=rect.right=x;
+ rect.top=rect.bottom=y;
+ frameWriteByCPURectFlag[xidx][yidx]=true;
+ }
+ else
+ {
+ if( x < rect.left ) rect.left = x;
+ if( x > rect.right ) rect.right = x;
+ if( y < rect.top ) rect.top = y;
+ if( y > rect.bottom ) rect.bottom = y;
+ }
+#else
+ if( x < frameWriteByCPURect.left ) frameWriteByCPURect.left = x;
+ if( x > frameWriteByCPURect.right ) frameWriteByCPURect.right = x;
+ if( y < frameWriteByCPURect.top ) frameWriteByCPURect.top = y;
+ if( y > frameWriteByCPURect.bottom ) frameWriteByCPURect.bottom = y;
+#endif
+ }
+ }
+
+ frameWriteRecord.clear();
+ LOG_TEXTURE(TRACE4("Frame Buffer Write: Left=%d, Top=%d, Right=%d, Bottom=%d", frameWriteByCPURect.left,
+ frameWriteByCPURect.top, frameWriteByCPURect.right, frameWriteByCPURect.bottom));
+ return true;
+ }
+}
+
+void FrameBufferManager::FrameBufferReadByCPU( uint32 addr )
+{
+ ///return; // it does not work very well anyway
+
+
+ if( !frameBufferOptions.bProcessCPURead ) return;
+
+ addr &= (g_dwRamSize-1);
+ int index = FindRecentCIInfoIndex(addr);
+ if( index == -1 )
+ {
+ // Check if this is the depth buffer
+ uint32 size = 2*g_RecentCIInfo[0].dwWidth*g_RecentCIInfo[0].dwHeight;
+ addr &= 0x3FFFFFFF;
+
+ if( addr >= g_ZI.dwAddr && addr < g_ZI.dwAddr + size )
+ {
+ TXTRBUF_OR_CI_DUMP(TRACE1("Depth Buffer read, reported by emulator, addr=%08X", addr));
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ if( status.gDlistCount - g_uRecentCIInfoPtrs[index]->lastUsedFrame > 3 )
+ {
+ // Ok, we don't have this frame anymore
+ return;
+ }
+
+ //TXTRBUF_OR_CI_DUMP(TRACE1("FB Read By CPU at %08X", addr));
+ if( g_uRecentCIInfoPtrs[index]->bCopied ) return;
+ //if( addr != g_uRecentCIInfoPtrs[index]->dwAddr ) return;
+
+ TXTRBUF_OR_CI_DUMP(TRACE1("Frame Buffer read, reported by emulator, addr=%08X", addr));
+ uint32 size = 0x1000 - addr%0x1000;
+ CheckAddrInBackBuffers(addr, size, true);
+
+ DEBUGGER_IF_DUMP(pauseAtNext,{TRACE0("Frame Buffer read");});
+ DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_RENDER_TEXTURE,
+ {DebuggerAppendMsg("Paused after setting Frame Buffer read:\n Cur CI Addr: 0x%08x, Fmt: %s Size: %s Width: %d",
+ g_CI.dwAddr, pszImgFormat[g_CI.dwFormat], pszImgSize[g_CI.dwSize], g_CI.dwWidth);});
+}
+
+
+
+extern RECT frameWriteByCPURect;
+extern std::vector<RECT> frameWriteByCPURects;
+extern RECT frameWriteByCPURectArray[20][20];
+extern bool frameWriteByCPURectFlag[20][20];
+#define FRAMEBUFFER_IN_BLOCK
+
+void FrameBufferManager::UpdateFrameBufferBeforeUpdateFrame()
+{
+ if( (frameBufferOptions.bProcessCPUWrite && status.frameWriteByCPU ) ||
+ (frameBufferOptions.bLoadBackBufFromRDRAM && !FrameBufferInRDRAMCheckCRC() ) )
+ // Checks if frame buffer has been modified by CPU
+ // Only happens to Dr. Mario
+ {
+ if( frameBufferOptions.bProcessCPUWrite )
+ {
+ if( ProcessFrameWriteRecord() )
+ {
+#ifdef FRAMEBUFFER_IN_BLOCK
+ int i,j;
+ for( i=0; i<20; i++)
+ {
+ for( j=0; j<20; j++ )
+ {
+ if( frameWriteByCPURectFlag[i][j] )
+ {
+ CRender::GetRender()->DrawFrameBuffer(false, frameWriteByCPURectArray[i][j].left, frameWriteByCPURectArray[i][j].top,
+ frameWriteByCPURectArray[i][j].right-frameWriteByCPURectArray[i][j].left+1, frameWriteByCPURectArray[i][j].bottom-frameWriteByCPURectArray[i][j].top+1);
+ }
+ }
+ }
+ for( i=0; i<20; i++)
+ {
+ for( j=0; j<20; j++ )
+ {
+ if( frameWriteByCPURectFlag[i][j] )
+ {
+ ClearN64FrameBufferToBlack(frameWriteByCPURectArray[i][j].left, frameWriteByCPURectArray[i][j].top,
+ frameWriteByCPURectArray[i][j].right-frameWriteByCPURectArray[i][j].left+1, frameWriteByCPURectArray[i][j].bottom-frameWriteByCPURectArray[i][j].top+1);
+ frameWriteByCPURectFlag[i][j] = false;
+ }
+ }
+ }
+ //memset(frameWriteByCPURectArray, 0, sizeof(frameWriteByCPURectArray));
+ //memset(frameWriteByCPURectFlag, 0, sizeof(frameWriteByCPURectFlag));
+#else
+ CRender::GetRender()->DrawFrameBuffer(false, frameWriteByCPURect.left, frameWriteByCPURect.top,
+ frameWriteByCPURect.right-frameWriteByCPURect.left, frameWriteByCPURect.bottom-frameWriteByCPURect.top);
+ ClearN64FrameBufferToBlack(frameWriteByCPURect.left, frameWriteByCPURect.top,
+ frameWriteByCPURect.right-frameWriteByCPURect.left+1, frameWriteByCPURect.bottom-frameWriteByCPURect.top+1);
+
+ /*
+ int size = frameWriteByCPURects.size();
+ for( int i=0; i<size; i++)
+ {
+ CRender::GetRender()->DrawFrameBuffer(false, frameWriteByCPURects[i].left, frameWriteByCPURects[i].top,
+ frameWriteByCPURects[i].right-frameWriteByCPURects[i].left, frameWriteByCPURects[i].bottom-frameWriteByCPURects[i].top);
+ ClearN64FrameBufferToBlack(frameWriteByCPURects[i].left, frameWriteByCPURects[i].top,
+ frameWriteByCPURects[i].right-frameWriteByCPURects[i].left+1, frameWriteByCPURects[i].bottom-frameWriteByCPURects[i].top+1);
+ }
+ frameWriteByCPURects.clear();
+ */
+#endif
+ }
+ status.frameWriteByCPU = FALSE;
+ }
+ else
+ {
+ if (CRender::IsAvailable())
+ {
+ RecentCIInfo &p = *(g_uRecentCIInfoPtrs[0]);
+ CRender::GetRender()->DrawFrameBuffer(false, 0,0,p.dwWidth,p.dwHeight);
+ ClearN64FrameBufferToBlack();
+ }
+ }
+ }
+}
+
+uint32 FrameBufferManager::ComputeCImgHeight(SetImgInfo &info, uint32 &height)
+{
+ uint32 dwPC = gDlistStack[gDlistStackPointer].pc; // This points to the next instruction
+
+ for( int i=0; i<10; i++ )
+ {
+ uint32 w0 = *(uint32 *)(g_pRDRAMu8 + dwPC + i*8);
+ uint32 w1 = *(uint32 *)(g_pRDRAMu8 + dwPC + 4 + i*8);
+
+ if( (w0>>24) == RDP_SETSCISSOR )
+ {
+ height = ((w1>>0 )&0xFFF)/4;
+ TXTRBUF_DETAIL_DUMP(TRACE1("buffer height = %d", height));
+ return RDP_SETSCISSOR;
+ }
+
+ if( (w0>>24) == RDP_FILLRECT )
+ {
+ uint32 x0 = ((w1>>12)&0xFFF)/4;
+ uint32 y0 = ((w1>>0 )&0xFFF)/4;
+ uint32 x1 = ((w0>>12)&0xFFF)/4;
+ uint32 y1 = ((w0>>0 )&0xFFF)/4;
+
+ if( x0 == 0 && y0 == 0 )
+ {
+ if( x1 == info.dwWidth )
+ {
+ height = y1;
+ TXTRBUF_DETAIL_DUMP(TRACE1("buffer height = %d", height));
+ return RDP_FILLRECT;
+ }
+
+ if(x1 == (unsigned int)(info.dwWidth-1))
+ {
+ height = y1+1;
+ TXTRBUF_DETAIL_DUMP(TRACE1("buffer height = %d", height));
+ return RDP_FILLRECT;
+ }
+ }
+ }
+
+ if( (w0>>24) == RDP_SETCIMG )
+ {
+ goto step2;
+ }
+
+ if( (w0>>24) == RDP_SETCIMG )
+ {
+ goto step2;
+ }
+ }
+
+ if( gRDP.scissor.left == 0 && gRDP.scissor.top == 0 && (unsigned int)gRDP.scissor.right == info.dwWidth )
+ {
+ height = gRDP.scissor.bottom;
+ TXTRBUF_DETAIL_DUMP(TRACE1("buffer height = %d", height));
+ return RDP_SETSCISSOR+1;
+ }
+
+step2:
+ TXTRBUF_DETAIL_DUMP(TRACE0("Not sure about buffer height"));
+
+ height = info.dwWidth*3/4;
+ if( status.dwTvSystem == TV_SYSTEM_PAL )
+ {
+ height = info.dwWidth*9/11;
+ }
+
+ if( gRDP.scissor.bottom < (int)height && gRDP.scissor.bottom != 0 )
+ {
+ height = gRDP.scissor.bottom;
+ }
+
+ if( info.dwAddr + height*info.dwWidth*info.dwSize >= g_dwRamSize )
+ {
+ height = info.dwWidth*3/4;
+ if( status.dwTvSystem == TV_SYSTEM_PAL )
+ {
+ height = info.dwWidth*9/11;
+ }
+
+ if( gRDP.scissor.bottom < (int)height && gRDP.scissor.bottom != 0 )
+ {
+ height = gRDP.scissor.bottom;
+ }
+
+ if( info.dwAddr + height*info.dwWidth*info.dwSize >= g_dwRamSize )
+ {
+ height = ( g_dwRamSize - info.dwAddr ) / info.dwWidth;
+ }
+ }
+
+ TXTRBUF_DETAIL_DUMP(TRACE1("render_texture height = %d", height));
+ return 0;
+}
+
+int FrameBufferManager::CheckRenderTexturesWithNewCI(SetImgInfo &CIinfo, uint32 height, bool byNewTxtrBuf)
+{
+ int matchidx = -1;
+ uint32 memsize = ((height*CIinfo.dwWidth)>>1)<<CIinfo.dwSize;
+
+ for( int i=0; i<numOfTxtBufInfos; i++ )
+ {
+ RenderTextureInfo &info = gRenderTextureInfos[i];
+ if( !info.isUsed ) continue;
+
+ bool covered = false;
+
+ if( info.CI_Info.dwAddr == CIinfo.dwAddr )
+ {
+ if( info.CI_Info.dwSize == CIinfo.dwSize &&
+ info.CI_Info.dwWidth == CIinfo.dwWidth &&
+ info.CI_Info.dwFormat == CIinfo.dwFormat &&
+ info.N64Height == height
+ )
+ {
+ // This is the same texture at the same address
+ if( byNewTxtrBuf )
+ {
+ matchidx = i;
+ break;
+ }
+ }
+
+ // At the same address, but not the same size
+ //SAFE_DELETE(info.psurf);
+ covered = true;
+ }
+
+ if( !covered )
+ {
+ uint32 memsize2 = ((info.N64Height*info.N64Width)>>1)<<info.CI_Info.dwSize;
+
+ if( info.CI_Info.dwAddr > CIinfo.dwAddr && info.CI_Info.dwAddr < CIinfo.dwAddr + memsize)
+ covered = true;
+ else if( info.CI_Info.dwAddr+memsize2 > CIinfo.dwAddr && info.CI_Info.dwAddr+memsize2 < CIinfo.dwAddr + memsize)
+ covered = true;
+ else if( CIinfo.dwAddr > info.CI_Info.dwAddr && CIinfo.dwAddr < info.CI_Info.dwAddr + memsize2 )
+ covered = true;
+ else if( CIinfo.dwAddr+ memsize > info.CI_Info.dwAddr && CIinfo.dwAddr+ memsize < info.CI_Info.dwAddr + memsize2 )
+ covered = true;
+ }
+
+ if( covered )
+ {
+ //SAFE_DELETE(info.psurf);
+ if( info.pRenderTexture->IsBeingRendered() )
+ {
+ TRACE0("Error, covering a render_texture which is being rendered");
+ TRACE3("New addrr=%08X, width=%d, height=%d", CIinfo.dwAddr, CIinfo.dwWidth, height );
+ TRACE3("Old addrr=%08X, width=%d, height=%d", info.CI_Info.dwAddr, info.N64Width, info.N64Height );
+ }
+ info.isUsed = false;
+ TXTRBUF_DUMP(TRACE5("Delete txtr buf %d at %08X, covered by new CI at %08X, Width=%d, Height=%d",
+ i, info.CI_Info.dwAddr, CIinfo.dwAddr, CIinfo.dwWidth, height ));
+ SAFE_DELETE(info.pRenderTexture);
+ info.txtEntry.pTexture = NULL;
+ continue;
+ }
+ }
+
+ return matchidx;
+}
+
+extern RecentCIInfo *g_uRecentCIInfoPtrs[5];
+RenderTextureInfo newRenderTextureInfo;
+
+int FrameBufferManager::FindASlot(void)
+{
+ int idx;
+
+ // Find an empty slot
+ bool found = false;
+ for( int i=0; i<numOfTxtBufInfos; i++ )
+ {
+ if( !gRenderTextureInfos[i].isUsed && gRenderTextureInfos[i].updateAtFrame < status.gDlistCount )
+ {
+ found = true;
+ idx = i;
+ break;
+ }
+ }
+
+ // If cannot find an empty slot, find the oldest slot and reuse the slot
+ if( !found )
+ {
+ uint32 oldestCount=0xFFFFFFFF;
+ uint32 oldestIdx = 0;
+ for( int i=0; i<numOfTxtBufInfos; i++ )
+ {
+ if( gRenderTextureInfos[i].updateAtUcodeCount < oldestCount )
+ {
+ oldestCount = gRenderTextureInfos[i].updateAtUcodeCount;
+ oldestIdx = i;
+ }
+ }
+
+ idx = oldestIdx;
+ }
+
+ DEBUGGER_IF_DUMP((logTextureBuffer && gRenderTextureInfos[idx].pRenderTexture ),TRACE2("Delete txtr buf %d at %08X, to reuse it.", idx, gRenderTextureInfos[idx].CI_Info.dwAddr ));
+ SAFE_DELETE(gRenderTextureInfos[idx].pRenderTexture) ;
+
+ return idx;
+}
+
+
+void FrameBufferManager::SetRenderTexture(void)
+{
+ memcpy(&(newRenderTextureInfo.CI_Info), &g_CI, sizeof(SetImgInfo));
+
+ newRenderTextureInfo.N64Width = newRenderTextureInfo.CI_Info.dwWidth;
+ newRenderTextureInfo.knownHeight = ComputeCImgHeight(g_CI, newRenderTextureInfo.N64Height);
+
+ status.bHandleN64RenderTexture = true;
+ newRenderTextureInfo.maxUsedHeight = 0;
+
+ if( defaultRomOptions.bInN64Resolution )
+ {
+ newRenderTextureInfo.bufferWidth = newRenderTextureInfo.N64Width;
+ newRenderTextureInfo.bufferHeight = newRenderTextureInfo.N64Height;
+ }
+ else if( defaultRomOptions.bDoubleSizeForSmallTxtrBuf && newRenderTextureInfo.N64Width<=128 && newRenderTextureInfo.N64Height<=128)
+ {
+ newRenderTextureInfo.bufferWidth = newRenderTextureInfo.N64Width*2;
+ newRenderTextureInfo.bufferHeight = newRenderTextureInfo.N64Height*2;
+ }
+ else
+ {
+ newRenderTextureInfo.bufferWidth = newRenderTextureInfo.N64Width;
+ newRenderTextureInfo.bufferHeight = newRenderTextureInfo.N64Height;
+ }
+
+ newRenderTextureInfo.scaleX = newRenderTextureInfo.bufferWidth / float(newRenderTextureInfo.N64Width);
+ newRenderTextureInfo.scaleY = newRenderTextureInfo.bufferHeight / float(newRenderTextureInfo.N64Height);
+
+ status.bFrameBufferIsDrawn = false;
+ status.bFrameBufferDrawnByTriangles = false;
+
+ newRenderTextureInfo.updateAtFrame = status.gDlistCount;
+ newRenderTextureInfo.updateAtUcodeCount = status.gUcodeCount;
+
+ // Delay activation of the render_texture until the 1st rendering
+
+ TXTRBUF_DUMP(TRACE1("Set render_texture: addr=%08X", g_CI.dwAddr));
+ DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_RENDER_TEXTURE,
+ {DebuggerAppendMsg("Paused after setting render_texture:\nAddr: 0x%08x, Fmt: %s Size: %s Width: %d, Height:%d",
+ g_CI.dwAddr, pszImgFormat[g_CI.dwFormat], pszImgSize[g_CI.dwSize], g_CI.dwWidth, g_pRenderTextureInfo->N64Height);});
+}
+
+int FrameBufferManager::SetBackBufferAsRenderTexture(SetImgInfo &CIinfo, int ciInfoIdx)
+{
+ // MUDLORD:
+ // OK, heres the drill!
+ //
+ // We set the graphics card's back buffer's contents as a render_texure
+ // This is done due to how the current framebuffer implementation detects
+ // changes to the backbuffer memory pointer and then we do a texture
+ // copy. This might be slow since it doesnt use hardware auxillary buffers
+
+ RenderTextureInfo tempRenderTextureInfo;
+
+ memcpy(&(tempRenderTextureInfo.CI_Info), &CIinfo, sizeof(SetImgInfo));
+
+ tempRenderTextureInfo.N64Width = g_uRecentCIInfoPtrs[ciInfoIdx]->dwLastWidth;
+ tempRenderTextureInfo.N64Height = g_uRecentCIInfoPtrs[ciInfoIdx]->dwLastHeight;
+ tempRenderTextureInfo.knownHeight = true;
+ tempRenderTextureInfo.maxUsedHeight = 0;
+
+ tempRenderTextureInfo.bufferWidth = windowSetting.uDisplayWidth;
+ tempRenderTextureInfo.bufferHeight = windowSetting.uDisplayHeight;
+
+ tempRenderTextureInfo.scaleX = tempRenderTextureInfo.bufferWidth / float(tempRenderTextureInfo.N64Width);
+ tempRenderTextureInfo.scaleY = tempRenderTextureInfo.bufferHeight / float(tempRenderTextureInfo.N64Height);
+
+ status.bFrameBufferIsDrawn = false;
+ status.bFrameBufferDrawnByTriangles = false;
+
+ tempRenderTextureInfo.updateAtFrame = status.gDlistCount;
+ tempRenderTextureInfo.updateAtUcodeCount = status.gUcodeCount;
+
+ // Checking against previous render_texture infos
+ //uint32 memsize = ((tempRenderTextureInfo.N64Height*tempRenderTextureInfo.N64Width)>>1)<<tempRenderTextureInfo.CI_Info.dwSize;
+ int matchidx = CheckRenderTexturesWithNewCI(CIinfo,tempRenderTextureInfo.N64Height,false);
+ int idxToUse = (matchidx >= 0) ? matchidx : FindASlot();
+
+ if( gRenderTextureInfos[idxToUse].pRenderTexture == NULL || matchidx < 0 )
+ {
+ gRenderTextureInfos[idxToUse].pRenderTexture =
+ new COGLRenderTexture(tempRenderTextureInfo.bufferWidth, tempRenderTextureInfo.bufferHeight, &gRenderTextureInfos[idxToUse], AS_BACK_BUFFER_SAVE);
+ }
+
+ // Need to set all variables for gRenderTextureInfos[idxToUse]
+ CRenderTexture *pRenderTexture = gRenderTextureInfos[idxToUse].pRenderTexture;
+ memcpy(&gRenderTextureInfos[idxToUse], &tempRenderTextureInfo, sizeof(RenderTextureInfo) );
+ gRenderTextureInfos[idxToUse].pRenderTexture = pRenderTexture;
+ gRenderTextureInfos[idxToUse].isUsed = true;
+ gRenderTextureInfos[idxToUse].txtEntry.pTexture = pRenderTexture->m_pTexture;
+ gRenderTextureInfos[idxToUse].txtEntry.txtrBufIdx = idxToUse+1;
+
+ TXTRBUF_DUMP(TRACE2("Set back buf as render_texture %d, addr=%08X", idxToUse, CIinfo.dwAddr));
+ DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_RENDER_TEXTURE,
+ {DebuggerAppendMsg("Paused after setting render_texture:\nAddr: 0x%08x, Fmt: %s Size: %s Width: %d, Height:%d",
+ CIinfo.dwAddr, pszImgFormat[CIinfo.dwFormat], pszImgSize[CIinfo.dwSize], CIinfo.dwWidth, g_pRenderTextureInfo->N64Height);});
+
+ return idxToUse;
+}
+
+void FrameBufferManager::CloseRenderTexture(bool toSave)
+{
+ if( m_curRenderTextureIndex < 0 )
+ return;
+
+ status.bHandleN64RenderTexture = false;
+ if( status.bDirectWriteIntoRDRAM )
+ {
+ // TODO: Implement
+ }
+ else
+ {
+ RestoreNormalBackBuffer();
+ if( !toSave || !status.bFrameBufferIsDrawn || !status.bFrameBufferDrawnByTriangles )
+ {
+ TXTRBUF_DUMP(TRACE0("Closing render_texture without save"););
+ SAFE_DELETE(gRenderTextureInfos[m_curRenderTextureIndex].pRenderTexture);
+ gRenderTextureInfos[m_curRenderTextureIndex].isUsed = false;
+ TXTRBUF_DUMP(TRACE1("Delete render_texture %d",m_curRenderTextureIndex););
+ }
+ else
+ {
+ TXTRBUF_DUMP(TRACE1("Closing render_texture %d", m_curRenderTextureIndex););
+ StoreRenderTextureToRDRAM();
+
+ if( frameBufferOptions.bRenderTextureWriteBack )
+ {
+ SAFE_DELETE(gRenderTextureInfos[m_curRenderTextureIndex].pRenderTexture);
+ gRenderTextureInfos[m_curRenderTextureIndex].isUsed = false;
+ TXTRBUF_DUMP(TRACE1("Delete render_texture %d after writing back to RDRAM",m_curRenderTextureIndex););
+ }
+ else
+ {
+ g_pRenderTextureInfo->crcInRDRAM = ComputeRenderTextureCRCInRDRAM(m_curRenderTextureIndex);
+ g_pRenderTextureInfo->crcCheckedAtFrame = status.gDlistCount;
+ }
+ }
+ }
+
+ SetScreenMult(windowSetting.uDisplayWidth/windowSetting.fViWidth, windowSetting.uDisplayHeight/windowSetting.fViHeight);
+ CRender::g_pRender->UpdateClipRectangle();
+ CRender::g_pRender->ApplyScissorWithClipRatio();
+
+ DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_RENDER_TEXTURE,
+ {
+ DebuggerAppendMsg("Paused after saving render_texture %d:\nAddr: 0x%08x, Fmt: %s Size: %s Width: %d", m_curRenderTextureIndex,
+ g_pRenderTextureInfo->CI_Info.dwAddr, pszImgFormat[g_pRenderTextureInfo->CI_Info.dwFormat], pszImgSize[g_pRenderTextureInfo->CI_Info.dwSize], g_pRenderTextureInfo->CI_Info.dwWidth);
+ });
+}
+
+void FrameBufferManager::ClearN64FrameBufferToBlack(uint32 left, uint32 top, uint32 width, uint32 height)
+{
+ RecentCIInfo &p = *(g_uRecentCIInfoPtrs[0]);
+ uint16 *frameBufferBase = (uint16*)(g_pRDRAMu8+p.dwAddr);
+ uint32 pitch = p.dwWidth;
+
+ if( width == 0 || height == 0 )
+ {
+ uint32 len = p.dwHeight*p.dwWidth*p.dwSize;
+ if( p.dwSize == TXT_SIZE_4b ) len = (p.dwHeight*p.dwWidth)>>1;
+ memset(frameBufferBase, 0, len);
+ }
+ else
+ {
+ for( uint32 y=0; y<height; y++)
+ {
+ for( uint32 x=0; x<width; x++ )
+ {
+ *(frameBufferBase+(y+top)*pitch+x+left) = 0;
+ }
+ }
+ }
+}
+
+uint8 RevTlutTable[0x10000];
+bool RevTlutTableNeedUpdate = false;
+void InitTlutReverseLookup(void)
+{
+ if( RevTlutTableNeedUpdate )
+ {
+ memset(RevTlutTable, 0, 0x10000);
+ for( int i=0; i<=0xFF; i++ )
+ {
+ RevTlutTable[g_wRDPTlut[i]] = uint8(i);
+ }
+
+ RevTlutTableNeedUpdate = false;
+ }
+}
+
+
+// Copies backbuffer to N64 framebuffer by notification by emu core
+// **buggy**
+void FrameBufferManager::CopyBackToFrameBufferIfReadByCPU(uint32 addr)
+{
+ int i = FindRecentCIInfoIndex(addr);
+ if( i != -1 )
+ {
+ //if( i == 0 ) CGraphicsContext::Get()->UpdateFrame();
+ RecentCIInfo *info = g_uRecentCIInfoPtrs[i];
+ StoreBackBufferToRDRAM( info->dwAddr, info->dwFormat, info->dwSize, info->dwWidth, info->dwHeight,
+ windowSetting.uDisplayWidth, windowSetting.uDisplayHeight, addr, 0x1000-addr%0x1000);
+ TRACE1("Copy back for CI Addr=%08X", info->dwAddr);
+ }
+}
+
+// We do these checks to see if a render_texture operation is occurring...
+void FrameBufferManager::CheckRenderTextureCRCInRDRAM(void)
+{
+ for( int i=0; i<numOfTxtBufInfos; i++ )
+ {
+ if( !gRenderTextureInfos[i].isUsed )
+ continue;
+
+ if( gRenderTextureInfos[i].pRenderTexture->IsBeingRendered() )
+ continue;
+
+ if( gRenderTextureInfos[i].crcCheckedAtFrame < status.gDlistCount )
+ {
+ uint32 crc = ComputeRenderTextureCRCInRDRAM(i);
+ if( gRenderTextureInfos[i].crcInRDRAM != crc )
+ {
+ // RDRAM has been modified by CPU core
+ TXTRBUF_DUMP(TRACE2("Delete txtr buf %d at %08X, CRC in RDRAM changed", i, gRenderTextureInfos[i].CI_Info.dwAddr ));
+ SAFE_DELETE(gRenderTextureInfos[i].pRenderTexture);
+ gRenderTextureInfos[i].isUsed = false;
+ continue;
+ }
+ else
+ {
+ gRenderTextureInfos[i].crcCheckedAtFrame = status.gDlistCount;
+ }
+ }
+ }
+}
+
+// Check render_texture memory addresses
+int FrameBufferManager::CheckAddrInRenderTextures(uint32 addr, bool checkcrc)
+{
+ for( int i=0; i<numOfTxtBufInfos; i++ )
+ {
+ if( !gRenderTextureInfos[i].isUsed )
+ continue;
+
+ if( gRenderTextureInfos[i].pRenderTexture->IsBeingRendered() )
+ continue;
+
+ uint32 bufHeight = gRenderTextureInfos[i].knownHeight ? gRenderTextureInfos[i].N64Height : gRenderTextureInfos[i].maxUsedHeight;
+ uint32 bufMemSize = gRenderTextureInfos[i].CI_Info.dwSize*gRenderTextureInfos[i].N64Width*bufHeight;
+ if( addr >=gRenderTextureInfos[i].CI_Info.dwAddr && addr < gRenderTextureInfos[i].CI_Info.dwAddr+bufMemSize)
+ {
+ if(checkcrc)
+ {
+ // Check the CRC in RDRAM
+ if( gRenderTextureInfos[i].crcCheckedAtFrame < status.gDlistCount )
+ {
+ uint32 crc = ComputeRenderTextureCRCInRDRAM(i);
+ if( gRenderTextureInfos[i].crcInRDRAM != crc )
+ {
+ // RDRAM has been modified by CPU core
+ TRACE3("Buf %d CRC in RDRAM changed from %08X to %08X", i, gRenderTextureInfos[i].crcInRDRAM, crc );
+ TXTRBUF_DUMP(TRACE2("Delete txtr buf %d at %08X, crcInRDRAM failed.", i, gRenderTextureInfos[i].CI_Info.dwAddr ));
+ SAFE_DELETE(gRenderTextureInfos[i].pRenderTexture);
+ gRenderTextureInfos[i].isUsed = false;
+ continue;
+ }
+ else
+ {
+ gRenderTextureInfos[i].crcCheckedAtFrame = status.gDlistCount;
+ }
+ }
+ }
+
+ TXTRBUF_DUMP(TRACE2("Loading texture addr = %08X from txtr buf %d", addr, i));
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+// Load texture from render_texture buffer
+void FrameBufferManager::LoadTextureFromRenderTexture(TxtrCacheEntry* pEntry, int infoIdx)
+{
+ if( infoIdx < 0 || infoIdx >= numOfTxtBufInfos )
+ {
+ infoIdx = CheckAddrInRenderTextures(pEntry->ti.Address);
+ }
+
+ if( infoIdx >= 0 && gRenderTextureInfos[infoIdx].isUsed && gRenderTextureInfos[infoIdx].pRenderTexture )
+ {
+ TXTRBUF_DUMP(TRACE1("Loading from render_texture %d", infoIdx));
+ gRenderTextureInfos[infoIdx].pRenderTexture->LoadTexture(pEntry);
+ }
+}
+
+void FrameBufferManager::RestoreNormalBackBuffer()
+{
+ if( m_curRenderTextureIndex >= 0 && m_curRenderTextureIndex < numOfTxtBufInfos )
+ {
+ if( gRenderTextureInfos[m_curRenderTextureIndex].pRenderTexture )
+ gRenderTextureInfos[m_curRenderTextureIndex].pRenderTexture->SetAsRenderTarget(false);
+ m_isRenderingToTexture = false;
+ m_lastTextureBufferIndex = m_curRenderTextureIndex;
+ }
+
+ if( !status.bFrameBufferIsDrawn || !status.bFrameBufferDrawnByTriangles )
+ {
+ gRenderTextureInfos[m_curRenderTextureIndex].isUsed = false;
+ TXTRBUF_DUMP(TRACE2("Delete txtr buf %d at %08X, it is never rendered", m_curRenderTextureIndex, gRenderTextureInfos[m_curRenderTextureIndex].CI_Info.dwAddr ));
+ SAFE_DELETE(gRenderTextureInfos[m_curRenderTextureIndex].pRenderTexture);
+ }
+}
+
+uint32 FrameBufferManager::ComputeRenderTextureCRCInRDRAM(int infoIdx)
+{
+ if( infoIdx >= numOfTxtBufInfos || infoIdx < 0 || !gRenderTextureInfos[infoIdx].isUsed )
+ return 0;
+
+ RenderTextureInfo &info = gRenderTextureInfos[infoIdx];
+ uint32 height = info.knownHeight ? info.N64Height : info.maxUsedHeight;
+ uint8 *pAddr = (uint8*)(g_pRDRAMu8+info.CI_Info.dwAddr);
+ uint32 pitch = (info.N64Width << info.CI_Info.dwSize ) >> 1;
+
+ return CalculateRDRAMCRC(pAddr, 0, 0, info.N64Width, height, info.CI_Info.dwSize, pitch);
+}
+
+// Activates texture buffer for drawing
+void FrameBufferManager::ActiveTextureBuffer(void)
+{
+ status.bCIBufferIsRendered = true;
+
+ if( status.bHandleN64RenderTexture )
+ {
+ // Checking against previous render_texture infos
+ int matchidx = -1;
+
+ //uint32 memsize = ((newRenderTextureInfo.N64Height*newRenderTextureInfo.N64Width)>>1)<<newRenderTextureInfo.CI_Info.dwSize;
+
+ matchidx = CheckRenderTexturesWithNewCI(g_CI,newRenderTextureInfo.N64Height,true);
+
+ int idxToUse=-1;
+ if( matchidx >= 0 )
+ {
+ // Reuse the matched slot
+ idxToUse = matchidx;
+ }
+ else
+ {
+ idxToUse = FindASlot();
+ }
+
+ if( gRenderTextureInfos[idxToUse].pRenderTexture == NULL || matchidx < 0 )
+ {
+ int w = newRenderTextureInfo.bufferWidth;
+ if( newRenderTextureInfo.knownHeight == RDP_SETSCISSOR && newRenderTextureInfo.CI_Info.dwAddr == g_ZI.dwAddr )
+ {
+ w = gRDP.scissor.right;
+ }
+
+ gRenderTextureInfos[idxToUse].pRenderTexture =
+ new COGLRenderTexture(w, newRenderTextureInfo.bufferHeight, &gRenderTextureInfos[idxToUse], AS_RENDER_TARGET);
+ }
+
+ // Need to set all variables for gRenderTextureInfos[idxToUse]
+ CRenderTexture *pRenderTexture = gRenderTextureInfos[idxToUse].pRenderTexture;
+ memcpy(&gRenderTextureInfos[idxToUse], &newRenderTextureInfo, sizeof(RenderTextureInfo) );
+ gRenderTextureInfos[idxToUse].pRenderTexture = pRenderTexture;
+ gRenderTextureInfos[idxToUse].isUsed = true;
+ gRenderTextureInfos[idxToUse].txtEntry.pTexture = pRenderTexture->m_pTexture;
+ gRenderTextureInfos[idxToUse].txtEntry.txtrBufIdx = idxToUse+1;
+
+ g_pRenderTextureInfo = &gRenderTextureInfos[idxToUse];
+
+ // Active the render_texture
+ if( m_curRenderTextureIndex >= 0 && gRenderTextureInfos[m_curRenderTextureIndex].isUsed && gRenderTextureInfos[m_curRenderTextureIndex].pRenderTexture )
+ {
+ gRenderTextureInfos[m_curRenderTextureIndex].pRenderTexture->SetAsRenderTarget(false);
+ m_isRenderingToTexture = false;
+ }
+
+ if( gRenderTextureInfos[idxToUse].pRenderTexture->SetAsRenderTarget(true) )
+ {
+ m_isRenderingToTexture = true;
+
+ //Clear(CLEAR_COLOR_AND_DEPTH_BUFFER,0x80808080,1.0f);
+ if( frameBufferOptions.bFillRectNextTextureBuffer )
+ CGraphicsContext::g_pGraphicsContext->Clear(CLEAR_COLOR_BUFFER,gRDP.fillColor,1.0f);
+ else if( options.enableHackForGames == HACK_FOR_MARIO_TENNIS && g_pRenderTextureInfo->N64Width > 64 && g_pRenderTextureInfo->N64Width < 300 )
+ {
+ CGraphicsContext::g_pGraphicsContext->Clear(CLEAR_COLOR_BUFFER,0,1.0f);
+ }
+ else if( options.enableHackForGames == HACK_FOR_MARIO_TENNIS && g_pRenderTextureInfo->N64Width < 64 && g_pRenderTextureInfo->N64Width > 32 )
+ {
+ CGraphicsContext::g_pGraphicsContext->Clear(CLEAR_COLOR_BUFFER,0,1.0f);
+ }
+
+ m_curRenderTextureIndex = idxToUse;
+
+ status.bDirectWriteIntoRDRAM = false;
+
+ //SetScreenMult(1, 1);
+ SetScreenMult(gRenderTextureInfos[m_curRenderTextureIndex].scaleX, gRenderTextureInfos[m_curRenderTextureIndex].scaleY);
+ CRender::g_pRender->UpdateClipRectangle();
+
+ // If needed, draw RDRAM into the render_texture
+ //if( frameBufferOptions.bLoadRDRAMIntoRenderTexture )
+ //{
+ // CRender::GetRender()->LoadTxtrBufFromRDRAM();
+ //}
+ }
+ else
+ {
+ if( CDeviceBuilder::m_deviceGeneralType == DIRECTX_DEVICE )
+ {
+ TRACE1("Error to set Render Target: %d", idxToUse);
+ TRACE1("Addr = %08X", gRenderTextureInfos[idxToUse].CI_Info.dwAddr);
+ TRACE2("Width = %d, Height=%d", gRenderTextureInfos[idxToUse].N64Width, gRenderTextureInfos[idxToUse].N64Height);
+ }
+ }
+
+
+ TXTRBUF_DUMP(TRACE2("Rendering to render_texture %d, addr=%08X", idxToUse, g_CI.dwAddr));
+ DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_RENDER_TEXTURE,
+ {DebuggerAppendMsg("Paused after activating render_texture:\nAddr: 0x%08x, Fmt: %s Size: %s Width: %d, Height:%d",
+ g_CI.dwAddr, pszImgFormat[g_CI.dwFormat], pszImgSize[g_CI.dwSize], g_CI.dwWidth, g_pRenderTextureInfo->N64Height);});
+ }
+ else
+ {
+ UpdateRecentCIAddr(g_CI);
+ CheckRenderTexturesWithNewCI(g_CI,gRDP.scissor.bottom,false);
+ }
+}
+
+#define SAVE_CI {g_CI.dwAddr = newCI.dwAddr;g_CI.dwFormat = newCI.dwFormat;g_CI.dwSize = newCI.dwSize;g_CI.dwWidth = newCI.dwWidth;g_CI.bpl=newCI.bpl;}
+
+// Sets CI address for framebuffer copies
+void FrameBufferManager::Set_CI_addr(SetImgInfo &newCI)
+{
+ bool wasDrawingTextureBuffer = status.bN64IsDrawingTextureBuffer;
+ status.bN64IsDrawingTextureBuffer = ( newCI.dwSize != TXT_SIZE_16b || newCI.dwFormat != TXT_FMT_RGBA || newCI.dwWidth < 200 || ( newCI.dwAddr != g_ZI.dwAddr && newCI.dwWidth != 512 && !g_pFrameBufferManager->HasAddrBeenDisplayed(newCI.dwAddr, newCI.dwWidth)) );
+ status.bN64FrameBufferIsUsed = status.bN64IsDrawingTextureBuffer;
+
+ if( !wasDrawingTextureBuffer && g_CI.dwAddr == g_ZI.dwAddr && status.bCIBufferIsRendered )
+ {
+ TXTRBUF_DUMP(TRACE0("ZI is rendered"));
+
+ if( options.enableHackForGames != HACK_FOR_CONKER && g_uRecentCIInfoPtrs[0]->bCopied == false )
+ {
+ // Conker is not actually using a backbuffer
+ g_pFrameBufferManager->UpdateRecentCIAddr(g_CI);
+ if( status.leftRendered != -1 && status.topRendered != -1 && status.rightRendered != -1 && status.bottomRendered != -1 )
+ {
+ RECT rect={status.leftRendered,status.topRendered,status.rightRendered,status.bottomRendered};
+ g_pFrameBufferManager->SaveBackBuffer(0,&rect);
+ }
+ else
+ {
+ g_pFrameBufferManager->SaveBackBuffer(0,NULL);
+ }
+ }
+ }
+
+ frameBufferOptions.bFillRectNextTextureBuffer = false;
+ if( g_CI.dwAddr == newCI.dwAddr && status.bHandleN64RenderTexture && (g_CI.dwFormat != newCI.dwFormat || g_CI.dwSize != newCI.dwSize || g_CI.dwWidth != newCI.dwWidth ) )
+ {
+ // Mario Tennis player shadow
+ g_pFrameBufferManager->CloseRenderTexture(true);
+ if( options.enableHackForGames == HACK_FOR_MARIO_TENNIS )
+ frameBufferOptions.bFillRectNextTextureBuffer = true; // Hack for Mario Tennis
+ }
+
+ SAVE_CI;
+
+ if( g_CI.dwAddr == g_ZI.dwAddr && !status.bN64IsDrawingTextureBuffer )
+ {
+ if( g_pFrameBufferManager->IsDIaRenderTexture() )
+ {
+ status.bN64IsDrawingTextureBuffer = true;
+ status.bN64FrameBufferIsUsed = status.bN64IsDrawingTextureBuffer;
+ }
+ }
+
+ status.bCIBufferIsRendered = false;
+ status.leftRendered = status.topRendered = status.rightRendered = status.bottomRendered = -1;
+
+ if( currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_CI_CHANGE && !status.bN64IsDrawingTextureBuffer )
+ {
+ if( status.curRenderBuffer == 0 )
+ {
+ status.curRenderBuffer = g_CI.dwAddr;
+ }
+ else if( status.curRenderBuffer != g_CI.dwAddr )
+ {
+ status.curDisplayBuffer = status.curRenderBuffer;
+ CGraphicsContext::Get()->UpdateFrame();
+ status.curRenderBuffer = g_CI.dwAddr;
+ DEBUGGER_IF_DUMP(pauseAtNext,{DebuggerAppendMsg("Screen Update because CI change to %08X, Display Buf=%08X", status.curRenderBuffer, status.curDisplayBuffer);});
+ }
+ }
+
+ if( frameBufferOptions.bAtEachFrameUpdate && !status.bHandleN64RenderTexture )
+ {
+ if( status.curRenderBuffer != g_CI.dwAddr )
+ {
+ if( status.gDlistCount%(currentRomOptions.N64FrameBufferWriteBackControl+1) == 0 )
+ {
+ g_pFrameBufferManager->StoreBackBufferToRDRAM(status.curRenderBuffer,
+ newCI.dwFormat, newCI.dwSize, windowSetting.uViWidth, windowSetting.uViHeight,
+ windowSetting.uDisplayWidth, windowSetting.uDisplayHeight);
+ }
+ }
+
+ //status.curDisplayBuffer = status.curRenderBuffer;
+ status.curRenderBuffer = g_CI.dwAddr;
+ }
+
+
+ switch( currentRomOptions.N64RenderToTextureEmuType )
+ {
+ case TXT_BUF_NONE:
+ if( status.bHandleN64RenderTexture )
+ g_pFrameBufferManager->CloseRenderTexture(false);
+ status.bHandleN64RenderTexture = false; // Don't handle N64 render_texture stuffs
+ if( !status.bN64IsDrawingTextureBuffer )
+ g_pFrameBufferManager->UpdateRecentCIAddr(g_CI);
+ break;
+ default:
+ if( status.bHandleN64RenderTexture )
+ {
+#ifdef DEBUGGER
+ if( pauseAtNext && eventToPause == NEXT_RENDER_TEXTURE )
+ {
+ pauseAtNext = TRUE;
+ eventToPause = NEXT_RENDER_TEXTURE;
+ }
+#endif
+ g_pFrameBufferManager->CloseRenderTexture(true);
+ }
+
+ status.bHandleN64RenderTexture = status.bN64IsDrawingTextureBuffer;
+ if( status.bHandleN64RenderTexture )
+ {
+ if( options.enableHackForGames != HACK_FOR_BANJO_TOOIE )
+ {
+ g_pFrameBufferManager->SetRenderTexture();
+ }
+ }
+ else
+ {
+#ifdef DEBUGGER
+ if( g_CI.dwWidth == 512 && pauseAtNext && (eventToPause==NEXT_OBJ_BG || eventToPause==NEXT_SET_CIMG) )
+ {
+ DebuggerAppendMsg("Warning SetCImg: new Addr=0x%08X, fmt:%s size=%sb, Width=%d\n",
+ g_CI.dwAddr, pszImgFormat[newCI.dwFormat], pszImgSize[newCI.dwSize], newCI.dwWidth);
+ }
+#endif
+ //g_pFrameBufferManager->UpdateRecentCIAddr(g_CI); // Delay this until the CI buffer is actally drawn
+ }
+ break;
+ }
+
+ TXTRBUF_DUMP(TRACE4("SetCImg : Addr=0x%08X, Fmt:%s-%sb, Width=%d\n",
+ g_CI.dwAddr, pszImgFormat[newCI.dwFormat], pszImgSize[newCI.dwSize], newCI.dwWidth));
+
+ DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG,
+ {
+ DebuggerAppendMsg("Pause after SetCImg: Addr=0x%08X, Fmt:%s-%sb, Width=%d\n",
+ g_CI.dwAddr, pszImgFormat[newCI.dwFormat], pszImgSize[newCI.dwSize], newCI.dwWidth);
+ }
+ );
+}
+
+
+void FrameBufferManager::StoreRenderTextureToRDRAM(int infoIdx)
+{
+ if( !frameBufferOptions.bRenderTextureWriteBack )
+ return;
+
+ if( infoIdx < 0 )
+ infoIdx = m_lastTextureBufferIndex;
+
+ if( !gRenderTextureInfos[infoIdx].pRenderTexture )
+ return;
+
+ if( gRenderTextureInfos[infoIdx].pRenderTexture->IsBeingRendered() )
+ {
+ TXTRBUF_DUMP(TRACE1("Cannot SaveTextureBuffer %d, it is being rendered", infoIdx));
+ return;
+ }
+
+ gRenderTextureInfos[infoIdx].pRenderTexture->StoreToRDRAM(infoIdx);
+}
+
+
+//does FB copy to N64 RDAM structure
+void FrameBufferManager::CopyBufferToRDRAM(uint32 addr, uint32 fmt, uint32 siz, uint32 width, uint32 height, uint32 bufWidth, uint32 bufHeight, uint32 startaddr, uint32 memsize, uint32 pitch, TextureFmt bufFmt, void *buffer, uint32 bufPitch)
+{
+ uint32 startline=0;
+
+ if( startaddr == 0xFFFFFFFF )
+ startaddr = addr;
+
+ startline = (startaddr-addr)/siz/pitch;
+ if( startline >= height )
+ {
+ //TRACE0("Warning: check me");
+ startline = height;
+ }
+
+ uint32 endline = height;
+ if( memsize != 0xFFFFFFFF )
+ {
+ endline = (startaddr+memsize-addr)/siz;
+ if( endline % pitch == 0 )
+ endline /= pitch;
+ else
+ endline = endline/pitch+1;
+ }
+ if( endline > height )
+ {
+ endline = height;
+ }
+
+ if( memsize != 0xFFFFFFFF )
+ {
+ TXTRBUF_DUMP(DebuggerAppendMsg("Start at: 0x%X, from line %d to %d", startaddr-addr, startline, endline););
+ }
+
+ int indexes[600];
+ {
+ float sx;
+ int sx0;
+ float ratio = bufWidth/(float)width;
+ for( uint32 j=0; j<width; j++ )
+ {
+ sx = j*ratio;
+ sx0 = int(sx+0.5);
+ indexes[j] = 4*sx0;
+ }
+ }
+
+ if( siz == TXT_SIZE_16b )
+ {
+ uint16 *frameBufferBase = (uint16*)(g_pRDRAMu8+addr);
+
+ if( bufFmt==TEXTURE_FMT_A8R8G8B8 )
+ {
+ int sy0;
+ float ratio = bufHeight/(float)height;
+
+ for( uint32 i=startline; i<endline; i++ )
+ {
+ sy0 = int(i*ratio+0.5);
+
+ uint16 *pD = frameBufferBase + i * pitch;
+ uint8 *pS0 = (uint8 *)buffer + sy0 * bufPitch;
+
+ for( uint32 j=0; j<width; j++ )
+ {
+ // Point
+ uint8 r = pS0[indexes[j]+2];
+ uint8 g = pS0[indexes[j]+1];
+ uint8 b = pS0[indexes[j]+0];
+ uint8 a = pS0[indexes[j]+3];
+
+ // Liner
+ *(pD+(j^1)) = ConvertRGBATo555( r, g, b, a);
+ }
+ }
+ }
+ else
+ {
+ TRACE1("Copy %sb FrameBuffer to Rdram, not implemented", pszImgSize[siz]);
+ }
+ }
+ else if( siz == TXT_SIZE_8b && fmt == TXT_FMT_CI )
+ {
+ uint8 *frameBufferBase = (uint8*)(g_pRDRAMu8+addr);
+
+ if( bufFmt==TEXTURE_FMT_A8R8G8B8 )
+ {
+ uint16 tempword;
+ InitTlutReverseLookup();
+
+ for( uint32 i=startline; i<endline; i++ )
+ {
+ uint8 *pD = frameBufferBase + i * width;
+ uint8 *pS = (uint8 *)buffer + i*bufHeight/height * bufPitch;
+ for( uint32 j=0; j<width; j++ )
+ {
+ int pos = 4*(j*bufWidth/width);
+ tempword = ConvertRGBATo555((pS[pos+2]), // Red
+ (pS[pos+1]), // Green
+ (pS[pos+0]), // Blue
+ (pS[pos+3])); // Alpha
+
+ //*pD = CIFindIndex(tempword);
+ *(pD+(j^3)) = RevTlutTable[tempword];
+ }
+ }
+ }
+ else
+ {
+ TRACE1("Copy %sb FrameBuffer to Rdram, not implemented", pszImgSize[siz]);
+ }
+ DEBUGGER_IF_DUMP(pauseAtNext,{DebuggerAppendMsg("Copy %sb FrameBuffer to Rdram", pszImgSize[siz]);});
+ }
+ else if( siz == TXT_SIZE_8b && fmt == TXT_FMT_I )
+ {
+ uint8 *frameBufferBase = (uint8*)(g_pRDRAMu8+addr);
+
+ if( bufFmt==TEXTURE_FMT_A8R8G8B8 )
+ {
+ int sy0;
+ float ratio = bufHeight/(float)height;
+
+ for( uint32 i=startline; i<endline; i++ )
+ {
+ sy0 = int(i*ratio+0.5);
+
+ uint8 *pD = frameBufferBase + i * width;
+ uint8 *pS0 = (uint8 *)buffer + sy0 * bufPitch;
+
+ for( uint32 j=0; j<width; j++ )
+ {
+ // Point
+ uint32 r = pS0[indexes[j]+2];
+ uint32 g = pS0[indexes[j]+1];
+ uint32 b = pS0[indexes[j]+0];
+
+ // Liner
+ *(pD+(j^3)) = (uint8)((r+b+g)/3);
+ }
+ }
+ }
+ else
+ {
+ //DebuggerAppendMsg("Copy %sb FrameBuffer to Rdram, not implemented", pszImgSize[siz]);
+ }
+ DEBUGGER_IF_DUMP(pauseAtNext,{DebuggerAppendMsg("Copy %sb FrameBuffer to Rdram", pszImgSize[siz]);});
+ }
+}
+
+
+#ifdef DEBUGGER
+void FrameBufferManager::DisplayRenderTexture(int infoIdx)
+{
+ if( infoIdx < 0 )
+ infoIdx = m_lastTextureBufferIndex;
+
+ if( gRenderTextureInfos[infoIdx].pRenderTexture )
+ {
+ if( gRenderTextureInfos[infoIdx].pRenderTexture->IsBeingRendered() )
+ {
+ TRACE1("Render texture %d is being rendered, cannot display", infoIdx);
+ }
+ else
+ {
+ TRACE1("Texture buffer %d:", infoIdx);
+ TRACE1("Addr=%08X", gRenderTextureInfos[infoIdx].CI_Info.dwAddr);
+ TRACE2("Width=%d, Created Height=%d", gRenderTextureInfos[infoIdx].N64Width,gRenderTextureInfos[infoIdx].N64Height);
+ TRACE2("Fmt=%d, Size=%d", gRenderTextureInfos[infoIdx].CI_Info.dwFormat,gRenderTextureInfos[infoIdx].CI_Info.dwSize);
+ }
+ }
+ else
+ {
+ TRACE1("Texture buffer %d is not used", infoIdx);
+ }
+}
+#endif
+
+
+
+// Saves backbuffer
+// this is the core to the current framebuffer code
+// We need to save backbuffer when changed by framebuffer
+// so that we can use it for framebuffer effects
+void FrameBufferManager::SaveBackBuffer(int ciInfoIdx, RECT* pSrcRect, bool forceToSaveToRDRAM)
+{
+ RecentCIInfo &ciInfo = *g_uRecentCIInfoPtrs[ciInfoIdx];
+
+ if( ciInfoIdx == 1 ) // to save the current front buffer
+ {
+ CGraphicsContext::g_pGraphicsContext->UpdateFrame(true);
+ }
+
+ if( frameBufferOptions.bWriteBackBufToRDRAM || forceToSaveToRDRAM )
+ {
+ uint32 width = ciInfo.dwWidth;
+ uint32 height = ciInfo.dwHeight;
+
+ if( ciInfo.dwWidth == *g_GraphicsInfo.VI_WIDTH_REG && ciInfo.dwWidth != windowSetting.uViWidth )
+ {
+ width = windowSetting.uViWidth;
+ height = windowSetting.uViHeight;
+ }
+
+ StoreBackBufferToRDRAM( ciInfo.dwAddr, ciInfo.dwFormat, ciInfo.dwSize, width, height,
+ windowSetting.uDisplayWidth, windowSetting.uDisplayHeight);
+
+ g_uRecentCIInfoPtrs[ciInfoIdx]->bCopied = true;
+ if( ciInfoIdx == 1 ) // to save the current front buffer
+ {
+ CGraphicsContext::g_pGraphicsContext->UpdateFrame(true);
+ }
+ return;
+ }
+
+
+ SetImgInfo tempinfo;
+ tempinfo.dwAddr = ciInfo.dwAddr;
+ tempinfo.dwFormat = ciInfo.dwFormat;
+ tempinfo.dwSize = ciInfo.dwSize;
+ tempinfo.dwWidth = ciInfo.dwWidth;
+
+ int idx = SetBackBufferAsRenderTexture(tempinfo, ciInfoIdx);
+
+ CopyBackBufferToRenderTexture(idx, ciInfo, pSrcRect);
+
+ gRenderTextureInfos[idx].crcCheckedAtFrame = status.gDlistCount;
+ gRenderTextureInfos[idx].crcInRDRAM = ComputeRenderTextureCRCInRDRAM(idx);
+
+ DEBUGGER_IF_DUMP((logTextureBuffer&&pSrcRect==NULL),TRACE1("SaveBackBuffer at 0x%08X", ciInfo.dwAddr));
+ DEBUGGER_IF_DUMP((logTextureBuffer&&pSrcRect),TRACE5("SaveBackBuffer at 0x%08X, {%d,%d -%d,%d)", ciInfo.dwAddr,
+ pSrcRect->left,pSrcRect->top,pSrcRect->right,pSrcRect->bottom));
+ DEBUGGER_IF_DUMP(( pauseAtNext && eventToPause == NEXT_RENDER_TEXTURE),{g_pFrameBufferManager->DisplayRenderTexture(idx);});
+
+ g_uRecentCIInfoPtrs[ciInfoIdx]->bCopied = true;
+}
+