Merge pull request #21917 from hrydgard/framebuffer-offset-work

Fix some issues with framebuffer offset, after the transform pipeline refactor
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-07-13 12:37:56 +02:00
commit 7cf5468221
4 files changed
+54 -43

No files matched your search

+48 -37
View File
@@ -517,54 +517,65 @@ ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat) {
return REPLACE_BLEND_STANDARD;
}
// Viewport and scissor really could be treated entirely separately, but the non-buffered case is nicer by doing them "together".
void ConvertViewportAndScissor(const DisplayLayoutConfig &config, bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out) {
float renderWidthFactor, renderHeightFactor;
float renderX = 0.0f, renderY = 0.0f;
float displayOffsetX, displayOffsetY;
// Scissor. The scissor needs to be offset by the framebuffer offset.
const int scissorX1 = gstate.getScissorX1();
const int scissorY1 = gstate.getScissorY1();
const int scissorW = gstate.getScissorX2() + 1 - scissorX1;
const int scissorH = gstate.getScissorY2() + 1 - scissorY1;
if (useBufferedRendering) {
displayOffsetX = 0.0f;
displayOffsetY = 0.0f;
renderWidthFactor = (float)renderWidth / (float)bufferWidth;
renderHeightFactor = (float)renderHeight / (float)bufferHeight;
const float renderWidthFactor = (float)renderWidth / (float)bufferWidth;
const float renderHeightFactor = (float)renderHeight / (float)bufferHeight;
if (scissorW < 0 || scissorH < 0) {
// Bad scissor, kill all drawing with a valid scissor setup.
out.scissorX = 0;
out.scissorY = 0;
out.scissorW = 0;
out.scissorH = 0;
} else {
out.scissorX = (scissorX1 + gstate_c.curRTOffsetX) * renderWidthFactor;
out.scissorY = (scissorY1 + gstate_c.curRTOffsetY) * renderHeightFactor;
out.scissorW = scissorW * renderWidthFactor;
out.scissorH = scissorH * renderHeightFactor;
}
out.viewportX = 0.0f;
out.viewportY = 0.0f;
out.viewportW = gstate_c.curRTWidth * renderWidthFactor;
out.viewportH = gstate_c.curRTHeight * renderHeightFactor;
} else {
// Hacky path for non-buffered rendering.
float pixelW = PSP_CoreParameter().pixelWidth;
float pixelH = PSP_CoreParameter().pixelHeight;
FRect frame = GetScreenFrame(config.bIgnoreScreenInsets, pixelW, pixelH);
FRect rc;
CalculateDisplayOutputRect(config, &rc, 480, 272, frame, ROTATION_LOCKED_HORIZONTAL);
displayOffsetX = rc.x;
displayOffsetY = rc.y;
const float displayOffsetX = rc.x;
const float displayOffsetY = rc.y;
renderWidth = rc.w;
renderHeight = rc.h;
renderWidthFactor = renderWidth / 480.0f;
renderHeightFactor = renderHeight / 272.0f;
const float renderWidthFactor = renderWidth / 480.0f;
const float renderHeightFactor = renderHeight / 272.0f;
if (scissorW < 0 || scissorH < 0) {
// Bad scissor, kill all drawing with a valid scissor setup.
out.scissorX = 0;
out.scissorY = 0;
out.scissorW = 0;
out.scissorH = 0;
} else {
out.scissorX = displayOffsetX + (scissorX1 + gstate_c.curRTOffsetX) * renderWidthFactor;
out.scissorY = displayOffsetY + (scissorY1 + gstate_c.curRTOffsetY) * renderHeightFactor;
out.scissorW = scissorW * renderWidthFactor;
out.scissorH = scissorH * renderHeightFactor;
}
out.viewportX = displayOffsetX;
out.viewportY = displayOffsetY;
out.viewportW = gstate_c.curRTWidth * renderWidthFactor;
out.viewportH = gstate_c.curRTHeight * renderHeightFactor;
}
// Scissor
int scissorX1 = gstate.getScissorX1();
int scissorY1 = gstate.getScissorY1();
int scissorX2 = gstate.getScissorX2() + 1;
int scissorY2 = gstate.getScissorY2() + 1;
if (scissorX2 < scissorX1 || scissorY2 < scissorY1) {
out.scissorX = 0;
out.scissorY = 0;
out.scissorW = 0;
out.scissorH = 0;
} else {
out.scissorX = displayOffsetX + scissorX1 * renderWidthFactor;
out.scissorY = displayOffsetY + scissorY1 * renderHeightFactor;
out.scissorW = (scissorX2 - scissorX1) * renderWidthFactor;
out.scissorH = (scissorY2 - scissorY1) * renderHeightFactor;
}
int curRTWidth = gstate_c.curRTWidth;
int curRTHeight = gstate_c.curRTHeight;
out.viewportX = displayOffsetX;
out.viewportY = displayOffsetY;
out.viewportW = curRTWidth * renderWidthFactor;
out.viewportH = curRTHeight * renderHeightFactor;
}
static const BlendFactor genericALookup[11] = {
+2 -2
View File
@@ -79,8 +79,8 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool useBuffe
if (dirtyUniforms & DIRTY_FRAMEBUFFER_DIM) {
ub->xywh[0] = (float)gstate_c.curRTOffsetX;
ub->xywh[1] = (float)gstate_c.curRTOffsetY;
ub->xywh[2] = (float)gstate_c.curRTWidth;
ub->xywh[3] = (float)gstate_c.curRTHeight;
ub->xywh[2] = (float)(2.0 / gstate_c.curRTWidth); // intentionally do double precision here.
ub->xywh[3] = (float)(2.0 / gstate_c.curRTHeight);
ub->rotation = useBufferedRendering ? 0 : (float)g_display.rotation;
}
+2 -2
View File
@@ -1041,8 +1041,8 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
WRITE(p, " %sgl_ClipDistance%s = u_minZmaxZ.y < 65535.0 ? (u_minZmaxZ.y - clipZFar) * outPos.w : 1.0;\n", compat.vsOutPrefix, maxZClipPlaneSuffix);
}
// Convert to NDC space, using the framebuffer offset and size stored in u_xywh.
WRITE(p, " outPos.xy = ((outPos.xy - u_xywh.xy) / u_xywh.zw) * 2.0 - 1.0;\n");
// Convert to NDC space, using the framebuffer offset and (inverse size * 2) stored in u_xywh.
WRITE(p, " outPos.xy = ((outPos.xy + u_xywh.xy) * u_xywh.zw) - vec2(1.0, 1.0);\n");
if (gstate_c.Use(GPU_ROUND_DEPTH_TO_16BIT)) {
// Actually 15-bit. Truncate here fixes Afterburner (similarly to the min/max clipping above).
+2 -2
View File
@@ -410,8 +410,8 @@ void LinkedShader::UpdateUniforms(const ShaderID &vsid, const ShaderLanguageDesc
float xywh[4];
xywh[0] = (float)gstate_c.curRTOffsetX;
xywh[1] = (float)gstate_c.curRTOffsetY;
xywh[2] = (float)gstate_c.curRTWidth;
xywh[3] = (float)gstate_c.curRTHeight;
xywh[2] = (float)(2.0 / gstate_c.curRTWidth); // intentionally double precision here
xywh[3] = (float)(2.0 / gstate_c.curRTHeight);
SetFloatUniform4(render_, &u_xywh, xywh);
float nan = std::numeric_limits<float>::quiet_NaN();
render_->SetUniformF1(&u_NaN, nan);