mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[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:
@@ -495,6 +495,24 @@ namespace MobileGL {
|
||||
// halves (PackDoubleVertexInputsPass and VertexInputStateFactory::ToVkVertexFormat)
|
||||
// still see one consistent world.
|
||||
Bool SupportsFloat64VertexAttributes = false;
|
||||
// Whether a TESSELLATION stage of this backend may access gl_PointSize - i.e.
|
||||
// whether a module declaring OpCapability TessellationPointSize can reach the
|
||||
// driver at all. DirectVulkan sets both this and the geometry twin from the one
|
||||
// shaderTessellationAndGeometryPointSize feature; DirectGLES sets them
|
||||
// independently from the EXT/OES_tessellation_point_size /
|
||||
// geometry_point_size extension pairs (PointSizeTier), which really do come
|
||||
// separately. When absent, ProgramSpirvTask demotes the built-in to an ordinary
|
||||
// varying program-wide (ShaderCompiler::
|
||||
// DemoteTessellationGeometryPointSizeForProgram); MOBILEGL_POINT_SIZE_DEMOTION
|
||||
// overrides the detection in either direction at backend init.
|
||||
//
|
||||
// Defaults TRUE, deliberately against the house "assume absent" rule: false
|
||||
// ARMS a rewrite, so the conservative no-backend answer (standalone compiles,
|
||||
// unit tests) is the one that leaves modules untouched. A backend that never
|
||||
// sets it gets standard modules and, at worst, the old honest declines.
|
||||
Bool SupportsTessellationPointSize = true;
|
||||
// The geometry-stage twin (OpCapability GeometryPointSize).
|
||||
Bool SupportsGeometryPointSize = true;
|
||||
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
||||
Uint32 SubgroupSize = 0;
|
||||
Uint32 SubgroupSupportedStages = 0;
|
||||
|
||||
@@ -1479,6 +1479,36 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// Follows the line above, and must: OpenGL ES has no double-precision vertex format and no
|
||||
// fp64 type to consume one with, so a 64-bit vertex attribute has nowhere to land here.
|
||||
m_dynamicParameters.SupportsFloat64VertexAttributes = false;
|
||||
// Whether a tessellation / geometry stage's ESSL may name gl_PointSize at all: the two
|
||||
// extension pairs the loader probed, independently, because they really do come
|
||||
// separately. False arms the shared phase-B demotion
|
||||
// (ShaderCompiler::DemoteTessellationGeometryPointSizeForProgram), whose ESSL then
|
||||
// never names the built-in in those stages and needs no extension.
|
||||
// MOBILEGL_POINT_SIZE_DEMOTION=1 pretends both are absent so the demotion can be
|
||||
// exercised on a healthy driver (the pinned integration lane); =0 restores the
|
||||
// detected answer's declines.
|
||||
m_dynamicParameters.SupportsTessellationPointSize =
|
||||
m_GLESCapabilities.TessellationPointSizeSupport !=
|
||||
MG_External::GLESCapabilities::PointSizeTier::None;
|
||||
m_dynamicParameters.SupportsGeometryPointSize =
|
||||
m_GLESCapabilities.GeometryPointSizeSupport !=
|
||||
MG_External::GLESCapabilities::PointSizeTier::None;
|
||||
switch (MG_Config::Features.PointSizeDemotion) {
|
||||
case MG_Config::QuirkOverride::ForceOn:
|
||||
MGLOG_I("DirectGLES: MOBILEGL_POINT_SIZE_DEMOTION=1 - treating tessellation/geometry "
|
||||
"gl_PointSize as unhosted so the demotion runs on this driver");
|
||||
m_dynamicParameters.SupportsTessellationPointSize = false;
|
||||
m_dynamicParameters.SupportsGeometryPointSize = false;
|
||||
break;
|
||||
case MG_Config::QuirkOverride::ForceOff:
|
||||
MGLOG_I("DirectGLES: MOBILEGL_POINT_SIZE_DEMOTION=0 - keeping the built-in and the "
|
||||
"plain declines regardless of the driver's extensions");
|
||||
m_dynamicParameters.SupportsTessellationPointSize = true;
|
||||
m_dynamicParameters.SupportsGeometryPointSize = true;
|
||||
break;
|
||||
case MG_Config::QuirkOverride::Auto:
|
||||
break;
|
||||
}
|
||||
m_dynamicParameters.MaxDrawBuffers = m_GLESCapabilities.MaxDrawBuffers;
|
||||
m_dynamicParameters.MaxColorAttachments = m_GLESCapabilities.MaxColorAttachments;
|
||||
m_dynamicParameters.MaxClipDistances = m_GLESCapabilities.MaxClipDistances;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1081,6 +1081,31 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// report VK_FALSE, so on every real mobile device this is false and the demotion runs
|
||||
// exactly as it always has.
|
||||
m_dynamicParameters.SupportsShaderFloat64 = m_vulkanCaps.SupportsShaderFloat64;
|
||||
// shaderTessellationAndGeometryPointSize, both stage families from the one feature.
|
||||
// False arms the shared phase-B point-size demotion, whose modules then carry no
|
||||
// TessellationPointSize/GeometryPointSize capability and build without the feature.
|
||||
// MOBILEGL_POINT_SIZE_DEMOTION=1 pretends it is absent so the demotion can be
|
||||
// exercised on a healthy driver (lavapipe advertises the feature); =0 restores the
|
||||
// detected answer's declines.
|
||||
{
|
||||
Bool supportsStagePointSize = m_vulkanCaps.SupportsTessellationAndGeometryPointSize;
|
||||
switch (MG_Config::Features.PointSizeDemotion) {
|
||||
case MG_Config::QuirkOverride::ForceOn:
|
||||
MGLOG_I("DirectVulkan: MOBILEGL_POINT_SIZE_DEMOTION=1 - treating tessellation/geometry "
|
||||
"gl_PointSize as unhosted so the demotion runs on this driver");
|
||||
supportsStagePointSize = false;
|
||||
break;
|
||||
case MG_Config::QuirkOverride::ForceOff:
|
||||
MGLOG_I("DirectVulkan: MOBILEGL_POINT_SIZE_DEMOTION=0 - keeping the built-in and the "
|
||||
"plain declines regardless of the device feature");
|
||||
supportsStagePointSize = true;
|
||||
break;
|
||||
case MG_Config::QuirkOverride::Auto:
|
||||
break;
|
||||
}
|
||||
m_dynamicParameters.SupportsTessellationPointSize = supportsStagePointSize;
|
||||
m_dynamicParameters.SupportsGeometryPointSize = supportsStagePointSize;
|
||||
}
|
||||
// Never, on any device, and DELIBERATELY NOT COUPLED to the line above even though it
|
||||
// once tracked the same feature. It used to, because a `dvec` input needed Float64 to
|
||||
// exist in the module at all; a 64-bit vertex FETCH was already impossible
|
||||
|
||||
@@ -1428,6 +1428,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
continue;
|
||||
}
|
||||
if (varying.name == "gl_PointSize") {
|
||||
// A demoted module (ShaderCompiler::
|
||||
// DemoteTessellationGeometryPointSizeForProgram) no longer ACCESSES the
|
||||
// built-in member - the value lives in the carrier variable the demotion
|
||||
// named - so the capture binds to the carrier directly. The mirror below
|
||||
// must not run for it: reading the now-unwritten member would capture
|
||||
// garbage, and the read itself is the capability access the demotion
|
||||
// exists to remove. Detected off the module's own debug names, so a
|
||||
// composite built from another program's stage answers for the module it
|
||||
// actually contains.
|
||||
const auto carrierIt = idsByName.find(
|
||||
MG_Util::ShaderTranspiler::ShaderCompiler::POINT_SIZE_CAPTURE_CARRIER_NAME);
|
||||
if (carrierIt != idsByName.end()) {
|
||||
decorateForXfb(carrierIt->second, varying.bufferIndex, varying.offsetBytes);
|
||||
modified = true;
|
||||
continue;
|
||||
}
|
||||
needsPointSizeMirror = true;
|
||||
pointSizeBufferIndex = varying.bufferIndex;
|
||||
pointSizeOffset = varying.offsetBytes;
|
||||
@@ -3454,6 +3470,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// `spirv` and `moduleSpirvs` for any program attached to after it linked.
|
||||
const Vector<ShaderStage> stages = program.GetLinkedShaderStages();
|
||||
auto& spirv = program.GetGeneratedSpirv();
|
||||
if (program.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 above it stays green on a healthy driver whether the
|
||||
// demotion ran or was silently disarmed. See PointSizeDemotionScenario.
|
||||
MGLOG_I_ONCE("DirectVulkan is building programs whose tessellation/geometry gl_PointSize was "
|
||||
"demoted to an ordinary varying, because this device cannot host the built-in "
|
||||
"in those stages.");
|
||||
}
|
||||
Vector<Vector<Uint>> moduleSpirvs(spirv.size());
|
||||
const Bool enableSpirvValidation = program.GetSpirvValidationEnabled();
|
||||
// Unconditional now: the two ValidateTransformedSpirv calls below run in every build,
|
||||
|
||||
Reference in New Issue
Block a user