From 1e84099521377e58075bd55417093eca58e99a77 Mon Sep 17 00:00:00 2001 From: Sergey Lipskiy Date: Wed, 9 Jan 2019 21:54:31 +0700 Subject: [PATCH] Correct mapping of RDP frame buffer to output one. Code of RdpUpdate::update is based on angrylion's rdp_update(). Main visible difference: VI cuts several pixels on left and right of original image. Cite from angrylion's commit message: "rdp: new tests revealed the shocking truth: the VI aggresssively and regardlessly of any data alignment specificities cuts off either 7 or 8 pixels closest to h_start and h_end, so that nothing ever appears on the screen unless (h_end - h_start) is 16 pixels or greater (this removes marginal garbage graphics on the far right in Mario Tennis, Turok 2 Seeds of Evil intro and probably other games)" --- src/FrameBuffer.cpp | 117 +++++++++++++++++++++++++------------------- src/FrameBuffer.h | 34 +++++++++++++ 2 files changed, 102 insertions(+), 49 deletions(-) diff --git a/src/FrameBuffer.cpp b/src/FrameBuffer.cpp index d2f8d72e..b22f9d95 100644 --- a/src/FrameBuffer.cpp +++ b/src/FrameBuffer.cpp @@ -551,6 +551,7 @@ void FrameBufferList::init() gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer); m_prevColorImageHeight = 0; m_overscan.init(); + m_rdpUpdate.init(); } void FrameBufferList::destroy() { @@ -1028,31 +1029,21 @@ void FrameBufferList::_renderScreenSizeBuffer() gDP.changed |= CHANGED_SCISSOR; } -struct RdpUpdateResult { - u32 vi_vres; - u32 vi_hres; - u32 vi_v_start; - u32 vi_h_start; - u32 vi_x_start; - u32 vi_y_start; - u32 vi_x_add; - u32 vi_y_add; - u32 vi_width; - u32 vi_origin; - bool vi_lowerfield; - bool vi_fsaa; - bool vi_divot; - bool vi_ispal; -}; - -/* This function taken from angrylion's code and adopted for my needs */ -static -bool rdp_update(RdpUpdateResult & _result) +void FrameBufferList::RdpUpdate::init() { - static const u32 PRESCALE_WIDTH = 640; - static const u32 PRESCALE_HEIGHT = 625; - static u32 oldvstart = 0; - static bool prevwasblank = false; + oldvstart = 0U; + prevvicurrent = 0U; + prevwasblank = false; + prevserrate = false; + oldlowerfield = false; + emucontrolsvicurrent = -1; +} + +/* This function was taken from angrylion's code and adopted for my needs */ +bool FrameBufferList::RdpUpdate::update(RdpUpdateResult & _result) +{ + static const u32 PRESCALE_WIDTH = 640U; + static const u32 PRESCALE_HEIGHT = 625U; const u32 x_add = _SHIFTR(*REG.VI_X_SCALE, 0, 12); const u32 y_add = _SHIFTR(*REG.VI_Y_SCALE, 0, 12); @@ -1067,31 +1058,61 @@ bool rdp_update(RdpUpdateResult & _result) const u32 delta_y = y2 - y1; const u32 vitype = _SHIFTR( *REG.VI_STATUS, 0, 2 ); - const bool interlaced = (*REG.VI_STATUS & 0x40) != 0; - const bool lowerfield = interlaced ? y1 > oldvstart : false; - oldvstart = y1; + const bool serration_pulses = (*REG.VI_STATUS & 0x40) != 0; + const bool validinterlace = ((vitype & 2) != 0 ) && serration_pulses; + if (validinterlace && prevserrate && emucontrolsvicurrent < 0) + emucontrolsvicurrent = (*REG.VI_V_CURRENT_LINE & 1) != prevvicurrent ? 1 : 0; + + bool lowerfield = 0; + if (validinterlace) { + if (emucontrolsvicurrent == 1) + lowerfield = (*REG.VI_V_CURRENT_LINE & 1) == 0; + else if (!emucontrolsvicurrent) { + if (y1 == oldvstart) + lowerfield = !oldlowerfield; + else + lowerfield = y1 < oldvstart; + } + } + + oldlowerfield = lowerfield; + + if (validinterlace) { + prevserrate = true; + prevvicurrent = *REG.VI_V_CURRENT_LINE & 1; + oldvstart = y1; + } else + prevserrate = false; u32 hres = delta_x; u32 vres = delta_y; s32 h_start = x1 - (ispal ? 128 : 108); - s32 v_start = y1 - (ispal ? 47 : 37); + s32 v_start = (y1 - (ispal ? 44 : 34)) / 2; u32 x_start = _SHIFTR(*REG.VI_X_SCALE, 16, 12); u32 y_start = _SHIFTR(*REG.VI_Y_SCALE, 16, 12); + bool h_start_clamped = h_start < 0; if (h_start < 0) { x_start -= x_add * h_start; + hres += h_start; + h_start = 0; } - v_start >>= 1; - v_start &= -int(v_start >= 0); + + if (v_start < 0) { + y_start += (y_add * (u32)(-v_start)); + v_start = 0; + } + vres >>= 1; - if (hres > PRESCALE_WIDTH - h_start) + const bool hres_clamped = hres + h_start > PRESCALE_WIDTH; + if (hres_clamped) hres = PRESCALE_WIDTH - h_start; - if (vres > PRESCALE_HEIGHT - v_start) + if (vres + v_start > PRESCALE_HEIGHT) vres = PRESCALE_HEIGHT - v_start; - s32 vactivelines = v_sync - (ispal ? 47 : 37); + s32 vactivelines = v_sync - (ispal ? 44 : 34); if (vactivelines > PRESCALE_HEIGHT) { LOG(LOG_MINIMAL, "VI_V_SYNC_REG too big\n"); return false; @@ -1105,7 +1126,7 @@ bool rdp_update(RdpUpdateResult & _result) if (hres <= 0 || vres <= 0 || ((vitype & 2) == 0 && prevwasblank)) /* early return. */ return false; - if (vitype >> 1 == 0) { + if ((vitype & 2) == 0) { prevwasblank = true; return false; } @@ -1121,6 +1142,8 @@ bool rdp_update(RdpUpdateResult & _result) _result.vi_y_start = y_start; _result.vi_x_add = x_add; _result.vi_y_add = y_add; + _result.vi_minhpass = h_start_clamped ? 0 : 8; + _result.vi_maxhpass = hres_clamped ? 0 : 7; _result.vi_width = _SHIFTR(*REG.VI_WIDTH, 0, 12); _result.vi_lowerfield = lowerfield; _result.vi_origin = _SHIFTR(*REG.VI_ORIGIN, 0, 24); @@ -1311,7 +1334,7 @@ void FrameBufferList::renderBuffer() } RdpUpdateResult rdpRes; - if (!rdp_update(rdpRes)) { + if (!m_rdpUpdate.update(rdpRes)) { gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer); gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f); dwnd().swapBuffers(); @@ -1332,7 +1355,6 @@ void FrameBufferList::renderBuffer() s32 dstX0, dstX1, dstY0, dstY1; s32 srcWidth, srcHeight; s32 XoffsetLeft = 0, XoffsetRight = 0; - s32 Xdivot = 0; s32 srcPartHeight = 0; s32 dstPartHeight = 0; @@ -1355,7 +1377,7 @@ void FrameBufferList::renderBuffer() XoffsetLeft = addrOffset % rdpRes.vi_width; } - if (rdpRes.vi_lowerfield && rdpRes.vi_width > 320) { + if (!rdpRes.vi_lowerfield && rdpRes.vi_width > 320) { if (srcY0 > 0) --srcY0; if (dstY0 > 0) @@ -1389,25 +1411,24 @@ void FrameBufferList::renderBuffer() for (const auto & f : postProcessor.getPostprocessingList()) pFilteredBuffer = f(postProcessor, pFilteredBuffer); - if (rdpRes.vi_fsaa && rdpRes.vi_divot) - Xdivot = 1; - const f32 viScaleX = _FIXED2FLOAT(_SHIFTR(*REG.VI_X_SCALE, 0, 12), 10); const f32 srcScaleX = pFilteredBuffer->m_scale; - const f32 dstScaleX = m_overscan.getScaleX(); - const s32 hx0 = rdpRes.vi_h_start; + const f32 dstScaleX = dwnd().getWidth() / (640 * viScaleX); + const s32 hx0 = rdpRes.vi_h_start + rdpRes.vi_minhpass; const s32 h0 = (rdpRes.vi_ispal ? 128 : 108); const s32 hEnd = _SHIFTR(*REG.VI_H_START, 0, 10); - const s32 hx1 = max(0, h0 + 640 - hEnd); + const s32 hx1 = max(0, h0 + 640 - hEnd + (s32)rdpRes.vi_maxhpass); //const s32 hx1 = hx0 + rdpRes.vi_hres; - dstX0 = (s32)((hx0 * viScaleX + XoffsetRight) * dstScaleX); - dstX1 = m_overscan.getDrawingWidth() - (s32)((hx1 * viScaleX + Xdivot) * dstScaleX); + dstX0 = (s32)((hx0 * viScaleX + f32(XoffsetRight)) * dstScaleX); + dstX1 = m_overscan.getDrawingWidth() - (s32)(hx1 * viScaleX * dstScaleX); const f32 srcScaleY = pFilteredBuffer->m_scale; CachedTexture * pBufferTexture = pFilteredBuffer->m_pTexture; - s32 srcCoord[4] = { (s32)(XoffsetLeft * srcScaleX), + const s32 cutleft = static_cast(rdpRes.vi_minhpass * viScaleX); + const s32 cutright = static_cast(rdpRes.vi_maxhpass * viScaleX); + s32 srcCoord[4] = { (s32)((XoffsetLeft + cutleft) * srcScaleX), (s32)(srcY0*srcScaleY), - (s32)((srcWidth + XoffsetLeft - XoffsetRight - Xdivot) * srcScaleX), + (s32)((srcWidth + XoffsetLeft - XoffsetRight - cutright) * srcScaleX), min((s32)(srcY1*srcScaleY), (s32)pBufferTexture->realHeight) }; if (srcCoord[2] > pBufferTexture->realWidth || srcCoord[3] > pBufferTexture->realHeight) { removeBuffer(pBuffer->m_startAddress); @@ -1479,9 +1500,7 @@ void FrameBufferList::renderBuffer() blitParams.srcY1 = min((s32)(srcY1*srcScaleY), (s32)pFilteredBuffer->m_pTexture->realHeight); blitParams.srcWidth = pBufferTexture->realWidth; blitParams.srcHeight = pBufferTexture->realHeight; - blitParams.dstX0 = hOffset; blitParams.dstY0 = vOffset + (s32)(dstY0*dstScaleY); - blitParams.dstX1 = hOffset + dstX1; blitParams.dstY1 = vOffset + (s32)(dstY1*dstScaleY); blitParams.dstWidth = m_overscan.getBufferWidth(); blitParams.dstHeight = m_overscan.getBufferHeight(); diff --git a/src/FrameBuffer.h b/src/FrameBuffer.h index 0255399b..18b49636 100644 --- a/src/FrameBuffer.h +++ b/src/FrameBuffer.h @@ -170,6 +170,40 @@ private: FrameBuffer * m_pCopy; u32 m_prevColorImageHeight; OverscanBuffer m_overscan; + + struct RdpUpdateResult { + u32 vi_vres; + u32 vi_hres; + u32 vi_v_start; + u32 vi_h_start; + u32 vi_x_start; + u32 vi_y_start; + u32 vi_x_add; + u32 vi_y_add; + u32 vi_width; + u32 vi_origin; + u32 vi_minhpass; + u32 vi_maxhpass; + bool vi_lowerfield; + bool vi_fsaa; + bool vi_divot; + bool vi_ispal; + }; + + class RdpUpdate + { + public: + void init(); + bool update(RdpUpdateResult & _result); + + private: + u32 oldvstart = 0U; + u32 prevvicurrent = 0U; + bool prevwasblank = false; + bool prevserrate = false; + bool oldlowerfield = false; + s32 emucontrolsvicurrent = -1; + } m_rdpUpdate; }; inline