[Feat, Test] (MG_Backend, MG_Util, MG_Impl): report the fp64 tier at startup, decline 64-bit vertex formats everywhere, advertise GL_ARB_gpu_shader_fp64 only on request

This commit is contained in:
2026-08-12 06:25:35 -04:00
parent 62a2dae5ba
commit 796a57a115
6 changed files with 139 additions and 16 deletions
+8
View File
@@ -82,6 +82,14 @@ namespace MobileGL::MG_Config {
#endif
// MOBILEGL_DISABLE_SUBGROUP: force-disable Vulkan shader subgroup support.
Bool DisableSubgroup = false;
// MOBILEGL_ADVERTISE_FP64: add GL_ARB_gpu_shader_fp64 to the advertised extension
// string. `double` in a shader always WORKS - it is narrowed to 32 bits before any
// module reaches a backend (ShaderTranspiler::DemoteFloat64Pass) - but the extension
// promises 64-bit precision, and that is the one thing the narrowing cannot deliver.
// Off by default so an application that checks the string before using doubles keeps
// its float path; on for measuring what the conformance suite makes of the demoted
// precision. See the DemoteFloat64Pass header and the "fp64" POST row.
Bool AdvertiseFp64 = false;
// MOBILEGL_MAGMA_R11G11B10F_FALLBACK: use fallback format for R11G11B10F on Vulkan.
Bool MagmaR11G11B10FFallback = false;
// MOBILEGL_MAGMA_FRAMESINFLIGHT: requested Magma frames in flight, defaulting to 3.
+1
View File
@@ -167,6 +167,7 @@ namespace MobileGL::MG_ConfigLoader {
QueryEnvVariable("MOBILEGL_TRACE_ANGLE_VARIANT", features.TraceAngleVariant, "");
#endif
features.DisableSubgroup = QueryEnvFlag("MOBILEGL_DISABLE_SUBGROUP");
features.AdvertiseFp64 = QueryEnvFlag("MOBILEGL_ADVERTISE_FP64");
features.MagmaR11G11B10FFallback = QueryEnvFlag("MOBILEGL_MAGMA_R11G11B10F_FALLBACK");
features.MagmaFramesInFlight = QueryEnvUint32("MOBILEGL_MAGMA_FRAMESINFLIGHT", 3, 1, 64);
features.AvoidSamplerMipmapMinFilter =
@@ -960,6 +960,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (MG_Util::Async::AsyncShaderCompileEnabled()) {
extensions.push_back(E_GL_KHR_parallel_shader_compile);
}
// GL_ARB_gpu_shader_fp64 is opt-in (MOBILEGL_ADVERTISE_FP64). Every `double` in a
// shader compiles and runs already - it is narrowed to 32 bits before the module
// reaches this backend - so an application that simply uses doubles needs nothing
// advertised. What the extension additionally promises is 64-bit PRECISION, which no
// mobile GPU has and the narrowing cannot fake, so advertising it by default would
// make an application that checks the string take a path MobileGL cannot honour.
if (MG_Config::Features.AdvertiseFp64) {
extensions.push_back(E_GL_ARB_gpu_shader_fp64);
}
// Only advertised when the device driver actually has usable timer queries
// (GL_EXT_disjoint_timer_query plus its entry points) and the
// MOBILEGL_DISABLE_TIMERQUERY escape hatch is off.
@@ -539,6 +539,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (MG_Util::Async::AsyncShaderCompileEnabled()) {
extensions.push_back(E_GL_KHR_parallel_shader_compile);
}
// GL_ARB_gpu_shader_fp64 is opt-in (MOBILEGL_ADVERTISE_FP64). Every `double` in a
// shader compiles and runs already - it is narrowed to 32 bits before the module
// reaches this backend - so an application that simply uses doubles needs nothing
// advertised. What the extension additionally promises is 64-bit PRECISION, which no
// mobile GPU has and the narrowing cannot fake, so advertising it by default would
// make an application that checks the string take a path MobileGL cannot honour.
if (MG_Config::Features.AdvertiseFp64) {
extensions.push_back(E_GL_ARB_gpu_shader_fp64);
}
// GL_ARB_timer_query gates MC's F3 GPU% (LWJGL checks the extension string);
// only advertised when the device actually supports timestamp queries and the
// MOBILEGL_DISABLE_TIMERQUERY escape hatch is off.
@@ -877,7 +886,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
DynParams::PerLayerFramebufferAttachmentBit(TextureTarget::TextureCubeMapArray);
}
}
m_dynamicParameters.SupportsFloat64VertexAttributes = m_vulkanCaps.SupportsShaderFloat64;
// Never, on any device, and no longer for the reason it used to be. It used to track
// shaderFloat64 because a `dvec3` input needed the Float64 capability to exist in the
// module at all; a 64-bit vertex FETCH was already impossible (VK_FORMAT_R64*_SFLOAT is
// optional and lavapipe reports zero bufferFeatures for all four), so the attribute
// arrived as its 32-bit word pair and PackDoubleVertexInputsPass bitcast it back.
//
// The shader half of that is gone: every 64-bit float is narrowed before any module
// reaches a backend (ShaderTranspiler::DemoteFloat64Pass), so there is no `double` input
// left to bitcast INTO, and feeding a UINT-formatted attribute to what is now a `float`
// input would be silent garbage. Reconstructing the value would mean decoding the
// IEEE-754 double bit pattern in the shader - software fp64, which is precisely what the
// demotion exists to avoid - and on Espryt it would additionally need the ES driver to
// fetch 2N uint components where the application declared N doubles, which a dvec3 or
// dvec4 cannot even express within one attribute location.
//
// So glVertexAttribLFormat / glVertexAttribLPointer are declined here exactly as they
// already were on Espryt and on every real mobile device (Adreno and Mali both report
// shaderFloat64 == VK_FALSE), and for the same visible reason. A `dvec3` INPUT still
// compiles and draws - it is a `vec3` after demotion - as long as the application feeds
// it with glVertexAttribPointer(GL_FLOAT) rather than 64-bit data.
m_dynamicParameters.SupportsFloat64VertexAttributes = false;
m_dynamicParameters.MaxShaderStorageBlockSize =
std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize);
if (m_vulkanCaps.SupportsShaderSubgroup) {
@@ -314,5 +314,44 @@ void main() {
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
}
TEST_F(DoublePrecisionScenario, TheFp64ExtensionIsNotAdvertised) {
if (!Ready()) return;
// The shader above compiled, linked and ran without the extension string, which is
// the point: an application does not need GL_ARB_gpu_shader_fp64 advertised to USE
// doubles here. What the string additionally promises is 64-bit precision, and that
// is the one thing the demotion cannot deliver - so it stays off unless
// MOBILEGL_ADVERTISE_FP64 asks for it, and an application that branches on the
// string keeps taking its float path.
GLint extensionCount = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
ASSERT_GT(extensionCount, 0);
bool advertised = false;
for (GLint i = 0; i < extensionCount; ++i) {
const char* name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(i)));
if (name != nullptr && std::string(name) == "GL_ARB_gpu_shader_fp64") advertised = true;
}
EXPECT_FALSE(advertised);
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
}
TEST_F(DoublePrecisionScenario, A64BitVertexFormatIsDeclinedOnEveryBackend) {
if (!Ready()) return;
// The demotion leaves no 64-bit shader input to feed, so there is nothing a 64-bit
// vertex FETCH could be fetched into - on either backend, and no longer only on the
// ones whose device lacks shaderFloat64. Declined loudly rather than accepted and
// drawn as garbage; the matching POST row says the same thing at startup.
GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
while (glGetError() != GL_NO_ERROR) {}
glVertexAttribLFormat(0, 3, GL_DOUBLE, 0);
EXPECT_EQ(glGetError(), static_cast<GLenum>(GL_INVALID_OPERATION));
glBindVertexArray(0);
glDeleteVertexArrays(1, &vao);
while (glGetError() != GL_NO_ERROR) {}
}
} // namespace
} // namespace MGITest
+52 -15
View File
@@ -46,6 +46,22 @@ namespace MobileGL::MG_Util::SelfTest {
RankMobileGLReported = 5,
};
// Both backends' fp64 rows end the same way, and the sentence they end with depends on
// a config flag rather than on anything either backend probes: the demotion is what
// makes doubles work, but GL_ARB_gpu_shader_fp64 promises the PRECISION the demotion
// cannot deliver, so the string is opt-in and the row has to say which way it went.
String AppendFp64AdvertisementNote(String detail) {
if (MG_Config::Features.AdvertiseFp64) {
return Move(detail) +
". GL_ARB_gpu_shader_fp64 IS advertised (MOBILEGL_ADVERTISE_FP64): an application "
"that checks the string will believe it has 64-bit precision, and it does not";
}
return Move(detail) +
". GL_ARB_gpu_shader_fp64 is not advertised, because the precision it promises is the "
"one thing the demotion cannot provide; set MOBILEGL_ADVERTISE_FP64=1 to advertise it "
"anyway";
}
struct ReportBuilder {
BackendPostReport report;
Bool fatalFailed = false;
@@ -484,14 +500,24 @@ namespace MobileGL::MG_Util::SelfTest {
break;
}
}
// Reported rather than probed: this one cannot come out any other way. OpenGL ES has no
// double-precision vertex format and ESSL has no fp64 type, so there is no driver and no
// extension that could make it work - the row exists so the loss is named at startup
// instead of discovered as an unexplained GL_INVALID_OPERATION at draw setup.
// Both rows are reported rather than probed: neither can come out any other way.
// ESSL has no 64-bit float type at all, so no driver and no extension could change
// either answer, and the rows exist so the two halves of the loss are named at
// startup instead of discovered as a shader that will not compile or an
// unexplained GL_INVALID_OPERATION at draw setup.
builder.Pass("fp64", AppendFp64AdvertisementNote(
"demoted to fp32 - ESSL has no 64-bit float type, so every double / "
"dvec / dmat in a shader is narrowed to 32 bits before transpilation "
"(DemoteFloat64Pass). Such shaders COMPILE AND RUN, at single "
"precision; a block containing a double is re-laid-out for the "
"narrowed members, so an application that hard-codes std140 offsets "
"computed for doubles must query them instead"));
builder.Warn("64-bit vertex attributes",
"not supported on any GLES driver (ES has no GL_DOUBLE vertex format and ESSL has "
"no fp64 type); glVertexAttribLFormat / glVertexArrayAttribLFormat report "
"GL_INVALID_OPERATION - use the Vulkan backend if the application needs them");
"not supported (ES has no GL_DOUBLE vertex format, and after the fp64 demotion "
"above there is no 64-bit shader input left to feed either); "
"glVertexAttribLFormat / glVertexArrayAttribLFormat report "
"GL_INVALID_OPERATION - feed the attribute with glVertexAttribPointer(GL_FLOAT), "
"which a demoted dvec input reads correctly");
if (glesFuncs.glPatchParameteri != nullptr) {
builder.Pass("Tessellation patch parameters",
"glPatchParameteri present (GL_PATCH_VERTICES reaches the driver)");
@@ -1843,14 +1869,25 @@ namespace MobileGL::MG_Util::SelfTest {
"unsupported; a GL_TEXTURE_CUBE_MAP_ARRAY texture gets no image at all, so sampling "
"one reads nothing and glFramebufferTextureLayer on one is declined");
}
if (features.shaderFloat64 == VK_TRUE) {
builder.Pass("shaderFloat64",
"GLSL double/dvec/dmat and 64-bit vertex attributes (glVertexAttribLFormat) supported");
} else {
builder.Warn("shaderFloat64",
"unsupported; any shader declaring a double fails to create a shader module, and "
"glVertexAttribLFormat reports GL_INVALID_OPERATION instead of feeding the attribute");
}
// Reported whichever way the device answers, because MobileGL no longer follows the
// device here: every 64-bit float is narrowed to 32 bits before any module reaches this
// backend (DemoteFloat64Pass), so the Float64 capability is never declared and a device
// that HAS the feature gains nothing from it. The device's own answer is still worth
// printing - it is the reason the demotion is unconditional.
builder.Pass("fp64", AppendFp64AdvertisementNote(
format("demoted to fp32 (device shaderFloat64 = {}) - every double / dvec / "
"dmat in a shader is narrowed to 32 bits before pipeline creation, so "
"such shaders BUILD AND RUN at single precision on every device "
"instead of failing to create a shader module on the ones without the "
"feature. A block containing a double is re-laid-out for the narrowed "
"members, so an application that hard-codes std140 offsets computed "
"for doubles must query them instead",
features.shaderFloat64 == VK_TRUE ? "supported" : "unsupported")));
builder.Warn("64-bit vertex attributes",
"not supported; there is no 64-bit shader input left to feed after the fp64 demotion "
"above, and no VK_FORMAT_R64*_SFLOAT vertex fetch to feed it with on most devices "
"anyway. glVertexAttribLFormat reports GL_INVALID_OPERATION - feed the attribute with "
"glVertexAttribPointer(GL_FLOAT), which a demoted dvec input reads correctly");
Bool shaderDrawParameters = false;
if (vkGetPhysicalDeviceFeatures2Fn != nullptr && properties.apiVersion >= VK_API_VERSION_1_1) {