1
0
mirror of https://github.com/blawar/GLideN64.git synced 2024-07-07 03:13:49 +00:00

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)"
This commit is contained in:
Sergey Lipskiy 2019-01-09 21:54:31 +07:00
parent 0f1b45cfa6
commit 1e84099521
2 changed files with 102 additions and 49 deletions

View File

@ -551,6 +551,7 @@ void FrameBufferList::init()
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer); gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
m_prevColorImageHeight = 0; m_prevColorImageHeight = 0;
m_overscan.init(); m_overscan.init();
m_rdpUpdate.init();
} }
void FrameBufferList::destroy() { void FrameBufferList::destroy() {
@ -1028,31 +1029,21 @@ void FrameBufferList::_renderScreenSizeBuffer()
gDP.changed |= CHANGED_SCISSOR; gDP.changed |= CHANGED_SCISSOR;
} }
struct RdpUpdateResult { void FrameBufferList::RdpUpdate::init()
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)
{ {
static const u32 PRESCALE_WIDTH = 640; oldvstart = 0U;
static const u32 PRESCALE_HEIGHT = 625; prevvicurrent = 0U;
static u32 oldvstart = 0; prevwasblank = false;
static bool 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 x_add = _SHIFTR(*REG.VI_X_SCALE, 0, 12);
const u32 y_add = _SHIFTR(*REG.VI_Y_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 delta_y = y2 - y1;
const u32 vitype = _SHIFTR( *REG.VI_STATUS, 0, 2 ); const u32 vitype = _SHIFTR( *REG.VI_STATUS, 0, 2 );
const bool interlaced = (*REG.VI_STATUS & 0x40) != 0; const bool serration_pulses = (*REG.VI_STATUS & 0x40) != 0;
const bool lowerfield = interlaced ? y1 > oldvstart : false; 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; oldvstart = y1;
} else
prevserrate = false;
u32 hres = delta_x; u32 hres = delta_x;
u32 vres = delta_y; u32 vres = delta_y;
s32 h_start = x1 - (ispal ? 128 : 108); 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 x_start = _SHIFTR(*REG.VI_X_SCALE, 16, 12);
u32 y_start = _SHIFTR(*REG.VI_Y_SCALE, 16, 12); u32 y_start = _SHIFTR(*REG.VI_Y_SCALE, 16, 12);
bool h_start_clamped = h_start < 0;
if (h_start < 0) { if (h_start < 0) {
x_start -= x_add * h_start; x_start -= x_add * h_start;
hres += h_start;
h_start = 0; 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; 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; hres = PRESCALE_WIDTH - h_start;
if (vres > PRESCALE_HEIGHT - v_start) if (vres + v_start > PRESCALE_HEIGHT)
vres = PRESCALE_HEIGHT - v_start; vres = PRESCALE_HEIGHT - v_start;
s32 vactivelines = v_sync - (ispal ? 47 : 37); s32 vactivelines = v_sync - (ispal ? 44 : 34);
if (vactivelines > PRESCALE_HEIGHT) { if (vactivelines > PRESCALE_HEIGHT) {
LOG(LOG_MINIMAL, "VI_V_SYNC_REG too big\n"); LOG(LOG_MINIMAL, "VI_V_SYNC_REG too big\n");
return false; return false;
@ -1105,7 +1126,7 @@ bool rdp_update(RdpUpdateResult & _result)
if (hres <= 0 || vres <= 0 || ((vitype & 2) == 0 && prevwasblank)) /* early return. */ if (hres <= 0 || vres <= 0 || ((vitype & 2) == 0 && prevwasblank)) /* early return. */
return false; return false;
if (vitype >> 1 == 0) { if ((vitype & 2) == 0) {
prevwasblank = true; prevwasblank = true;
return false; return false;
} }
@ -1121,6 +1142,8 @@ bool rdp_update(RdpUpdateResult & _result)
_result.vi_y_start = y_start; _result.vi_y_start = y_start;
_result.vi_x_add = x_add; _result.vi_x_add = x_add;
_result.vi_y_add = y_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_width = _SHIFTR(*REG.VI_WIDTH, 0, 12);
_result.vi_lowerfield = lowerfield; _result.vi_lowerfield = lowerfield;
_result.vi_origin = _SHIFTR(*REG.VI_ORIGIN, 0, 24); _result.vi_origin = _SHIFTR(*REG.VI_ORIGIN, 0, 24);
@ -1311,7 +1334,7 @@ void FrameBufferList::renderBuffer()
} }
RdpUpdateResult rdpRes; RdpUpdateResult rdpRes;
if (!rdp_update(rdpRes)) { if (!m_rdpUpdate.update(rdpRes)) {
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer); gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f); gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f);
dwnd().swapBuffers(); dwnd().swapBuffers();
@ -1332,7 +1355,6 @@ void FrameBufferList::renderBuffer()
s32 dstX0, dstX1, dstY0, dstY1; s32 dstX0, dstX1, dstY0, dstY1;
s32 srcWidth, srcHeight; s32 srcWidth, srcHeight;
s32 XoffsetLeft = 0, XoffsetRight = 0; s32 XoffsetLeft = 0, XoffsetRight = 0;
s32 Xdivot = 0;
s32 srcPartHeight = 0; s32 srcPartHeight = 0;
s32 dstPartHeight = 0; s32 dstPartHeight = 0;
@ -1355,7 +1377,7 @@ void FrameBufferList::renderBuffer()
XoffsetLeft = addrOffset % rdpRes.vi_width; XoffsetLeft = addrOffset % rdpRes.vi_width;
} }
if (rdpRes.vi_lowerfield && rdpRes.vi_width > 320) { if (!rdpRes.vi_lowerfield && rdpRes.vi_width > 320) {
if (srcY0 > 0) if (srcY0 > 0)
--srcY0; --srcY0;
if (dstY0 > 0) if (dstY0 > 0)
@ -1389,25 +1411,24 @@ void FrameBufferList::renderBuffer()
for (const auto & f : postProcessor.getPostprocessingList()) for (const auto & f : postProcessor.getPostprocessingList())
pFilteredBuffer = f(postProcessor, pFilteredBuffer); 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 viScaleX = _FIXED2FLOAT(_SHIFTR(*REG.VI_X_SCALE, 0, 12), 10);
const f32 srcScaleX = pFilteredBuffer->m_scale; const f32 srcScaleX = pFilteredBuffer->m_scale;
const f32 dstScaleX = m_overscan.getScaleX(); const f32 dstScaleX = dwnd().getWidth() / (640 * viScaleX);
const s32 hx0 = rdpRes.vi_h_start; const s32 hx0 = rdpRes.vi_h_start + rdpRes.vi_minhpass;
const s32 h0 = (rdpRes.vi_ispal ? 128 : 108); const s32 h0 = (rdpRes.vi_ispal ? 128 : 108);
const s32 hEnd = _SHIFTR(*REG.VI_H_START, 0, 10); 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; //const s32 hx1 = hx0 + rdpRes.vi_hres;
dstX0 = (s32)((hx0 * viScaleX + XoffsetRight) * dstScaleX); dstX0 = (s32)((hx0 * viScaleX + f32(XoffsetRight)) * dstScaleX);
dstX1 = m_overscan.getDrawingWidth() - (s32)((hx1 * viScaleX + Xdivot) * dstScaleX); dstX1 = m_overscan.getDrawingWidth() - (s32)(hx1 * viScaleX * dstScaleX);
const f32 srcScaleY = pFilteredBuffer->m_scale; const f32 srcScaleY = pFilteredBuffer->m_scale;
CachedTexture * pBufferTexture = pFilteredBuffer->m_pTexture; CachedTexture * pBufferTexture = pFilteredBuffer->m_pTexture;
s32 srcCoord[4] = { (s32)(XoffsetLeft * srcScaleX), const s32 cutleft = static_cast<s32>(rdpRes.vi_minhpass * viScaleX);
const s32 cutright = static_cast<s32>(rdpRes.vi_maxhpass * viScaleX);
s32 srcCoord[4] = { (s32)((XoffsetLeft + cutleft) * srcScaleX),
(s32)(srcY0*srcScaleY), (s32)(srcY0*srcScaleY),
(s32)((srcWidth + XoffsetLeft - XoffsetRight - Xdivot) * srcScaleX), (s32)((srcWidth + XoffsetLeft - XoffsetRight - cutright) * srcScaleX),
min((s32)(srcY1*srcScaleY), (s32)pBufferTexture->realHeight) }; min((s32)(srcY1*srcScaleY), (s32)pBufferTexture->realHeight) };
if (srcCoord[2] > pBufferTexture->realWidth || srcCoord[3] > pBufferTexture->realHeight) { if (srcCoord[2] > pBufferTexture->realWidth || srcCoord[3] > pBufferTexture->realHeight) {
removeBuffer(pBuffer->m_startAddress); removeBuffer(pBuffer->m_startAddress);
@ -1479,9 +1500,7 @@ void FrameBufferList::renderBuffer()
blitParams.srcY1 = min((s32)(srcY1*srcScaleY), (s32)pFilteredBuffer->m_pTexture->realHeight); blitParams.srcY1 = min((s32)(srcY1*srcScaleY), (s32)pFilteredBuffer->m_pTexture->realHeight);
blitParams.srcWidth = pBufferTexture->realWidth; blitParams.srcWidth = pBufferTexture->realWidth;
blitParams.srcHeight = pBufferTexture->realHeight; blitParams.srcHeight = pBufferTexture->realHeight;
blitParams.dstX0 = hOffset;
blitParams.dstY0 = vOffset + (s32)(dstY0*dstScaleY); blitParams.dstY0 = vOffset + (s32)(dstY0*dstScaleY);
blitParams.dstX1 = hOffset + dstX1;
blitParams.dstY1 = vOffset + (s32)(dstY1*dstScaleY); blitParams.dstY1 = vOffset + (s32)(dstY1*dstScaleY);
blitParams.dstWidth = m_overscan.getBufferWidth(); blitParams.dstWidth = m_overscan.getBufferWidth();
blitParams.dstHeight = m_overscan.getBufferHeight(); blitParams.dstHeight = m_overscan.getBufferHeight();

View File

@ -170,6 +170,40 @@ private:
FrameBuffer * m_pCopy; FrameBuffer * m_pCopy;
u32 m_prevColorImageHeight; u32 m_prevColorImageHeight;
OverscanBuffer m_overscan; 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 inline