[Fix] (ShaderTranspiler, Link, DirectGLES, DirectVulkan): demote tessellation/geometry gl_PointSize to an ordinary varying where the device cannot host the built-in - the value survives for gl_in reads and by-name capture, both backends' declines stay for shapes the pass refuses, and the verdict rides the L1 key

This commit is contained in:
2026-08-28 06:21:30 -04:00
parent 92dc41ebf9
commit d7f66722d1
21 changed files with 1320 additions and 6 deletions
@@ -7342,6 +7342,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_backendProgramUsable = false;
return;
}
if (stateProgramObject->PointSizeDemoted()) {
// THE ARMING SIGNAL, INFO on purpose and latched: the integration lane that
// pins MOBILEGL_POINT_SIZE_DEMOTION=1 asserts on exactly this line, because
// every rendering assertion stays green on a healthy driver whether the
// demotion ran or was silently disarmed. See PointSizeDemotionScenario.
MGLOG_I_ONCE("DirectGLES is building programs whose tessellation/geometry gl_PointSize was "
"demoted to an ordinary varying, because this driver cannot host the built-in "
"in those stages.");
}
MGLOG_D("Attaching %zu shaders to program %u", linkedStages.size(), m_backendProgramId);
for (const auto& ref : stateProgramObject->GetLinkedShaderSnapshot()) {
if (!ref.shader) continue;
@@ -8012,6 +8021,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
// for; it has the variable that replaced it. Everything else - including a
// member of a block that was left alone - keeps the application's spelling.
// Storage first, pointers after: xfbNames holds pointers into these strings.
//
// Same rule for a demoted gl_PointSize: the capture stage's ESSL no longer
// spells the built-in at all - the value lives in the carrier the demotion
// named - so the driver-side request has to follow it there. Only when the
// capture stage IS a demoted one (geometry, else evaluation): a program whose
// capture stage is the vertex shader keeps the built-in and its spelling,
// whatever happened to a control stage behind it.
Bool captureStageDemoted = false;
if (stateProgramObject->PointSizeDemoted()) {
for (const ShaderStage linkedStage : linkedStages) {
if (linkedStage == ShaderStage::TessEval || linkedStage == ShaderStage::Geometry) {
captureStageDemoted = true;
break;
}
}
}
Vector<String> rewrittenXfbNames(xfbVaryings.size());
for (SizeT nameIndex = 0; nameIndex < xfbVaryings.size(); ++nameIndex) {
String flatName;
@@ -8019,6 +8044,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::ShaderTranspiler::ShaderCompiler::RewriteXfbCaptureNameForFlattenedBlock(
xfbVaryings[nameIndex].name, flattenedXfbBlockNames, flatName)) {
rewrittenXfbNames[nameIndex] = std::move(flatName);
} else if (captureStageDemoted && xfbVaryings[nameIndex].name == "gl_PointSize") {
rewrittenXfbNames[nameIndex] =
MG_Util::ShaderTranspiler::ShaderCompiler::POINT_SIZE_CAPTURE_CARRIER_NAME;
} else {
rewrittenXfbNames[nameIndex] = xfbVaryings[nameIndex].name;
}