diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index d1cf55b982..a6301e53a2 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -578,7 +578,10 @@ void Renderer::SetViewport() Wd = (X + Wd <= GetTargetWidth()) ? Wd : (GetTargetWidth() - X); Ht = (Y + Ht <= GetTargetHeight()) ? Ht : (GetTargetHeight() - Y); - D3D11_VIEWPORT vp = CD3D11_VIEWPORT(X, Y, Wd, Ht, D3D11_MIN_DEPTH, 16777215.0f / 16777216.0f); + // We do depth clipping and depth range in the vertex shader instead of relying + // on the graphics API. However we still need to ensure depth values don't exceed + // the maximum value supported by the console GPU. + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(X, Y, Wd, Ht, D3D11_MIN_DEPTH, GX_MAX_DEPTH); D3D::context->RSSetViewports(1, &vp); } diff --git a/Source/Core/VideoBackends/D3D12/Render.cpp b/Source/Core/VideoBackends/D3D12/Render.cpp index 09e7356259..912cd4908d 100644 --- a/Source/Core/VideoBackends/D3D12/Render.cpp +++ b/Source/Core/VideoBackends/D3D12/Render.cpp @@ -482,8 +482,10 @@ void Renderer::SetViewport() width = (x + width <= GetTargetWidth()) ? width : (GetTargetWidth() - x); height = (y + height <= GetTargetHeight()) ? height : (GetTargetHeight() - y); - D3D12_VIEWPORT vp = {x, y, width, height, D3D12_MIN_DEPTH, 16777215.0f / 16777216.0f}; - + // We do depth clipping and depth range in the vertex shader instead of relying + // on the graphics API. However we still need to ensure depth values don't exceed + // the maximum value supported by the console GPU. + D3D12_VIEWPORT vp = {x, y, width, height, D3D12_MIN_DEPTH, GX_MAX_DEPTH}; D3D::current_command_list->RSSetViewports(1, &vp); } diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index a47f0c1e40..78d5d106b7 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1159,8 +1159,12 @@ void Renderer::SetViewport() glViewport(iceilf(X), iceilf(Y), iceilf(Width), iceilf(Height)); } + // Set the reversed depth range. If we do depth clipping and depth range in the + // vertex shader we only need to ensure depth values don't exceed the maximum + // value supported by the console GPU. If not, we simply clamp the near/far values + // themselves to the maximum value as done above. if (g_ActiveConfig.backend_info.bSupportsDepthClamp) - glDepthRangef(16777215.0f / 16777216.0f, 0.0f); + glDepthRangef(GX_MAX_DEPTH, 0.0f); else glDepthRangef(GLFar, GLNear); } diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index fade0d00b9..3b50cd2313 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -86,6 +86,12 @@ unsigned int Renderer::efb_scale_numeratorY = 1; unsigned int Renderer::efb_scale_denominatorX = 1; unsigned int Renderer::efb_scale_denominatorY = 1; +// The maximum depth that is written to the depth buffer should never exceed this value. +// This is necessary because we use a 2^24 divisor for all our depth values to prevent +// floating-point round-trip errors. However the console GPU doesn't ever write a value +// to the depth buffer that exceeds 2^24 - 1. +const float Renderer::GX_MAX_DEPTH = 16777215.0f / 16777216.0f; + static float AspectToWidescreen(float aspect) { return aspect * ((16.0f / 9.0f) / (4.0f / 3.0f)); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 81554f5e78..1ae3121637 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -176,6 +176,8 @@ protected: static std::unique_ptr m_post_processor; + static const float GX_MAX_DEPTH; + private: static PEControl::PixelFormat prev_efb_format; static unsigned int efb_scale_numeratorX; diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp index 4603e22d7e..a42f090716 100644 --- a/Source/Core/VideoCommon/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/VertexShaderGen.cpp @@ -399,43 +399,43 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const vertex_shader_uid_da out.Write("o.colors_1 = color1;\n"); } + // Write the true depth value. If the game uses depth textures, then the pixel shader will + // override it with the correct values if not then early z culling will improve speed. if (g_ActiveConfig.backend_info.bSupportsDepthClamp) { + // If we can disable the incorrect depth clipping planes using depth clamping, then we can do + // our own depth clipping and calculate the depth range before the perspective divide. + // Since we're adjusting z for the depth range before the perspective divide, we have to do our - // own clipping. We want to clip so that -w <= z <= 0. + // own clipping. We want to clip so that -w <= z <= 0, which matches the console -1..0 range. out.Write("o.clipDist0 = o.pos.z + o.pos.w;\n"); // Near: z < -w out.Write("o.clipDist1 = -o.pos.z;\n"); // Far: z > 0 - // We have to handle the depth range in the vertex shader, because some games will use a depth - // range beyond the normal depth range of 0..1. + // Adjust z for the depth range. We're using an equation which incorperates a depth inversion, + // so we can map the console -1..0 range to the 0..1 range used in the depth buffer. + // We have to handle the depth range in the vertex shader instead of after the perspective + // divide, because some games will use a depth range larger than what is allowed by the + // graphics API. These large depth ranges will still be clipped to the 0..1 range, so these + // games effectively add a depth bias to the values written to the depth buffer. out.Write("o.pos.z = o.pos.w * " I_PIXELCENTERCORRECTION ".w - " "o.pos.z * " I_PIXELCENTERCORRECTION ".z;\n"); } else { - // User-defined clipping is not supported, thus we rely on the API to handle the depth range. - // We still need to take care of the reversed depth, so we do that here. + // If we can't disable the incorrect depth clipping planes, then we need to rely on the + // graphics API to handle the depth range after the perspective divide. This can result in + // inaccurate depth values due to the missing depth bias, but that can be least corrected by + // overriding depth values in the pixel shader. We still need to take care of the reversed depth + // though, so we do that here. out.Write("o.pos.z = -o.pos.z;\n"); } - // write the true depth value, if the game uses depth textures pixel shaders will override with - // the correct values - // if not early z culling will improve speed if (!g_ActiveConfig.backend_info.bSupportsClipControl) { - // this results in a scale from -1..0 to -1..1 after perspective - // divide + // If the graphics API doesn't support a depth range of 0..1, then we need to map z to + // the -1..1 range. Unfortunately we have to use a substraction, which is a lossy floating-point + // operation that can introduce a round-trip error. out.Write("o.pos.z = o.pos.z * 2.0 - o.pos.w;\n"); - - // the next steps of the OGL pipeline are: - // (x_c,y_c,z_c,w_c) = o.pos //switch to OGL spec terminology - // clipping to -w_c <= (x_c,y_c,z_c) <= w_c - // (x_d,y_d,z_d) = (x_c,y_c,z_c)/w_c//perspective divide - // z_w = (f-n)/2*z_d + (n+f)/2 - // z_w now contains the value to go to the 0..1 depth buffer - - // trying to get the correct semantic while not using glDepthRange - // seems to get rather complicated } // The console GPU places the pixel center at 7/12 in screen space unless diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index dabc7870fd..9b1c3437fe 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -389,8 +389,9 @@ void VertexShaderManager::SetConstants() // The depth range is handled in the vertex shader. We need to reverse // the far value to get a reversed depth range mapping. This is necessary - // because we have the most precision at the near plane, while the console - // has the most percision at the far plane. + // because the standard depth range equation pushes all depth values towards + // the back of the depth buffer where conventionally depth buffers have the + // least precision. constants.pixelcentercorrection[2] = xfmem.viewport.zRange / 16777215.0f; constants.pixelcentercorrection[3] = 1.0f - xfmem.viewport.farZ / 16777215.0f;