Files
ARMSX2/pcsx2/GS/Renderers/Common/GSGPUProfile.h
T
jpolo1224 4d8701a49d GS: driver-bug database and per-driver shader workarounds
Ported from EmuCoreX with sashkinbro's approval. The GPU family alone was
never enough to decide behaviour: the same Mali part behaves differently
under ARM's proprietary driver than under Mesa PanVK, a lesson this tree
learned twice the expensive way - the r44p1 DEVICE_LOST fix had to be
gated on driverID rather than vendorID, and the 8 Elite push-descriptor
disable likewise. Driver identity, version and a bug set are now recorded
in a table, so the next device quirk is an entry rather than another
bespoke branch. Twenty-five rules, with match confidence so a vendor-wide
rule never overrides a driver-version-specific one.

The shaders gain gpu_bitwise_and / gpu_bitwise_not / gpu_boolean_not,
whose bodies the device emits: plain a & b normally, scalarized where the
database says the driver miscompiles vector bitwise ops. A device matching
no rule renders exactly as before.

opengl/tfx_fs.glsl already carried this fix as IAND3/UAND2/UAND4 macros
gated on GPU_PROFILE_MALI. Those are replaced by the shared helpers, but
the GL macro is deliberately (workaround || IsMaliGPUProfile()): Mali
reached through ANGLE or Panfrost resolves a non-ARM driver and matches no
rule, so a database-only gate would have silently removed a fix those
users have today. Widening only.

SHADER_CACHE_VERSION 108 -> 109. Every TFX and convert shader's source
text changed, so a blob cached from 108 no longer matches the source that
produced it; without the bump users would get stale binaries and garbage
rendering after updating.

RewriteConstantLoads is deliberately absent - the database records
BrokenConstantLoad with no workaround bits, so the macro would be
permanently zero and the shader code dead.

Shaders validated offline with glslc across 136 Vulkan and 296 GL/GLES
permutations, every macro on and off; the failure set is byte-identical to
the pre-change baseline.
2026-07-26 14:53:48 -04:00

265 lines
7.4 KiB
C++

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "common/Pcsx2Defs.h"
#include <string>
#include <string_view>
enum class GpuProfileOverride : u8
{
Auto,
Mali,
Adreno,
PowerVR,
Xclipse,
};
enum class RuntimeGpuProfile : u8
{
Unknown,
Mali,
Adreno,
PowerVR,
Xclipse,
/// Apple Silicon (M-series / A-series). A TBDR like the mobile parts, but it is NOT one of
/// them and must never inherit their workarounds — before this existed, desktop GL resolved
/// anything not-Mali to Adreno, so an M2 ran Adreno-only paths (reported by bmd: "GL: Adreno -
/// routing depth feedback through the depth sampler"). Distinct from Unknown so the tiler-ness
/// can be acted on deliberately later rather than by accident.
Apple,
};
enum class MobileGpuArchitecture : u8
{
Unknown,
Adreno2xx,
Adreno3xx,
Adreno4xx,
Adreno5xx,
Adreno6xx,
Adreno7xx,
Adreno8xx,
AdrenoX,
MaliUtgard,
MaliMidgard,
MaliBifrost,
MaliValhall1,
MaliValhall2,
MaliValhall3,
MaliFifthGen,
MaliG1,
PowerVR,
};
// ---------------------------------------------------------------------------------------------
// Driver identity and known-bug model, ported from EmuCoreX (sashkinbro) with his approval.
//
// The GPU family alone is not enough to decide behaviour: the same Mali part behaves differently
// under Arm's proprietary driver than under Mesa PanVK, which is a lesson this tree learned the
// expensive way (the r44p1 DEVICE_LOST fix had to be gated on driverID, not vendorID, and the
// 8 Elite push-descriptor disable likewise). Recording it as driver + version + a bug set means
// the next device-specific quirk is a table entry rather than another bespoke branch.
// ---------------------------------------------------------------------------------------------
enum class MobileGpuApi : u8
{
Unknown,
OpenGL,
Vulkan,
};
// Deliberately independent of VkDriverId so profile resolution stays unit-testable without
// pulling in Vulkan headers, and so the OpenGL path can use the same table.
enum class MobileGpuDriver : u8
{
Unknown,
ArmProprietary,
MesaPanVK,
QualcommProprietary,
MesaTurnip,
ImaginationProprietary,
MesaPowerVR,
Angle,
};
/// How specifically a profile was matched. A rule matched on the exact driver version is worth
/// more than one matched on the vendor alone, so a broad entry never overrides a precise one.
enum class DriverProfileConfidence : u8
{
Unknown,
Vendor,
Model,
Driver,
DriverVersion,
};
/// Observed driver defects. Naming is descriptive of the DEFECT, not of the fix, so one bug can
/// drive several workarounds and the table stays readable.
enum class DriverBug : u8
{
BrokenBufferStreaming,
BrokenUnsynchronizedMapping,
BrokenNegatedBoolean,
BrokenVectorBitwiseAnd,
BrokenBitwiseOpNegation,
BrokenPrimitiveRestart,
BrokenPushDescriptors,
BrokenProvokingVertex,
BrokenAttachmentFeedbackLoopLayout,
BrokenSubpassFeedback,
BrokenColorWriteMaskWithDepthTest,
BrokenDepthStencilDiscard,
BrokenReversedDepthRange,
SlowCachedReadbackMemory,
SlowOptimalImageToBufferCopy,
BrokenClearLoadOpRenderPass,
Broken16BitTextureFormats,
BrokenGenerateMipmapTallTexture,
BrokenEmptyRenderPass,
BrokenConstantLoad,
BrokenUniformIndexing,
BrokenVSync,
BrokenMultithreadedShaderCompilation,
BrokenDynamicRendering,
BrokenImagelessFramebuffer,
BrokenExtendedDynamicState,
BrokenPrimitiveTopologyDynamicState,
BrokenGraphicsPipelineLibrary,
Count,
};
/// What we actually DO about a bug. Kept separate from [DriverBug] because the same mitigation
/// answers several defects, and because a workaround can be forced on for testing without
/// claiming the device has the bug.
enum class DriverWorkaround : u8
{
RewriteBooleanNegation,
ScalarizeVectorBitwiseAnd,
StoreBitwiseNegationInTemporary,
UseDescriptorSets,
DisableProvokingVertex,
DisableAttachmentFeedbackLoopLayout,
EmulateColorWriteMask,
PreferCoherentReadback,
UseStagingImageForReadback,
AvoidClearLoadOpRenderPass,
GenerateMipmapManuallyForTallTextures,
RewriteUniformIndexing,
ForceFifoPresent,
AlignSwapchainWidthTo32,
Count,
};
struct MobileDriverVersion
{
u32 raw = 0;
u16 major = 0;
u16 minor = 0;
u16 patch = 0;
u32 build = 0;
bool known = false;
bool legacy_hash = false;
};
/// Everything the resolver is allowed to look at. Filled from VkPhysicalDeviceProperties on the
/// Vulkan path and from the GL strings otherwise.
struct MobileDriverContext
{
MobileGpuApi api = MobileGpuApi::Unknown;
u32 vendor_id = 0;
u32 device_id = 0;
u32 driver_version = 0;
u32 driver_id = 0;
u32 api_version = 0;
u32 android_sdk = 0;
u32 max_draw_indirect_count = 0;
std::string_view driver_name;
std::string_view driver_info;
std::string_view api_version_string;
};
struct MobileDriverProfile
{
static constexpr u32 DATABASE_VERSION = 1;
MobileGpuApi api = MobileGpuApi::Unknown;
MobileGpuDriver driver = MobileGpuDriver::Unknown;
MobileDriverVersion version;
u64 bugs = 0;
u64 workarounds = 0;
u32 matched_rule_count = 0;
DriverProfileConfidence confidence = DriverProfileConfidence::Unknown;
/// True when nothing in the table matched and the safe defaults are in force.
bool conservative_fallback = true;
std::string driver_name;
constexpr bool HasBug(DriverBug bug) const
{
return (bugs & (u64{1} << static_cast<u8>(bug))) != 0;
}
constexpr bool UsesWorkaround(DriverWorkaround workaround) const
{
return (workarounds & (u64{1} << static_cast<u8>(workaround))) != 0;
}
};
// Both sets are u64 bitfields, so neither enum may exceed 64 entries without widening them.
static_assert(static_cast<u8>(DriverBug::Count) <= 64);
static_assert(static_cast<u8>(DriverWorkaround::Count) <= 64);
struct MobileGsTuning
{
bool constrained = true;
bool prefer_new_textures = false;
bool force_partial_texture_preloading = true;
u32 pooled_targets = 96;
u32 target_age = 8;
u32 pooled_textures = 96;
u32 texture_age = 6;
};
struct MobileGpuIdentity
{
MobileGpuArchitecture architecture = MobileGpuArchitecture::Unknown;
u16 model_number = 0;
u8 core_count = 0;
bool recognized = false;
std::string name = "Unknown";
};
struct GpuProfileSelection
{
GpuProfileOverride override_mode = GpuProfileOverride::Auto;
RuntimeGpuProfile runtime_profile = RuntimeGpuProfile::Unknown;
bool is_mediatek_soc = false;
MobileGpuIdentity gpu;
MobileGsTuning gs_tuning;
MobileDriverProfile driver;
std::string hints;
};
class GpuProfileDetector
{
public:
static GpuProfileOverride ParseOverride(std::string_view value);
static const char* OverrideToConfigString(GpuProfileOverride value);
static const char* OverrideToString(GpuProfileOverride value);
static const char* RuntimeProfileToString(RuntimeGpuProfile value);
static const char* ArchitectureToString(MobileGpuArchitecture value);
static const char* ApiToString(MobileGpuApi value);
static const char* DriverToString(MobileGpuDriver value);
static const char* BugToString(DriverBug value);
static const char* WorkaroundToString(DriverWorkaround value);
static GpuProfileSelection Resolve(std::string_view override_value, std::string_view gpu_vendor,
std::string_view gpu_renderer_or_name);
/// Overload that also resolves the driver profile. The three-argument form keeps working and
/// simply leaves GpuProfileSelection::driver in its conservative-fallback state.
static GpuProfileSelection Resolve(std::string_view override_value, std::string_view gpu_vendor,
std::string_view gpu_renderer_or_name, const MobileDriverContext& driver_context);
};