diff --git a/src/gDP.cpp b/src/gDP.cpp index 89c8885fc..d0ff98e82 100644 --- a/src/gDP.cpp +++ b/src/gDP.cpp @@ -230,22 +230,28 @@ void gDPSetFillColor( u32 c ) DebugMsg( DEBUG_NORMAL, "gDPSetFillColor( 0x%08X );\n", c ); } -void gDPGetFillColor(f32 _fillColor[4]) +static void getFillColor(u32 c, f32 _fillColor[4]) { - const u32 c = gDP.fillColor.color; if (gDP.colorImage.size < 3) { - _fillColor[0] = _FIXED2FLOATCOLOR( _SHIFTR( c, 11, 5 ), 5 ); - _fillColor[1] = _FIXED2FLOATCOLOR( _SHIFTR( c, 6, 5 ), 5 ); - _fillColor[2] = _FIXED2FLOATCOLOR( _SHIFTR( c, 1, 5 ), 5 ); - _fillColor[3] = (f32)_SHIFTR( c, 0, 1 ); - } else { - _fillColor[0] = _FIXED2FLOATCOLOR( _SHIFTR( c, 24, 8 ), 8 ); - _fillColor[1] = _FIXED2FLOATCOLOR( _SHIFTR( c, 16, 8 ), 8 ); - _fillColor[2] = _FIXED2FLOATCOLOR( _SHIFTR( c, 8, 8 ), 8 ); - _fillColor[3] = _FIXED2FLOATCOLOR( _SHIFTR( c, 0, 8 ), 8 ); + _fillColor[0] = _FIXED2FLOATCOLOR(_SHIFTR(c, 11, 5), 5); + _fillColor[1] = _FIXED2FLOATCOLOR(_SHIFTR(c, 6, 5), 5); + _fillColor[2] = _FIXED2FLOATCOLOR(_SHIFTR(c, 1, 5), 5); + _fillColor[3] = (f32)_SHIFTR(c, 0, 1); + } + else { + _fillColor[0] = _FIXED2FLOATCOLOR(_SHIFTR(c, 24, 8), 8); + _fillColor[1] = _FIXED2FLOATCOLOR(_SHIFTR(c, 16, 8), 8); + _fillColor[2] = _FIXED2FLOATCOLOR(_SHIFTR(c, 8, 8), 8); + _fillColor[3] = _FIXED2FLOATCOLOR(_SHIFTR(c, 0, 8), 8); } } +void gDPGetFillColor(f32 _fillColor[4]) +{ + const u32 c = gDP.fillColor.color; + return getFillColor(c, _fillColor); +} + void gDPSetPrimColor( u32 m, u32 l, u32 r, u32 g, u32 b, u32 a ) { gDP.primColor.m = _FIXED2FLOAT( m, 5 ); @@ -942,6 +948,132 @@ void gDPSetScissor(u32 mode, s16 xh, s16 yh, s16 xl, s16 yl) #endif } +template +class BackupRestore +{ +public: + BackupRestore(T& _value) : value(_value), backup(_value) {} + ~BackupRestore() { value = backup; }; + +private: + T& value; + T backup; +}; + +// This performs the same thing action as gDPFillRectangle but explicitly specifying what to overwrite +void gDPMemset(u32 value, u32 addr, u32 length) +{ + // TODO: Can this be different? + u32 screenWidth = VI.width; + u32 screenHeight = VI.height; + + GraphicsDrawer& drawer = dwnd().getDrawer(); + u32 fillColor = value | (value << 16); + u32 depthImageStart = gDP.depthImageAddress; + u32 depthImageEnd = gDP.depthImageAddress + 320 * 240 * 2; + if (depthImageStart <= addr && addr < depthImageEnd) + { + // ZB clear + gDP.rectColor.r = _FIXED2FLOATCOLOR(_SHIFTR(fillColor, 11, 5), 5); + gDP.rectColor.g = _FIXED2FLOATCOLOR(_SHIFTR(fillColor, 6, 5), 5); + gDP.rectColor.b = _FIXED2FLOATCOLOR(_SHIFTR(fillColor, 1, 5), 5); + gDP.rectColor.a = (f32)_SHIFTR(fillColor, 0, 1); + + // HACK: this usually replaces gDPSetColorImage for zb so save zb + frameBufferList().saveBuffer(gDP.depthImageAddress, (u16)G_IM_FMT_RGBA, (u16)G_IM_SIZ_16b, (u16)screenWidth, false); + + if (config.generalEmulation.enableFragmentDepthWrite == 0) + drawer.clearDepthBuffer(); + else + depthBufferList().setCleared(true); + + // Deduce ulx, uly, lrx, lry from addr, size, and colorImageStart + // Currently I am assuming a simple letterbox case - top and bottom are not cleared + u32 lineSize = screenWidth * 2; + u32 ulx = 0; + u32 uly = (addr - depthImageStart) / lineSize; + u32 lrx = 320; + u32 lry = uly + length / lineSize; + + if (config.generalEmulation.enableFragmentDepthWrite != 0) + { + // Pretend that we are drawing the rectangle over ZB with fill mode + BackupRestore backupColorImageAddress(gDP.colorImage.address); + BackupRestore backupWidth(gDP.colorImage.width); + unsigned int backupDepthSource = gDP.otherMode.depthSource; + unsigned int backupCycleType = gDP.otherMode.cycleType; + + gDP.colorImage.address = gDP.depthImageAddress; + gDP.colorImage.width = screenWidth; + gDP.otherMode.cycleType = G_CYC_FILL; + + drawer.drawRect(ulx, uly, lrx, lry); + frameBufferList().setBufferChanged(f32(lry)); + + gDP.otherMode.depthSource = backupDepthSource; + gDP.otherMode.cycleType = backupCycleType; + } + + // TODO: Following the same logic and not filling in zb values in RDRAM, why? + return; + } + + // Either RGBA16 or RGBA32. This heavily assume "niceness" of developers to bind color buffer before memset... + u32 colorSize = gDP.colorImage.size < 3 ? 2 : 4; + u32 colorImageStart = gDP.colorImage.address; + u32 colorImageEnd = gDP.colorImage.address + screenWidth * screenHeight * colorSize; + if (colorImageStart <= addr && addr < colorImageEnd) + { + // FB clear + f32 fillColor32[4]; + getFillColor(fillColor, fillColor32); + gDP.rectColor.r = fillColor32[0]; + gDP.rectColor.g = fillColor32[1]; + gDP.rectColor.b = fillColor32[2]; + gDP.rectColor.a = fillColor32[3]; + + u32 lineSize = colorSize * screenWidth; + u32 ulx = 0; + u32 uly = (addr - colorImageStart) / lineSize; + u32 lrx = 320; + u32 lry = uly + length / lineSize; + + { + BackupRestore backupFillColor(gDP.fillColor.color); + unsigned int backupDepthSource = gDP.otherMode.depthSource; + unsigned int backupCycleType = gDP.otherMode.cycleType; + + gDP.fillColor.color = fillColor; + gDP.otherMode.cycleType = G_CYC_FILL; + + drawer.drawRect(ulx, uly, lrx, lry); + if (auto cur = frameBufferList().getCurrent()) + cur->setBufferClearParams(gDP.fillColor.color, ulx, uly, lrx, lry); + + frameBufferList().setBufferChanged(f32(lry)); + + gDP.otherMode.depthSource = backupDepthSource; + gDP.otherMode.cycleType = backupCycleType; + } + + if (config.frameBufferEmulation.copyFromRDRAM != 0) + { + for (int i = 0; i < length / 4; i++) + { + *(u32*)(RDRAM + addr + i * 4) = fillColor; + } + } + } + else + { + // TODO: It is something we do not know about... I hope it is not a big deal to just memset it? + for (int i = 0; i < length / 4; i++) + { + *(u32*)(RDRAM + addr + i * 4) = fillColor; + } + } +} + void gDPFillRectangle( s32 ulx, s32 uly, s32 lrx, s32 lry ) { GraphicsDrawer & drawer = dwnd().getDrawer(); diff --git a/src/gDP.h b/src/gDP.h index 58928f530..2c1eeb766 100644 --- a/src/gDP.h +++ b/src/gDP.h @@ -280,6 +280,7 @@ void gDPLoadTile( u32 tile, u32 uls, u32 ult, u32 lrs, u32 lrt ); void gDPLoadBlock( u32 tile, u32 uls, u32 ult, u32 lrs, u32 dxt ); void gDPLoadTLUT( u32 tile, u32 uls, u32 ult, u32 lrs, u32 lrt ); void gDPSetScissor( u32 mode, s16 xh, s16 yh, s16 xl, s16 yl); +void gDPMemset(u32 value, u32 addr, u32 length); void gDPFillRectangle( s32 ulx, s32 uly, s32 lrx, s32 lry ); void gDPSetConvert( s32 k0, s32 k1, s32 k2, s32 k3, s32 k4, s32 k5 ); void gDPSetKeyR( u32 cR, u32 sR, u32 wR ); diff --git a/src/uCodes/F3DEX3.cpp b/src/uCodes/F3DEX3.cpp index 579a61f14..ed760a2ca 100644 --- a/src/uCodes/F3DEX3.cpp +++ b/src/uCodes/F3DEX3.cpp @@ -8,6 +8,8 @@ #define F3DEX3_BRANCH_WZ 0x04 +#define F3DEX3_MEMSET 0xD5 + #define F3DEX3_TRISTRIP 0x08 #define F3DEX3_TRIFAN 0x09 #define F3DEX3_LIGHTTORDP 0x0A @@ -248,6 +250,14 @@ static void F3DEX3_RelSegment(u32 w0, u32 w1) gSPRelSegment(_SHIFTR(w0, 2, 4), w1 & 0x00FFFFFF); } +static void F3DEX3_Memset(u32 w0, u32 w1) +{ + u32 value = (u16) gDP.half_1; + u32 addr = w1; + u32 length = w0 & 0x00FFFFFF; + gDPMemset(value, addr, length); +} + void F3DEX3_Init() { gSPSetupFunctions(); @@ -272,7 +282,6 @@ void F3DEX3_Init() GBI_SetGBI(G_POPMTX, F3DEX2_POPMTX, F3DEX2_PopMtx); GBI_SetGBI(G_TEXTURE, F3DEX2_TEXTURE, F3DEX2_Texture); GBI_SetGBI(G_DMA_IO, F3DEX2_DMA_IO, F3DEX2_DMAIO); - GBI_SetGBI(G_SPECIAL_1, F3DEX2_SPECIAL_1, F3DEX2_Special_1); GBI_SetGBI(G_SPECIAL_2, F3DEX2_SPECIAL_2, F3DEX2_Special_2); GBI_SetGBI(G_SPECIAL_3, F3DEX2_SPECIAL_3, F3DEX2_Special_3); @@ -287,4 +296,5 @@ void F3DEX3_Init() GBI_SetGBI(G_TRIFAN, F3DEX3_TRIFAN, F3DEX3_TriFan); GBI_SetGBI(G_LIGHTTORDP, F3DEX3_LIGHTTORDP, F3DEX3_LightToRDP); GBI_SetGBI(G_RELSEGMENT, F3DEX3_RELSEGMENT, F3DEX3_RelSegment); + GBI_SetGBI(G_SPECIAL_1, F3DEX3_MEMSET, F3DEX3_Memset); } diff --git a/src/windows/ZilmarAPIImpl_windows.cpp b/src/windows/ZilmarAPIImpl_windows.cpp index 61a9fbf5f..2ceb0e6f5 100644 --- a/src/windows/ZilmarAPIImpl_windows.cpp +++ b/src/windows/ZilmarAPIImpl_windows.cpp @@ -28,7 +28,7 @@ void PluginAPI::GetDllInfo(PLUGIN_INFO * PluginInfo) { PluginInfo->Version = 0x103; PluginInfo->Type = PLUGIN_TYPE_GFX; - sprintf(PluginInfo->Name, "ANGLE %s v4.3.14", pluginName, PLUGIN_REVISION); + sprintf(PluginInfo->Name, "ANGLE %s v4.3.15", pluginName, PLUGIN_REVISION); PluginInfo->NormalMemory = FALSE; PluginInfo->MemoryBswaped = TRUE; }