Show a panic alert if the CP vertex config doesn't match the XF vertex config

This probably isn't triggered by real games, but it's possible to accidentally do it with libogc (which results in freezes on real hardware).
This commit is contained in:
Pokechu22 2022-05-17 13:42:31 -07:00
parent 46bcdc4372
commit 38a75f6a49
3 changed files with 87 additions and 1 deletions

View File

@ -136,7 +136,7 @@ void DolphinAnalytics::ReportGameStart()
} }
// Keep in sync with enum class GameQuirk definition. // Keep in sync with enum class GameQuirk definition.
constexpr std::array<const char*, 24> GAME_QUIRKS_NAMES{ constexpr std::array<const char*, 27> GAME_QUIRKS_NAMES{
"icache-matters", "icache-matters",
"directly-reads-wiimote-input", "directly-reads-wiimote-input",
"uses-DVDLowStopLaser", "uses-DVDLowStopLaser",
@ -161,6 +161,9 @@ constexpr std::array<const char*, 24> GAME_QUIRKS_NAMES{
"sets-xf-clipdisable-bit-0", "sets-xf-clipdisable-bit-0",
"sets-xf-clipdisable-bit-1", "sets-xf-clipdisable-bit-1",
"sets-xf-clipdisable-bit-2", "sets-xf-clipdisable-bit-2",
"mismatched-gpu-colors-between-cp-and-xf",
"mismatched-gpu-normals-between-cp-and-xf",
"mismatched-gpu-tex-coords-between-cp-and-xf",
}; };
static_assert(GAME_QUIRKS_NAMES.size() == static_cast<u32>(GameQuirk::COUNT), static_assert(GAME_QUIRKS_NAMES.size() == static_cast<u32>(GameQuirk::COUNT),
"Game quirks names and enum definition are out of sync."); "Game quirks names and enum definition are out of sync.");

View File

@ -81,6 +81,13 @@ enum class GameQuirk
SETS_XF_CLIPDISABLE_BIT_1, SETS_XF_CLIPDISABLE_BIT_1,
SETS_XF_CLIPDISABLE_BIT_2, SETS_XF_CLIPDISABLE_BIT_2,
// Similar to the XF-BP mismatch, CP and XF might be configured with different vertex formats.
// Real hardware seems to hang in this case, so games probably don't do this, but it would
// be good to know if anything does it.
MISMATCHED_GPU_COLORS_BETWEEN_CP_AND_XF,
MISMATCHED_GPU_NORMALS_BETWEEN_CP_AND_XF,
MISMATCHED_GPU_TEX_COORDS_BETWEEN_CP_AND_XF,
COUNT, COUNT,
}; };

View File

@ -16,6 +16,7 @@
#include "Common/EnumMap.h" #include "Common/EnumMap.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/DolphinAnalytics.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "VideoCommon/BPMemory.h" #include "VideoCommon/BPMemory.h"
@ -28,6 +29,7 @@
#include "VideoCommon/VertexLoaderBase.h" #include "VideoCommon/VertexLoaderBase.h"
#include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VertexManagerBase.h"
#include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/XFMemory.h"
namespace VertexLoaderManager namespace VertexLoaderManager
{ {
@ -249,6 +251,78 @@ static VertexLoaderBase* RefreshLoader(int vtx_attr_group, bool preprocess = fal
return loader; return loader;
} }
static void CheckCPConfiguration(int vtx_attr_group)
{
// Validate that the XF input configuration matches the CP configuration
u32 num_cp_colors = std::count_if(
g_main_cp_state.vtx_desc.low.Color.begin(), g_main_cp_state.vtx_desc.low.Color.end(),
[](auto format) { return format != VertexComponentFormat::NotPresent; });
u32 num_cp_tex_coords = std::count_if(
g_main_cp_state.vtx_desc.high.TexCoord.begin(), g_main_cp_state.vtx_desc.high.TexCoord.end(),
[](auto format) { return format != VertexComponentFormat::NotPresent; });
u32 num_cp_normals;
if (g_main_cp_state.vtx_desc.low.Normal == VertexComponentFormat::NotPresent)
num_cp_normals = 0;
else if (g_main_cp_state.vtx_attr[vtx_attr_group].g0.NormalElements == NormalComponentCount::NTB)
num_cp_normals = 3;
else
num_cp_normals = 1;
std::optional<u32> num_xf_normals;
switch (xfmem.invtxspec.numnormals)
{
case NormalCount::None:
num_xf_normals = 0;
break;
case NormalCount::Normal:
num_xf_normals = 1;
break;
case NormalCount::NormalTangentBinormal:
num_xf_normals = 3;
break;
default:
PanicAlertFmt("xfmem.invtxspec.numnormals is invalid: {}", xfmem.invtxspec.numnormals);
break;
}
if (num_cp_colors != xfmem.invtxspec.numcolors || num_cp_normals != num_xf_normals ||
num_cp_tex_coords != xfmem.invtxspec.numtextures)
{
PanicAlertFmt("Mismatched configuration between CP and XF stages - {}/{} colors, {}/{} "
"normals, {}/{} texture coordinates. Please report on the issue tracker.\n\n"
"VCD: {:08x} {:08x}\nVAT {}: {:08x} {:08x} {:08x}\nXF vertex spec: {:08x}",
num_cp_colors, xfmem.invtxspec.numcolors, num_cp_normals,
num_xf_normals.has_value() ? fmt::to_string(num_xf_normals.value()) : "invalid",
num_cp_tex_coords, xfmem.invtxspec.numtextures, g_main_cp_state.vtx_desc.low.Hex,
g_main_cp_state.vtx_desc.high.Hex, vtx_attr_group,
g_main_cp_state.vtx_attr[vtx_attr_group].g0.Hex,
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex, xfmem.invtxspec.hex);
// Analytics reporting so we can discover which games have this problem, that way when we
// eventually simulate the behavior we have test cases for it.
if (num_cp_colors != xfmem.invtxspec.numcolors)
{
DolphinAnalytics::Instance().ReportGameQuirk(
GameQuirk::MISMATCHED_GPU_COLORS_BETWEEN_CP_AND_XF);
}
if (num_cp_normals != num_xf_normals)
{
DolphinAnalytics::Instance().ReportGameQuirk(
GameQuirk::MISMATCHED_GPU_NORMALS_BETWEEN_CP_AND_XF);
}
if (num_cp_tex_coords != xfmem.invtxspec.numtextures)
{
DolphinAnalytics::Instance().ReportGameQuirk(
GameQuirk::MISMATCHED_GPU_TEX_COORDS_BETWEEN_CP_AND_XF);
}
// Don't bail out, though; we can still render something successfully
// (real hardware seems to hang in this case, though)
}
}
int RunVertices(int vtx_attr_group, OpcodeDecoder::Primitive primitive, int count, DataReader src, int RunVertices(int vtx_attr_group, OpcodeDecoder::Primitive primitive, int count, DataReader src,
bool is_preprocess) bool is_preprocess)
{ {
@ -265,6 +339,8 @@ int RunVertices(int vtx_attr_group, OpcodeDecoder::Primitive primitive, int coun
if (is_preprocess) if (is_preprocess)
return size; return size;
CheckCPConfiguration(vtx_attr_group);
// If the native vertex format changed, force a flush. // If the native vertex format changed, force a flush.
if (loader->m_native_vertex_format != s_current_vtx_fmt || if (loader->m_native_vertex_format != s_current_vtx_fmt ||
loader->m_native_components != g_current_components) loader->m_native_components != g_current_components)