diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index ba91c0551e..725b39911b 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -230,18 +230,24 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange) depth = 1.0f - depth; - u32 ret = 0; + // Convert to 24bit depth + u32 z24depth = std::clamp(static_cast(depth * 16777216.0f), 0, 0xFFFFFF); + if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16) { - // if Z is in 16 bit format you must return a 16 bit integer - ret = std::clamp(static_cast(depth * 65536.0f), 0, 0xFFFF); - } - else - { - ret = std::clamp(static_cast(depth * 16777216.0f), 0, 0xFFFFFF); + // When in RGB565_Z16 mode, EFB Z peeks return a 16bit value, which is presumably a + // resolved sample from the MSAA buffer. + // Dolphin doesn't currently emulate the 3 sample MSAA mode (and potentially never will) + // it just transparently upgrades the framebuffer to 24bit depth and color and whatever + // level of MSAA and higher Internal Resolution the user has configured. + + // This is mostly transparent, unless the game does an EFB read. + // But we can simply convert the 24bit depth on the fly to the 16bit depth the game expects. + + return CompressZ16(z24depth, bpmem.zcontrol.zformat); } - return ret; + return z24depth; } } diff --git a/Source/Core/VideoCommon/VideoCommon.h b/Source/Core/VideoCommon/VideoCommon.h index ff574727f5..50d57267b2 100644 --- a/Source/Core/VideoCommon/VideoCommon.h +++ b/Source/Core/VideoCommon/VideoCommon.h @@ -4,8 +4,13 @@ #pragma once +#include + +#include "Common/BitUtils.h" #include "Common/CommonTypes.h" +#include "VideoCommon/BPMemory.h" + // These are accurate (disregarding AA modes). constexpr u32 EFB_WIDTH = 640; constexpr u32 EFB_HEIGHT = 528; @@ -59,3 +64,71 @@ inline u32 Z24ToZ16ToZ24(u32 src) { return (src & 0xFFFF00) | (src >> 16); } + +inline u32 CompressZ16(u32 z24depth, DepthFormat format) +{ + // Flipper offers a number of choices for 16bit Z formats that adjust + // where the bulk of the precision lies. + + if (format == DepthFormat::ZLINEAR) + { + // This is just a linear depth buffer with 16 bits of precision + return z24depth >> 8; + } + + // ZNEAR/ZMID/ZFAR are custom floating point formats with 2/3/4 bits of exponent + // The exponent is simply the number of leading ones that have been removed + // The first zero bit is skipped and not stored. The mantissa contains the next 14/13/12 bits + // If exponent is at the MAX (3, 7, or 12) then the next bit might still be a one, and can't + // be skipped, so the mantissa simply contains the next 14/13/12 bits + + u32 leading_ones = Common::CountLeadingZeros((~z24depth) << 8); + bool next_bit_is_one = false; // AKA: Did we clamp leading_ones? + u32 exp_bits; + + switch (format) + { + case DepthFormat::ZNEAR: + exp_bits = 2; + if (leading_ones >= 3u) + { + leading_ones = 3u; + next_bit_is_one = true; + } + break; + case DepthFormat::ZMID: + exp_bits = 3; + if (leading_ones >= 7u) + { + leading_ones = 7u; + next_bit_is_one = true; + } + break; + case DepthFormat::ZFAR: + exp_bits = 4; + if (leading_ones >= 12u) + { + // The hardware implementation only uses values 0 to 12 in the exponent + leading_ones = 12u; + next_bit_is_one = true; + } + break; + default: + return z24depth >> 8; + } + + u32 mantissa_bits = 16 - exp_bits; + + // Calculate which bits we need to extract from z24depth for our mantissa + u32 top = std::max(24 - leading_ones, mantissa_bits); + if (!next_bit_is_one) + { + top -= 1; // We know the next bit is zero, so we don't need to include it. + } + u32 bottom = top - mantissa_bits; + + u32 exponent = leading_ones << mantissa_bits; // Upper bits contain exponent + u32 mantissa = Common::ExtractBits(z24depth, bottom, top - 1); + + return exponent | mantissa; +}