diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index b600524db6..b58e9687d3 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -222,13 +222,27 @@ void ShaderManagerDX9::VSSetMatrix(int creg, const float* pMatrix) { } // Depth in ogl is between -1;1 we need between 0;1 and optionally reverse it -static void ConvertProjMatrixToD3D(Matrix4x4 & in, bool invertedX, bool invertedY, bool invertedZ) { +static void ConvertProjMatrixToD3D(Matrix4x4 &in, bool invertedX, bool invertedY, bool invertedZ) { Matrix4x4 s; Matrix4x4 t; - s.setScaling(Vec3(1, 1, invertedZ ? -0.5 : 0.5f)); + + s.setScaling(Vec3(gstate_c.vpWidthScale, gstate_c.vpHeightScale, invertedZ ? -0.5 : 0.5f)); float xoff = 0.5f / gstate_c.curRTRenderWidth; + xoff = gstate_c.vpXOffset + (invertedX ? xoff : -xoff); float yoff = 0.5f / gstate_c.curRTRenderHeight; - t.setTranslation(Vec3(invertedX ? xoff : -xoff, invertedY ? -yoff : yoff, 0.5f)); + yoff = gstate_c.vpYOffset + (invertedY ? yoff : -yoff); + t.setTranslation(Vec3(xoff, yoff, 0.5f)); + in = in * s * t; +} + +static void ConvertProjMatrixToD3DThrough(Matrix4x4 &in) { + Matrix4x4 s; + Matrix4x4 t; + + s.setScaling(Vec3(1.0f, 1.0f, 0.5f)); + float xoff = -0.5f / gstate_c.curRTRenderWidth; + float yoff = -0.5f / gstate_c.curRTRenderHeight; + t.setTranslation(Vec3(xoff, yoff, 0.5f)); in = in * s * t; } @@ -310,7 +324,7 @@ void ShaderManagerDX9::VSUpdateUniforms(int dirtyUniforms) { Matrix4x4 proj_through; proj_through.setOrtho(0.0f, gstate_c.curRTWidth, gstate_c.curRTHeight, 0, 0, 1); - ConvertProjMatrixToD3D(proj_through, false, false, false); + ConvertProjMatrixToD3DThrough(proj_through); VSSetMatrix(CONST_VS_PROJ_THROUGH, proj_through.getReadPtr()); } diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 28211c8a73..21e5a1c640 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -713,7 +713,6 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { (regionY2 - regionY1) * renderHeightFactor, 0.f, 1.f); } else { - // These we can turn into a glViewport call, offset by offsetX and offsetY. Math after. float vpXScale = getFloat24(gstate.viewportx1); float vpXCenter = getFloat24(gstate.viewportx2); float vpYScale = getFloat24(gstate.viewporty1); @@ -737,8 +736,6 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { vpY0 *= renderHeightFactor; vpWidth *= renderWidthFactor; vpHeight *= renderHeightFactor; - - // shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); float zScale = getFloat24(gstate.viewportz1) / 65535.0f; float zOff = getFloat24(gstate.viewportz2) / 65535.0f; @@ -748,15 +745,59 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { gstate_c.vpDepth = zScale * 2; - // D3D doesn't like viewports partially outside the target. Clamp the viewport for now. Should also adjust - // the projection matrix to compensate, really. - float left = std::max(0.0f, vpX0 + renderX); - float top = std::max(0.0f, vpY0 + renderY); - float right = std::min(left + vpWidth, renderWidth); - float bottom = std::min(top + vpHeight, renderHeight); + // D3D doesn't like viewports partially outside the target, so we + // apply the viewport partially in the shader. + float left = renderX + vpX0; + float top = renderY + vpY0; + float right = left + vpWidth; + float bottom = top + vpHeight; + + float wScale = 1.0f; + float xOffset = 0.0f; + float hScale = 1.0f; + float yOffset = 0.0f; + + // If we're within the bounds, we want clipping the viewport way. So leave it be. + if (left < 0.0f || right > renderWidth) { + float overageLeft = std::max(-left, 0.0f); + float overageRight = std::max(right - renderWidth, 0.0f); + // Our center drifted by the difference in overages. + float drift = overageRight - overageLeft; + + left += overageLeft; + right -= overageRight; + + wScale = vpWidth / (right - left); + xOffset = drift / (right - left); + } + + if (top < 0.0f || bottom > renderHeight) { + float overageTop = std::max(-top, 0.0f); + float overageBottom = std::max(bottom - renderHeight, 0.0f); + // Our center drifted by the difference in overages. + float drift = overageBottom - overageTop; + + top += overageTop; + bottom -= overageBottom; + + hScale = vpHeight / (bottom - top); + yOffset = -drift / (bottom - top); + } + depthRangeMin = std::max(0.0f, depthRangeMin); depthRangeMax = std::min(1.0f, depthRangeMax); + bool scaleChanged = gstate_c.vpWidthScale != wScale || gstate_c.vpHeightScale != hScale; + bool offsetChanged = gstate_c.vpXOffset != xOffset || gstate_c.vpYOffset != yOffset; + if (scaleChanged || offsetChanged) + { + gstate_c.vpWidthScale = wScale; + gstate_c.vpHeightScale = hScale; + gstate_c.vpXOffset = xOffset; + gstate_c.vpYOffset = yOffset; + shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); + } + dxstate.viewport.set(left, top, right - left, bottom - top, depthRangeMin, depthRangeMax); } } diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 55bb87bb57..b14a94f9b0 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -476,6 +476,11 @@ struct GPUStateCache float vpWidth; float vpHeight; float vpDepth; + // Only used by Direct3D, not saved. + float vpXOffset; + float vpYOffset; + float vpWidthScale; + float vpHeightScale; u32 curRTWidth; u32 curRTHeight;