OpenGL: Improve checks for fragment shader float precision.

Fixes #22001 (although there may be other artifacts).
This commit is contained in:
Henrik Rydgård committed 2026-07-30 10:03:15 +02:00
1 parent 683704d61d
commit 10a5d2f810
10 files changed
+49 -17

No files matched your search

+1
View File
@@ -297,6 +297,7 @@ D3D11DrawContext::D3D11DrawContext(ComPtr<ID3D11Device> device, ComPtr<ID3D11Dev
_assert_(featureLevel_ >= D3D_FEATURE_LEVEL_9_3);
caps_.coordConvention = CoordConvention::Direct3D11;
caps_.fragmentShaderFullPrecisionFloat = true;
switch (featureLevel_) {
case D3D_FEATURE_LEVEL_11_1:
+1 -1
View File
@@ -494,7 +494,7 @@ bool CheckGLExtensions() {
GL_LOW_FLOAT, GL_MEDIUM_FLOAT, GL_HIGH_FLOAT,
GL_LOW_INT, GL_MEDIUM_INT, GL_HIGH_INT
};
GLint shaderTypes[2] = {
const GLint shaderTypes[2] = {
GL_VERTEX_SHADER, GL_FRAGMENT_SHADER
};
for (int st = 0; st < 2; st++) {
+9
View File
@@ -138,6 +138,15 @@ struct GLExtensions {
// greater-or-equal than
bool VersionGEThan(int major, int minor, int sub = 0);
int GLSLVersion();
bool FullPrecisionIntInFragment() const {
return range[1][5][0] >= 30 && range[1][5][1] >= 30;
}
// If 23, normal floats are supported.
int HighpFragmentFloatMantissaBits() const {
return precision[1][2];
}
};
extern GLExtensions gl_extensions;
+9 -4
View File
@@ -561,11 +561,11 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
} else {
caps_.preferredDepthBufferFormat = DataFormat::D16;
}
if (gl_extensions.FullPrecisionIntInFragment()) {
caps_.fragmentShaderInt32Supported = true;
}
if (gl_extensions.GLES3) {
// Mali reports 30 but works fine...
if (gl_extensions.range[1][5][1] >= 30) {
caps_.fragmentShaderInt32Supported = true;
}
// 30 is an ok value for the max since it's 2^31-1, which is the max value for a signed int. NVIDIA also reports this.
caps_.samplerLodControl = true;
}
caps_.texture3DSupported = gl_extensions.GLES3 || gl_extensions.OES_texture_3D;
@@ -574,12 +574,17 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
if (gl_extensions.VersionGEThan(3, 3, 0)) {
caps_.fragmentShaderInt32Supported = true;
}
caps_.fragmentShaderFullPrecisionFloat = true;
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
caps_.texture3DSupported = true;
caps_.textureDepthSupported = true;
caps_.samplerLodControl = true;
}
if (gl_extensions.HighpFragmentFloatMantissaBits() >= 23 && !(gl_extensions.bugs & BUG_PVR_SHADER_PRECISION_TERRIBLE)) {
caps_.fragmentShaderFullPrecisionFloat = true;
}
caps_.maxTextureSize = gl_extensions.maxTextureSize;
caps_.coordConvention = CoordConvention::OpenGL;
caps_.setMaxFrameLatencySupported = true;
+1
View File
@@ -918,6 +918,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread)
// Make sure that the surface has been initialized.
_dbg_assert_(vulkan->GetAvailablePresentModes().size() > 0);
caps_.fragmentShaderFullPrecisionFloat = true;
caps_.coordConvention = CoordConvention::Vulkan;
caps_.setMaxFrameLatencySupported = true;
caps_.anisoSupported = vulkan->GetDeviceFeatures().enabled.standard.samplerAnisotropy != 0;
+1
View File
@@ -612,6 +612,7 @@ struct DeviceCaps {
bool framebufferStencilBlitSupported;
bool framebufferFetchSupported;
bool texture3DSupported;
bool fragmentShaderFullPrecisionFloat; // Without this, our clip plane workaround doesn't work
bool fragmentShaderInt32Supported;
bool fragmentShaderDepthWriteSupported;
bool fragmentShaderStencilWriteSupported;
+18 -10
View File
@@ -138,11 +138,15 @@ void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform,
id.SetBit(VS_BIT_NORM_REVERSE, gstate.areNormalsReversed());
}
if (clipInfoFlags & ClipInfoFlags::DepthClampFragment) {
id.SetBit(VS_BIT_FS_DEPTH_CLAMP);
}
if (clipInfoFlags & ClipInfoFlags::MinMaxZDiscard) {
id.SetBit(VS_BIT_FS_MINMAX_DISCARD);
if (gstate_c.Use(GPU_USE_FULL_PRECISION_IN_FRAGMENT)) {
if (clipInfoFlags & ClipInfoFlags::DepthClampFragment) {
id.SetBit(VS_BIT_FS_DEPTH_CLAMP);
}
if (clipInfoFlags & ClipInfoFlags::MinMaxZDiscard) {
id.SetBit(VS_BIT_FS_MINMAX_DISCARD);
}
} else {
// TODO: We're gonna need to soft-clip... Ugh.
}
id.SetBit(VS_BIT_FLATSHADE, doFlatShading);
@@ -286,11 +290,15 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip
// NOTE: This check MUST be identical to the one in ComputeVertexShaderID, otherwise we might get mismatches between VS and FS and end up with no shader at all.
if (!isModeThrough) {
if (clipInfoFlags & ClipInfoFlags::DepthClampFragment) {
id.SetBit(FS_BIT_DEPTH_CLAMP);
}
if (clipInfoFlags & ClipInfoFlags::MinMaxZDiscard) {
id.SetBit(FS_BIT_MINMAX_DISCARD);
if (gstate_c.Use(GPU_USE_FULL_PRECISION_IN_FRAGMENT)) {
if (clipInfoFlags & ClipInfoFlags::DepthClampFragment) {
id.SetBit(FS_BIT_DEPTH_CLAMP);
}
if (clipInfoFlags & ClipInfoFlags::MinMaxZDiscard) {
id.SetBit(FS_BIT_MINMAX_DISCARD);
}
} else {
// TODO: We're gonna need to soft-clip... Ugh.
}
} else {
_dbg_assert_(0 == (clipInfoFlags & (ClipInfoFlags::DepthClampFragment | ClipInfoFlags::MinMaxZDiscard)));
+4
View File
@@ -578,6 +578,10 @@ u32 GPUCommonHW::CheckGPUFeatures() const {
features |= GPU_USE_CULL_DISTANCE;
}
if (draw_->GetDeviceCaps().fragmentShaderFullPrecisionFloat) {
features |= GPU_USE_FULL_PRECISION_IN_FRAGMENT;
}
if (draw_->GetDeviceCaps().depthClampSupported) {
features |= GPU_USE_DEPTH_CLAMP;
}
+1 -1
View File
@@ -483,7 +483,7 @@ enum : u32 {
// Free bit: 18
GPU_USE_FRAMEBUFFER_ARRAYS = FLAG_BIT(19),
GPU_USE_FRAMEBUFFER_FETCH = FLAG_BIT(20),
// Free bit: 21
GPU_USE_FULL_PRECISION_IN_FRAGMENT = FLAG_BIT(21), // Required for the min-max/depthclamp in fragment shader stuff
GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT = FLAG_BIT(22),
GPU_ROUND_DEPTH_TO_16BIT = FLAG_BIT(23), // Can be disabled either per game or if we use a real 16-bit depth buffer
GPU_USE_CLIP_DISTANCE = FLAG_BIT(24),
+4 -1
View File
@@ -184,6 +184,8 @@ void SystemInfoScreen::CreateDeviceInfoTab(UI::LinearLayout *deviceSpecs) {
const int highp_int_max = gl_extensions.range[1][5][1];
const int highp_float_min = gl_extensions.range[1][2][0];
const int highp_float_max = gl_extensions.range[1][2][1];
const int highp_int_precision = gl_extensions.precision[1][5];
const int highp_float_precision = gl_extensions.precision[1][2];
if (highp_int_max != 0) {
char temp[128];
snprintf(temp, sizeof(temp), "%d-%d", highp_int_min, highp_int_max);
@@ -191,7 +193,8 @@ void SystemInfoScreen::CreateDeviceInfoTab(UI::LinearLayout *deviceSpecs) {
}
if (highp_float_max != 0) {
char temp[128];
snprintf(temp, sizeof(temp), "%d-%d", highp_float_min, highp_float_max);
// if highp_float_precision is 23, full floats are available which is good.
snprintf(temp, sizeof(temp), "%d-%d (%d bits)", highp_float_min, highp_float_max, highp_float_precision);
gpuInfo->Add(new InfoItem(si->T("High precision float range"), temp));
}
}