Files
MobileGL/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.h
T
BZLZHH c93e5fa409 [Refactor] (MG_State, MG_Util): join-by-construction link/compile artifacts (P1 stage 2)
Still fully synchronous - EnsureLinkJoined()/EnsureCompileJoined() are empty
inline no-ops (verified to fold away at every one of the ~1200 call sites;
this project builds without LTO) - but every read of link- or compile-produced
state now goes through a private accessor the compiler enforces, so when
stage 4 moves the bodies onto pool workers, 'which reads must join' is a
type-system fact instead of a 400-line audit.

- ProgramObject: the 31 fields ResetLinkArtifacts clears plus the 5 link
  outputs it forgot (infoLog, linkedFragData{Location,Index}, the geometry
  strip-capture pair) move into a nested LinkArtifacts behind Artifacts().
  ResetLinkArtifacts is now a worker-safe pure clear; the link-observable
  version bumps (backendState/link/uboContent) move to a GL-thread-only
  BumpLinkObservableVersions() called once from Link()'s prologue and from
  glProgramBinary's mandated failure - the link body never writes them, so
  a stage-4 worker cannot lose an invalidation against the draw path.
- ShaderObject: compile artifacts (TShader, preprocessed source, side-channel
  maps, status/log, consume-once flag) behind Compiled(); the P0b layer-1
  memo trio deliberately stays outside as the future non-joining
  COMPLETION_STATUS_KHR fast path.
- CompileEnv (new): a GL-thread snapshot of everything the compile pipeline
  used to read live from the backend mid-parse - compute limits (the
  GetIntegeri_v reach-back is gone from the worker path), advertised
  extensions, device quirks, TBuiltInResource inputs. Captured lazily per
  backend activation; the consume-once re-parse now runs against the same
  env as the original parse.
- The GL-thread prologue / worker-body boundary is marked in Link() where
  the stage sort ends; everything below is a pure function of the snapshot.

Public getter signatures unchanged - MG_Impl and both backends compile
untouched. Unit 476/476, Program suites 117/117, DirectGLES retrace 38/39 on
llvmpipe (the one failure is the known pre-existing non-CI iterationrp case;
the NVIDIA userspace driver was updated out from under the running kernel
module mid-session, so GLX there is down until a reboot).
2026-08-08 07:12:37 -04:00

82 lines
5.1 KiB
C++

// MobileGL - MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#pragma once
#include <Includes.h>
#include <MG_State/GLState/ProgramState/ShaderObject.h>
#include <MG_Util/ShaderTranspiler/CompileEnv.h>
namespace MobileGL {
enum class ShaderProfile {
Core,
Compatibility,
ES,
};
namespace MG_Util {
namespace ShaderTranspiler {
// The whole source-rewriting pipeline. `env` is the compile-time snapshot of
// everything outside (stage, source) this reads - advertised extensions and the
// device-quirk inputs - so the transformation is a pure function of its three
// arguments and can run on a worker thread.
void PreprocessShaderSource(ShaderStage stage, String& source, const CompileEnv& env);
// Convenience overload that resolves the current context's env itself. GL thread
// only, and deliberately not used by the compile pipeline: it exists for the unit
// tests and diagnostics that drive the preprocessor standalone.
void PreprocessShaderSource(ShaderStage stage, String& source);
// Some desktop-captured compute shaders build a workgroup-wide linear prefix scan
// from subgroupInclusiveAdd plus a shared array of subgroup totals. Qualcomm's
// Vulkan driver miscompiles that exact float InclusiveScan path for native subgroups
// wider than the capture's 32 lanes. For the narrowly recognized, uniform-control-
// flow template, replace the subgroup-local scan with a shared-memory, strict
// left-fold over virtual 32-lane segments. Returns true only when the complete safe
// template was recognized and rewritten. PreprocessShaderSource reaches this through
// its device-quirk registry: by default only on detected Qualcomm Vulkan devices,
// overridable either way with MOBILEGL_QUIRK_SUBGROUP_PREFIX_SCAN=1/0. The explicit
// entry point exists for deterministic tests.
Bool RewriteLinearSubgroupPrefixScanForVulkan(ShaderStage stage, Uint32 nativeSubgroupSize, String& source);
// Rewrites a "#version 330 core" directive that PreprocessShaderSource normalized down
// from a legacy desktop version back up to "#version 460 core". Returns false (leaving
// the source untouched) for anything else: ES, compatibility, or an already-modern
// declaration. Exists so a shader that only parses under the laxer 460 rules - e.g. it
// uses 420-era syntax without the matching #extension line, which real drivers tend to
// accept - can be retried instead of failing to compile.
Bool RetargetLegacyVersionDirectiveTo460(String& source);
// GLSL reserves a few names glslang happily accepts as identifiers ("packed",
// "row_major" outside a layout(...) list, the image*Shadow family). Returns the
// compile-error text for the first violation, or nullopt for a clean source.
std::optional<String> FindReservedIdentifierViolation(const String& source);
// Explicit layout(location = N) qualifiers on default-block uniform declarations,
// keyed by declared name (no "[0]" suffix). Multi-declarator statements assign
// consecutive locations, advancing by the array element count.
//
// Exists because the single link-compatible parse runs under relaxed Vulkan rules,
// where glslang's vkRelaxedRemapUniformVariable moves plain uniforms into
// MGL_GLOBAL_UBO and DISCARDS their location qualifiers ("ignoring layout qualifier
// for uniform location"); opaque uniforms keep theirs. This lexical side-channel
// restores the discarded locations to the GL location assigner
// (ProgramObject::DoReflection). It scans preprocessor-visible text, so a
// declaration inside an inactive #if branch is still recorded - harmless unless a
// pack declares the same uniform with different explicit locations in alternative
// branches (none observed; explicit uniform locations have zero incidence in the
// shader-pack corpus, this is an ARB_explicit_uniform_location conformance surface).
UnorderedMap<String, Int> ExtractExplicitUniformLocations(const String& source);
// Explicit layout(binding = N) on sampler/image uniforms, i.e. their initial
// texture/image units. The Vulkan-client relaxed parse strips these before
// mapIO can capture them, so they are recovered lexically (same narrow
// grammar discipline as ExtractExplicitUniformLocations).
UnorderedMap<String, Uint> ExtractExplicitOpaqueBindings(const String& source);
} // namespace ShaderTranspiler
} // namespace MG_Util
} // namespace MobileGL