summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/Src/VertexShaderManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoCommon/Src/VertexShaderManager.cpp')
-rw-r--r--Source/Core/VideoCommon/Src/VertexShaderManager.cpp85
1 files changed, 67 insertions, 18 deletions
diff --git a/Source/Core/VideoCommon/Src/VertexShaderManager.cpp b/Source/Core/VideoCommon/Src/VertexShaderManager.cpp
index e603e6e6c4..5ec371a0ba 100644
--- a/Source/Core/VideoCommon/Src/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/Src/VertexShaderManager.cpp
@@ -39,13 +39,6 @@ static float s_fViewRotation[2];
VertexShaderConstants VertexShaderManager::constants;
bool VertexShaderManager::dirty;
-void UpdateViewport(Matrix44& vpCorrection);
-
-void UpdateViewportWithCorrection()
-{
- UpdateViewport(s_viewportCorrection);
-}
-
struct ProjectionHack
{
float sign;
@@ -130,6 +123,58 @@ void UpdateProjectionHack(int iPhackvalue[], std::string sPhackvalue[])
g_ProjHack3 = bProjHack3;
}
+
+// Viewport correction:
+// In D3D, the viewport rectangle must fit within the render target.
+// Say you want a viewport at (ix, iy) with size (iw, ih),
+// but your viewport must be clamped at (ax, ay) with size (aw, ah).
+// Just multiply the projection matrix with the following to get the same
+// effect:
+// [ (iw/aw) 0 0 ((iw - 2*(ax-ix)) / aw - 1) ]
+// [ 0 (ih/ah) 0 ((-ih + 2*(ay-iy)) / ah + 1) ]
+// [ 0 0 1 0 ]
+// [ 0 0 0 1 ]
+static void ViewportCorrectionMatrix(Matrix44& result)
+{
+ int scissorXOff = bpmem.scissorOffset.x * 2;
+ int scissorYOff = bpmem.scissorOffset.y * 2;
+
+ // TODO: ceil, floor or just cast to int?
+ // TODO: Directly use the floats instead of rounding them?
+ float intendedX = xfregs.viewport.xOrig - xfregs.viewport.wd - scissorXOff;
+ float intendedY = xfregs.viewport.yOrig + xfregs.viewport.ht - scissorYOff;
+ float intendedWd = 2.0f * xfregs.viewport.wd;
+ float intendedHt = -2.0f * xfregs.viewport.ht;
+
+ if (intendedWd < 0.f)
+ {
+ intendedX += intendedWd;
+ intendedWd = -intendedWd;
+ }
+ if (intendedHt < 0.f)
+ {
+ intendedY += intendedHt;
+ intendedHt = -intendedHt;
+ }
+
+ // fit to EFB size
+ float X = (intendedX >= 0.f) ? intendedX : 0.f;
+ float Y = (intendedY >= 0.f) ? intendedY : 0.f;
+ float Wd = (X + intendedWd <= EFB_WIDTH) ? intendedWd : (EFB_WIDTH - X);
+ float Ht = (Y + intendedHt <= EFB_HEIGHT) ? intendedHt : (EFB_HEIGHT - Y);
+
+ Matrix44::LoadIdentity(result);
+ if (Wd == 0 || Ht == 0)
+ return;
+
+ result.data[4*0+0] = intendedWd / Wd;
+ result.data[4*0+3] = (intendedWd - 2.f * (X - intendedX)) / Wd - 1.f;
+ result.data[4*1+1] = intendedHt / Ht;
+ result.data[4*1+3] = (-intendedHt + 2.f * (Y - intendedY)) / Ht + 1.f;
+}
+
+void UpdateViewport();
+
void VertexShaderManager::Init()
{
Dirty();
@@ -152,16 +197,16 @@ void VertexShaderManager::Shutdown()
void VertexShaderManager::Dirty()
{
- nTransformMatricesChanged[0] = 0;
+ nTransformMatricesChanged[0] = 0;
nTransformMatricesChanged[1] = 256;
nNormalMatricesChanged[0] = 0;
nNormalMatricesChanged[1] = 96;
- nPostTransformMatricesChanged[0] = 0;
+ nPostTransformMatricesChanged[0] = 0;
nPostTransformMatricesChanged[1] = 256;
- nLightsChanged[0] = 0;
+ nLightsChanged[0] = 0;
nLightsChanged[1] = 0x80;
bPosNormalMatrixChanged = true;
@@ -171,7 +216,7 @@ void VertexShaderManager::Dirty()
bProjectionChanged = true;
nMaterialsChanged = 15;
-
+
dirty = true;
}
@@ -259,7 +304,7 @@ void VertexShaderManager::SetConstants()
constants.materials[i][3] = ( data & 0xFF) / 255.0f;
}
}
-
+
for (int i = 0; i < 2; ++i)
{
if (nMaterialsChanged & (1 << (i + 2)))
@@ -293,7 +338,7 @@ void VertexShaderManager::SetConstants()
if (bTexMatricesChanged[0])
{
bTexMatricesChanged[0] = false;
- const float *fptrs[] =
+ const float *fptrs[] =
{
(const float *)xfmem + MatrixIndexA.Tex0MtxIdx * 4, (const float *)xfmem + MatrixIndexA.Tex1MtxIdx * 4,
(const float *)xfmem + MatrixIndexA.Tex2MtxIdx * 4, (const float *)xfmem + MatrixIndexA.Tex3MtxIdx * 4
@@ -326,18 +371,22 @@ void VertexShaderManager::SetConstants()
bViewportChanged = false;
constants.depthparams[0] = xfregs.viewport.farZ / 16777216.0f;
constants.depthparams[1] = xfregs.viewport.zRange / 16777216.0f;
- constants.depthparams[2] = -1.f / g_renderer->EFBToScaledX(ceilf(2.0f * xfregs.viewport.wd));
- constants.depthparams[3] = 1.f / g_renderer->EFBToScaledY(ceilf(-2.0f * xfregs.viewport.ht));
dirty = true;
// This is so implementation-dependent that we can't have it here.
- UpdateViewport(s_viewportCorrection);
- bProjectionChanged = true;
+ UpdateViewport();
+
+ // Update projection if the viewport isn't 1:1 useable
+ if(!g_ActiveConfig.backend_info.bSupportsOversizedViewports)
+ {
+ ViewportCorrectionMatrix(s_viewportCorrection);
+ bProjectionChanged = true;
+ }
}
if (bProjectionChanged)
{
bProjectionChanged = false;
-
+
float *rawProjection = xfregs.projection.rawProjection;
switch(xfregs.projection.type)