2015-05-24 04:55:12 +00:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2021-07-05 01:22:19 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2010-11-18 02:21:26 +00:00
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
|
|
// GC graphics pipeline
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
2015-01-11 05:17:29 +00:00
|
|
|
// 3d commands are issued through the fifo. The GPU draws to the 2MB EFB.
|
2010-11-18 02:21:26 +00:00
|
|
|
// The efb can be copied back into ram in two forms: as textures or as XFB.
|
|
|
|
// The XFB is the region in RAM that the VI chip scans out to the television.
|
|
|
|
// So, after all rendering to EFB is done, the image is copied into one of two XFBs in RAM.
|
|
|
|
// Next frame, that one is scanned out and the other one gets the copy. = double buffering.
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
|
|
|
2017-02-03 17:31:20 +00:00
|
|
|
#include "VideoCommon/RenderBase.h"
|
|
|
|
|
2017-12-25 23:38:44 +00:00
|
|
|
#include <algorithm>
|
2010-11-18 02:21:26 +00:00
|
|
|
#include <cmath>
|
2015-12-21 02:49:49 +00:00
|
|
|
#include <memory>
|
2017-02-03 17:31:20 +00:00
|
|
|
#include <tuple>
|
2010-11-18 02:21:26 +00:00
|
|
|
|
2019-11-22 22:10:41 +00:00
|
|
|
#include <fmt/format.h>
|
2018-10-09 13:57:52 +00:00
|
|
|
|
2016-11-10 12:36:11 +00:00
|
|
|
#include "Common/Assert.h"
|
2019-06-29 09:27:53 +00:00
|
|
|
#include "Common/ChunkFile.h"
|
2016-01-17 21:54:31 +00:00
|
|
|
#include "Common/CommonTypes.h"
|
2017-08-01 14:37:42 +00:00
|
|
|
#include "Common/Config/Config.h"
|
2016-11-18 12:57:08 +00:00
|
|
|
#include "Common/Logging/Log.h"
|
|
|
|
#include "Common/MsgHandler.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
|
2021-10-02 00:56:46 +00:00
|
|
|
#include "Core/Config/GraphicsSettings.h"
|
2017-08-01 14:37:42 +00:00
|
|
|
#include "Core/Config/SYSCONFSettings.h"
|
2014-10-13 03:53:10 +00:00
|
|
|
#include "Core/ConfigManager.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/Core.h"
|
2020-09-15 10:13:10 +00:00
|
|
|
#include "Core/DolphinAnalytics.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/FifoPlayer/FifoRecorder.h"
|
2020-12-06 18:03:50 +00:00
|
|
|
#include "Core/FreeLookConfig.h"
|
2018-10-27 03:11:20 +00:00
|
|
|
#include "Core/HW/SystemTimers.h"
|
2022-11-27 12:50:50 +00:00
|
|
|
#include "Core/System.h"
|
2015-07-21 00:12:29 +00:00
|
|
|
|
2018-01-21 10:22:45 +00:00
|
|
|
#include "VideoCommon/AbstractFramebuffer.h"
|
2023-01-26 22:34:59 +00:00
|
|
|
#include "VideoCommon/AbstractGfx.h"
|
2017-09-03 02:30:34 +00:00
|
|
|
#include "VideoCommon/AbstractTexture.h"
|
2021-06-09 11:42:21 +00:00
|
|
|
#include "VideoCommon/BoundingBox.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/CommandProcessor.h"
|
2023-01-27 04:03:15 +00:00
|
|
|
#include "VideoCommon/FrameDumper.h"
|
2019-02-15 01:59:50 +00:00
|
|
|
#include "VideoCommon/FramebufferManager.h"
|
2023-01-29 16:01:05 +00:00
|
|
|
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
|
2016-10-15 10:36:11 +00:00
|
|
|
#include "VideoCommon/OnScreenDisplay.h"
|
2023-01-27 04:03:15 +00:00
|
|
|
#include "VideoCommon/PerformanceMetrics.h"
|
2019-02-15 01:59:50 +00:00
|
|
|
#include "VideoCommon/PixelEngine.h"
|
2017-03-04 06:40:08 +00:00
|
|
|
#include "VideoCommon/PixelShaderManager.h"
|
2023-01-27 04:03:15 +00:00
|
|
|
#include "VideoCommon/Present.h"
|
2018-02-24 15:15:35 +00:00
|
|
|
#include "VideoCommon/ShaderCache.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/Statistics.h"
|
2017-03-03 22:36:51 +00:00
|
|
|
#include "VideoCommon/VertexManagerBase.h"
|
2019-02-15 01:59:50 +00:00
|
|
|
#include "VideoCommon/VideoBackendBase.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/VideoConfig.h"
|
|
|
|
|
2015-12-21 02:49:49 +00:00
|
|
|
std::unique_ptr<Renderer> g_renderer;
|
2010-11-18 02:21:26 +00:00
|
|
|
|
2023-01-28 02:13:49 +00:00
|
|
|
Renderer::Renderer()
|
|
|
|
: m_prev_efb_format{PixelFormat::INVALID_FMT},
|
|
|
|
m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT}
|
2010-11-18 02:21:26 +00:00
|
|
|
{
|
|
|
|
UpdateActiveConfig();
|
2017-03-09 13:39:48 +00:00
|
|
|
CalculateTargetSize();
|
2023-01-30 11:46:10 +00:00
|
|
|
UpdateWidescreen();
|
2023-01-30 11:49:23 +00:00
|
|
|
|
|
|
|
m_config_changed_handle = ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "Renderer");
|
|
|
|
|
2023-01-30 11:46:10 +00:00
|
|
|
// VertexManager doesn't maintain statistics in Wii mode.
|
|
|
|
if (!SConfig::GetInstance().bWii)
|
|
|
|
m_update_widescreen_handle = AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic");
|
2010-11-18 02:21:26 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 04:46:30 +00:00
|
|
|
Renderer::~Renderer() = default;
|
|
|
|
|
2023-01-26 22:34:59 +00:00
|
|
|
void Renderer::ClearScreen(const MathUtil::Rectangle<int>& rc, bool color_enable, bool alpha_enable,
|
|
|
|
bool z_enable, u32 color, u32 z)
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
2023-01-26 22:34:59 +00:00
|
|
|
g_framebuffer_manager->FlushEFBPokes();
|
|
|
|
g_framebuffer_manager->FlagPeekCacheAsOutOfDate();
|
2019-02-15 01:59:50 +00:00
|
|
|
|
2023-01-26 22:34:59 +00:00
|
|
|
// Native -> EFB coordinates
|
|
|
|
MathUtil::Rectangle<int> target_rc = Renderer::ConvertEFBRectangle(rc);
|
|
|
|
target_rc.ClampUL(0, 0, m_target_width, m_target_height);
|
2019-02-15 01:59:50 +00:00
|
|
|
|
2023-01-26 22:34:59 +00:00
|
|
|
// Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha
|
|
|
|
// channel to 0xFF.
|
|
|
|
// On backends that don't allow masking Alpha clears, this allows us to use the fast path
|
|
|
|
// almost all the time
|
|
|
|
if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16 ||
|
|
|
|
bpmem.zcontrol.pixel_format == PixelFormat::RGB8_Z24 ||
|
|
|
|
bpmem.zcontrol.pixel_format == PixelFormat::Z24)
|
|
|
|
{
|
|
|
|
// Force alpha writes, and clear the alpha channel.
|
|
|
|
alpha_enable = true;
|
|
|
|
color &= 0x00FFFFFF;
|
|
|
|
}
|
2019-03-09 13:31:29 +00:00
|
|
|
|
2023-01-26 22:34:59 +00:00
|
|
|
g_gfx->ClearRegion(rc, target_rc, color_enable, alpha_enable, z_enable, color, z);
|
|
|
|
|
|
|
|
// Scissor rect must be restored.
|
|
|
|
BPFunctions::SetScissorAndViewport();
|
2019-02-15 01:59:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::ReinterpretPixelData(EFBReinterpretType convtype)
|
|
|
|
{
|
|
|
|
g_framebuffer_manager->ReinterpretPixelData(convtype);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
|
|
|
|
{
|
|
|
|
if (type == EFBAccessType::PeekColor)
|
|
|
|
{
|
|
|
|
u32 color = g_framebuffer_manager->PeekEFBColor(x, y);
|
|
|
|
|
|
|
|
// a little-endian value is expected to be returned
|
|
|
|
color = ((color & 0xFF00FF00) | ((color >> 16) & 0xFF) | ((color << 16) & 0xFF0000));
|
|
|
|
|
2021-02-11 02:11:31 +00:00
|
|
|
if (bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24)
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
|
|
|
color = RGBA8ToRGBA6ToRGBA8(color);
|
|
|
|
}
|
2021-02-11 02:11:31 +00:00
|
|
|
else if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16)
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
|
|
|
color = RGBA8ToRGB565ToRGBA8(color);
|
|
|
|
}
|
2021-02-11 02:11:31 +00:00
|
|
|
if (bpmem.zcontrol.pixel_format != PixelFormat::RGBA6_Z24)
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
|
|
|
color |= 0xFF000000;
|
|
|
|
}
|
|
|
|
|
2022-01-29 21:28:17 +00:00
|
|
|
// check what to do with the alpha channel (GX_PokeAlphaRead)
|
2022-12-10 15:35:07 +00:00
|
|
|
PixelEngine::AlphaReadMode alpha_read_mode =
|
|
|
|
Core::System::GetInstance().GetPixelEngine().GetAlphaReadMode();
|
2022-01-29 21:28:17 +00:00
|
|
|
|
|
|
|
if (alpha_read_mode == PixelEngine::AlphaReadMode::ReadNone)
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
2022-01-29 21:28:17 +00:00
|
|
|
return color;
|
2019-02-15 01:59:50 +00:00
|
|
|
}
|
2022-01-29 21:28:17 +00:00
|
|
|
else if (alpha_read_mode == PixelEngine::AlphaReadMode::ReadFF)
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
2022-01-29 21:28:17 +00:00
|
|
|
return color | 0xFF000000;
|
2019-02-15 01:59:50 +00:00
|
|
|
}
|
2022-01-29 21:28:17 +00:00
|
|
|
else
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
2022-01-29 21:28:17 +00:00
|
|
|
if (alpha_read_mode != PixelEngine::AlphaReadMode::Read00)
|
|
|
|
{
|
|
|
|
PanicAlertFmt("Invalid PE alpha read mode: {}", static_cast<u16>(alpha_read_mode));
|
|
|
|
}
|
|
|
|
return color & 0x00FFFFFF;
|
2019-02-15 01:59:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else // if (type == EFBAccessType::PeekZ)
|
|
|
|
{
|
|
|
|
// Depth buffer is inverted for improved precision near far plane
|
|
|
|
float depth = g_framebuffer_manager->PeekEFBDepth(x, y);
|
|
|
|
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
|
|
|
|
depth = 1.0f - depth;
|
|
|
|
|
2021-04-26 09:55:38 +00:00
|
|
|
// Convert to 24bit depth
|
|
|
|
u32 z24depth = std::clamp<u32>(static_cast<u32>(depth * 16777216.0f), 0, 0xFFFFFF);
|
|
|
|
|
2021-02-11 02:11:31 +00:00
|
|
|
if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16)
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
2021-04-26 09:55:38 +00:00
|
|
|
// 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);
|
2019-02-15 01:59:50 +00:00
|
|
|
}
|
|
|
|
|
2021-04-26 09:55:38 +00:00
|
|
|
return z24depth;
|
2019-02-15 01:59:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points)
|
|
|
|
{
|
|
|
|
if (type == EFBAccessType::PokeColor)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < num_points; i++)
|
|
|
|
{
|
|
|
|
// Convert to expected format (BGRA->RGBA)
|
|
|
|
// TODO: Check alpha, depending on mode?
|
|
|
|
const EfbPokeData& point = points[i];
|
|
|
|
u32 color = ((point.data & 0xFF00FF00) | ((point.data >> 16) & 0xFF) |
|
|
|
|
((point.data << 16) & 0xFF0000));
|
|
|
|
g_framebuffer_manager->PokeEFBColor(point.x, point.y, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // if (type == EFBAccessType::PokeZ)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < num_points; i++)
|
|
|
|
{
|
|
|
|
// Convert to floating-point depth.
|
|
|
|
const EfbPokeData& point = points[i];
|
|
|
|
float depth = float(point.data & 0xFFFFFF) / 16777216.0f;
|
|
|
|
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
|
|
|
|
depth = 1.0f - depth;
|
|
|
|
|
|
|
|
g_framebuffer_manager->PokeEFBDepth(point.x, point.y, depth);
|
|
|
|
}
|
|
|
|
}
|
2018-01-26 05:09:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 15:04:46 +00:00
|
|
|
unsigned int Renderer::GetEFBScale() const
|
|
|
|
{
|
|
|
|
return m_efb_scale;
|
|
|
|
}
|
|
|
|
|
2017-04-08 23:40:04 +00:00
|
|
|
int Renderer::EFBToScaledX(int x) const
|
2011-01-07 04:57:59 +00:00
|
|
|
{
|
2017-07-03 14:32:02 +00:00
|
|
|
return x * static_cast<int>(m_efb_scale);
|
2012-10-02 20:11:15 +00:00
|
|
|
}
|
|
|
|
|
2017-04-08 23:40:04 +00:00
|
|
|
int Renderer::EFBToScaledY(int y) const
|
2011-01-07 04:57:59 +00:00
|
|
|
{
|
2017-07-03 14:32:02 +00:00
|
|
|
return y * static_cast<int>(m_efb_scale);
|
2012-10-02 20:11:15 +00:00
|
|
|
}
|
|
|
|
|
2017-04-08 23:40:04 +00:00
|
|
|
float Renderer::EFBToScaledXf(float x) const
|
|
|
|
{
|
|
|
|
return x * ((float)GetTargetWidth() / (float)EFB_WIDTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
float Renderer::EFBToScaledYf(float y) const
|
|
|
|
{
|
|
|
|
return y * ((float)GetTargetHeight() / (float)EFB_HEIGHT);
|
|
|
|
}
|
|
|
|
|
2017-04-09 18:38:43 +00:00
|
|
|
std::tuple<int, int> Renderer::CalculateTargetScale(int x, int y) const
|
2012-10-02 20:11:15 +00:00
|
|
|
{
|
2017-07-03 14:32:02 +00:00
|
|
|
return std::make_tuple(x * static_cast<int>(m_efb_scale), y * static_cast<int>(m_efb_scale));
|
2012-10-02 20:11:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// return true if target size changed
|
2016-11-10 13:31:44 +00:00
|
|
|
bool Renderer::CalculateTargetSize()
|
2012-10-02 20:11:15 +00:00
|
|
|
{
|
2017-07-03 14:32:02 +00:00
|
|
|
if (g_ActiveConfig.iEFBScale == EFB_SCALE_AUTO_INTEGRAL)
|
|
|
|
{
|
2023-01-27 04:03:15 +00:00
|
|
|
auto target_rectangle = g_presenter->GetTargetRectangle();
|
2017-07-03 14:32:02 +00:00
|
|
|
// Set a scale based on the window size
|
2023-01-27 04:03:15 +00:00
|
|
|
int width = EFB_WIDTH * target_rectangle.GetWidth() / m_last_xfb_width;
|
|
|
|
int height = EFB_HEIGHT * target_rectangle.GetHeight() / m_last_xfb_height;
|
2017-07-03 14:32:02 +00:00
|
|
|
m_efb_scale = std::max((width - 1) / EFB_WIDTH + 1, (height - 1) / EFB_HEIGHT + 1);
|
|
|
|
}
|
|
|
|
else
|
2012-10-02 20:11:15 +00:00
|
|
|
{
|
2017-07-03 14:32:02 +00:00
|
|
|
m_efb_scale = g_ActiveConfig.iEFBScale;
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-07-03 14:32:02 +00:00
|
|
|
const u32 max_size = g_ActiveConfig.backend_info.MaxTextureSize;
|
|
|
|
if (max_size < EFB_WIDTH * m_efb_scale)
|
|
|
|
m_efb_scale = max_size / EFB_WIDTH;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2019-08-05 02:57:12 +00:00
|
|
|
auto [new_efb_width, new_efb_height] = CalculateTargetScale(EFB_WIDTH, EFB_HEIGHT);
|
2019-02-15 01:59:50 +00:00
|
|
|
new_efb_width = std::max(new_efb_width, 1);
|
|
|
|
new_efb_height = std::max(new_efb_height, 1);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-04-09 18:38:43 +00:00
|
|
|
if (new_efb_width != m_target_width || new_efb_height != m_target_height)
|
2010-11-18 02:21:26 +00:00
|
|
|
{
|
2017-04-09 18:38:43 +00:00
|
|
|
m_target_width = new_efb_width;
|
|
|
|
m_target_height = new_efb_height;
|
2022-12-27 16:42:02 +00:00
|
|
|
auto& system = Core::System::GetInstance();
|
|
|
|
auto& pixel_shader_manager = system.GetPixelShaderManager();
|
|
|
|
pixel_shader_manager.SetEfbScaleChanged(EFBToScaledXf(1), EFBToScaledYf(1));
|
2010-11-18 02:21:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-28 06:53:16 +00:00
|
|
|
|
2019-08-05 02:45:25 +00:00
|
|
|
MathUtil::Rectangle<int> Renderer::ConvertEFBRectangle(const MathUtil::Rectangle<int>& rc) const
|
2019-02-15 01:59:50 +00:00
|
|
|
{
|
2019-04-15 14:47:46 +00:00
|
|
|
MathUtil::Rectangle<int> result;
|
2019-02-15 01:59:50 +00:00
|
|
|
result.left = EFBToScaledX(rc.left);
|
|
|
|
result.top = EFBToScaledY(rc.top);
|
|
|
|
result.right = EFBToScaledX(rc.right);
|
|
|
|
result.bottom = EFBToScaledY(rc.bottom);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-01-30 11:46:10 +00:00
|
|
|
void Renderer::UpdateWidescreen()
|
2011-03-27 02:55:08 +00:00
|
|
|
{
|
2023-01-30 11:46:10 +00:00
|
|
|
if (SConfig::GetInstance().bWii)
|
|
|
|
m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2023-01-30 11:46:10 +00:00
|
|
|
// suggested_aspect_mode overrides SYSCONF_WIDESCREEN
|
|
|
|
if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog)
|
|
|
|
m_is_game_widescreen = false;
|
|
|
|
else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide)
|
|
|
|
m_is_game_widescreen = true;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2023-01-30 11:46:10 +00:00
|
|
|
// If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9.
|
|
|
|
if (!g_ActiveConfig.bWidescreenHack)
|
2019-12-05 13:11:52 +00:00
|
|
|
{
|
2023-01-30 11:46:10 +00:00
|
|
|
const auto aspect_mode = g_ActiveConfig.aspect_mode;
|
|
|
|
if (aspect_mode == AspectMode::Analog)
|
|
|
|
m_is_game_widescreen = false;
|
|
|
|
else if (aspect_mode == AspectMode::AnalogWide)
|
|
|
|
m_is_game_widescreen = true;
|
2011-03-27 02:55:08 +00:00
|
|
|
}
|
2019-08-17 19:40:58 +00:00
|
|
|
}
|
|
|
|
|
2020-01-26 01:04:50 +00:00
|
|
|
// Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode.
|
|
|
|
void Renderer::UpdateWidescreenHeuristic()
|
2014-02-05 10:48:45 +00:00
|
|
|
{
|
2020-01-26 01:04:50 +00:00
|
|
|
const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount();
|
|
|
|
|
|
|
|
// If suggested_aspect_mode (GameINI) is configured don't use heuristic.
|
|
|
|
if (g_ActiveConfig.suggested_aspect_mode != AspectMode::Auto)
|
|
|
|
return;
|
|
|
|
|
2023-01-30 11:46:10 +00:00
|
|
|
UpdateWidescreen();
|
|
|
|
|
2020-01-26 01:04:50 +00:00
|
|
|
// If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic.
|
|
|
|
if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::Analog ||
|
|
|
|
g_ActiveConfig.aspect_mode == AspectMode::AnalogWide))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Modify the threshold based on which aspect ratio we're already using:
|
|
|
|
// If the game's in 4:3, it probably won't switch to anamorphic, and vice-versa.
|
|
|
|
static constexpr u32 TRANSITION_THRESHOLD = 3;
|
|
|
|
|
|
|
|
const auto looks_normal = [](auto& counts) {
|
|
|
|
return counts.normal_vertex_count > counts.anamorphic_vertex_count * TRANSITION_THRESHOLD;
|
|
|
|
};
|
|
|
|
const auto looks_anamorphic = [](auto& counts) {
|
|
|
|
return counts.anamorphic_vertex_count > counts.normal_vertex_count * TRANSITION_THRESHOLD;
|
|
|
|
};
|
|
|
|
|
2020-01-31 21:14:19 +00:00
|
|
|
const auto& persp = flush_statistics.perspective;
|
|
|
|
const auto& ortho = flush_statistics.orthographic;
|
|
|
|
|
2020-01-26 01:04:50 +00:00
|
|
|
const auto ortho_looks_anamorphic = looks_anamorphic(ortho);
|
|
|
|
|
|
|
|
if (looks_anamorphic(persp) || ortho_looks_anamorphic)
|
2017-03-03 22:36:51 +00:00
|
|
|
{
|
2020-01-26 01:04:50 +00:00
|
|
|
// If either perspective or orthographic projections look anamorphic, it's a safe bet.
|
|
|
|
m_is_game_widescreen = true;
|
2018-11-07 18:00:24 +00:00
|
|
|
}
|
2020-01-26 01:04:50 +00:00
|
|
|
else if (looks_normal(persp) || (m_was_orthographically_anamorphic && looks_normal(ortho)))
|
2018-11-07 18:00:24 +00:00
|
|
|
{
|
2020-01-26 01:04:50 +00:00
|
|
|
// Many widescreen games (or AR/GeckoCodes) use anamorphic perspective projections
|
|
|
|
// with NON-anamorphic orthographic projections.
|
|
|
|
// This can cause incorrect changes to 4:3 when perspective projections are temporarily not
|
|
|
|
// shown. e.g. Animal Crossing's inventory menu.
|
2020-12-31 10:59:54 +00:00
|
|
|
// Unless we were in a situation which was orthographically anamorphic
|
2020-01-26 01:04:50 +00:00
|
|
|
// we won't consider orthographic data for changes from 16:9 to 4:3.
|
|
|
|
m_is_game_widescreen = false;
|
2018-11-07 18:00:24 +00:00
|
|
|
}
|
2017-03-03 22:36:51 +00:00
|
|
|
|
2020-01-26 01:04:50 +00:00
|
|
|
m_was_orthographically_anamorphic = ortho_looks_anamorphic;
|
|
|
|
}
|
|
|
|
|
2023-01-30 11:46:10 +00:00
|
|
|
void Renderer::OnConfigChanged(u32 bits)
|
2020-01-26 01:04:50 +00:00
|
|
|
{
|
2023-01-30 11:46:10 +00:00
|
|
|
if (bits & CONFIG_CHANGE_BIT_ASPECT_RATIO)
|
|
|
|
UpdateWidescreen();
|
|
|
|
}
|
2022-09-26 21:41:56 +00:00
|
|
|
|
2023-01-30 11:59:17 +00:00
|
|
|
void Renderer::TrackSwaps(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
|
2023-01-30 11:46:10 +00:00
|
|
|
{
|
2019-03-31 04:11:53 +00:00
|
|
|
if (xfb_addr && fb_width && fb_stride && fb_height)
|
2017-05-29 22:02:09 +00:00
|
|
|
{
|
2017-08-13 00:51:33 +00:00
|
|
|
// Update our last xfb values
|
2019-06-29 09:27:53 +00:00
|
|
|
m_last_xfb_addr = xfb_addr;
|
|
|
|
m_last_xfb_ticks = ticks;
|
|
|
|
m_last_xfb_width = fb_width;
|
|
|
|
m_last_xfb_stride = fb_stride;
|
|
|
|
m_last_xfb_height = fb_height;
|
2017-05-29 22:02:09 +00:00
|
|
|
}
|
2014-02-05 10:48:45 +00:00
|
|
|
}
|
2015-05-01 16:58:11 +00:00
|
|
|
|
2017-02-24 14:16:28 +00:00
|
|
|
bool Renderer::UseVertexDepthRange() const
|
|
|
|
{
|
|
|
|
// We can't compute the depth range in the vertex shader if we don't support depth clamp.
|
|
|
|
if (!g_ActiveConfig.backend_info.bSupportsDepthClamp)
|
|
|
|
return false;
|
|
|
|
|
2017-03-14 00:02:13 +00:00
|
|
|
// We need a full depth range if a ztexture is used.
|
2021-02-11 02:11:31 +00:00
|
|
|
if (bpmem.ztex2.op != ZTexOp::Disabled && !bpmem.zcontrol.early_ztest)
|
2017-03-14 00:02:13 +00:00
|
|
|
return true;
|
2017-02-24 14:16:28 +00:00
|
|
|
|
2017-03-14 00:02:13 +00:00
|
|
|
// If an inverted depth range is unsupported, we also need to check if the range is inverted.
|
|
|
|
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange && xfmem.viewport.zRange < 0.0f)
|
|
|
|
return true;
|
2017-02-24 14:16:28 +00:00
|
|
|
|
2017-03-14 00:02:13 +00:00
|
|
|
// If an oversized depth range or a ztexture is used, we need to calculate the depth range
|
|
|
|
// in the vertex shader.
|
|
|
|
return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f;
|
2017-02-24 14:16:28 +00:00
|
|
|
}
|
2018-02-25 07:56:09 +00:00
|
|
|
|
2019-06-29 09:27:53 +00:00
|
|
|
void Renderer::DoState(PointerWrap& p)
|
|
|
|
{
|
2020-01-26 01:04:50 +00:00
|
|
|
p.Do(m_is_game_widescreen);
|
2019-06-29 09:27:53 +00:00
|
|
|
p.Do(m_frame_count);
|
|
|
|
p.Do(m_prev_efb_format);
|
|
|
|
p.Do(m_last_xfb_ticks);
|
|
|
|
p.Do(m_last_xfb_addr);
|
|
|
|
p.Do(m_last_xfb_width);
|
|
|
|
p.Do(m_last_xfb_stride);
|
|
|
|
p.Do(m_last_xfb_height);
|
|
|
|
|
2023-01-26 22:34:59 +00:00
|
|
|
g_bounding_box->DoState(p);
|
2021-06-09 11:42:21 +00:00
|
|
|
|
2022-05-18 05:29:05 +00:00
|
|
|
if (p.IsReadMode())
|
2019-06-29 09:27:53 +00:00
|
|
|
{
|
2020-01-26 01:04:50 +00:00
|
|
|
m_was_orthographically_anamorphic = false;
|
|
|
|
|
2023-01-30 11:59:17 +00:00
|
|
|
// This technically counts as the end of the frame
|
|
|
|
AfterFrameEvent::Trigger();
|
|
|
|
|
|
|
|
// re-display the most recent XFB
|
|
|
|
g_presenter->ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride,
|
|
|
|
m_last_xfb_height, m_last_xfb_ticks);
|
2019-06-29 09:27:53 +00:00
|
|
|
}
|
2020-10-21 14:37:16 +00:00
|
|
|
|
|
|
|
#if defined(HAVE_FFMPEG)
|
2023-01-27 04:03:15 +00:00
|
|
|
g_frame_dumper->DoState(p);
|
2020-10-21 14:37:16 +00:00
|
|
|
#endif
|
2019-06-29 09:27:53 +00:00
|
|
|
}
|