1
0
mirror of https://github.com/blawar/GLideN64.git synced 2024-07-02 09:03:37 +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);
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<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)((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();

View File

@ -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