Commit Graph
277 Commits
Author SHA1 Message Date
swung0x48 b8dc4a6004 [Fix] (MG_Util/Texture): expand channels and convert component types on texture unpack to the internal shadow layout 2026-07-16 04:39:57 -04:00
swung0x48 35626da5c4 [Fix] (MG_Util/Texture): infer RGBA8 for packed RGBA uploads
(cherry picked from commit 1146188ed4)
2026-07-16 03:41:29 -04:00
swung0x48 7514587b5a [Fix] (MG_Impl/GLImpl, MG_State): fallback UBO backing for optimizer-eliminated uniforms (null-MapUBO SIGSEGV in KHR-GL33 do_while loops) + per-element locations/offsets for array uniforms incl. nested struct arrays (size assert in KHR-GL33 struct.uniform); demote uniform write assert to log-and-clamp 2026-07-16 03:31:32 -04:00
swung0x48 3a40778c4b [Fix] (MG_Util/ShaderTranspiler): stop stripping precision qualifiers - the strip corrupted "precision highp float;" into invalid syntax; glslang accepts and ignores them natively in 460 core (unblocks ~2000 GL CTS cases per backend) 2026-07-16 01:50:33 -04:00
swung0x48 e8d9a913d8 [Fix] (MG_Util/SelfTest): preserve POST checks before format probing 2026-07-16 00:10:12 -04:00
swung0x48 4453f1910d [Feat] (MG_Backend, android-plugin): show format capability tables in POST 2026-07-16 00:10:11 -04:00
swung0x48 b6d311f20b [Fix] (Retrace): bundle and select signed ANGLE variants 2026-07-15 06:48:10 +08:00
swung0x48 533219ede7 [Fix] (Retrace): load ANGLE through LD_LIBRARY_PATH 2026-07-13 21:20:02 -04:00
swung0x48 b1f55026af [Chore] (MobileGL/ConfigLoader): rename/clean up more env var clutter 2026-07-13 20:45:40 -04:00
swung0x48 0f99d93300 [Feat] (MobileGL): full dual-source blending across state, transpiler, and both backends
Wire GL_SRC1_* dual-source blend factors (glBlendFunc) end to end with the
glBindFragDataLocationIndexed color index, so a fragment shader can drive both
dual-source blend inputs.

State + converters:
  - RenderState BlendFactor gains Src1Color/OneMinusSrc1Color/Src1Alpha/
    OneMinusSrc1Alpha; GLToMG/MGToGL/MGToVk/MGToStr converters map them to
    GL_SRC1_*, VK_BLEND_FACTOR_SRC1_*, and readable names.

Transpiler layout(index = N):
  - ProgramAttrib carries explicitFragmentOutIndices; ProgramObject threads
    m_explicitFragDataIndex into it at both link sites.
  - TMglGlslIoResolver applies the color index as TQualifier.layoutIndex on the
    fragment output, emitting layout(index = 1) via the glslang Index decoration
    -> SPIRV-Cross path. Only the non-zero (dual-source) index is emitted: index 0
    is the GL default and an explicit "index = 0" would demand
    GL_EXT_blend_func_extended on GLES for ordinary single-source outputs.

Feature detection, POST, and hard-fail at use time (no silent fallback):
  - Vulkan: dualSrcBlend is detected at device creation and cached; a draw whose
    enabled blend state uses a SRC1 factor without the feature throws at pipeline
    build with the reason and a pointer to the POST row.
  - GLES: GL_EXT_blend_func_extended detected at load into
    GLESCapabilities.SupportsDualSourceBlend; a draw enabling blend with a SRC1
    factor without it throws in the blend-state sync with the same guidance.
  - DriverPost adds a dual-source-blend row for both backends (Pass/Warn).

Tests:
  - ProgramTest.CompileAndLinkWithExplicitFragmentOut now asserts the transpiled
    fragment shader carries layout(location = 0, index = 1) after a re-link with
    glBindFragDataLocationIndexed(index 1), and still omits any index qualifier
    for the plain index-0 output.
2026-07-11 01:32:07 -04:00
swung0x48 e9fa99e16b [Feat] (MG_Backend): wire primitive restart into both backends; detect dualSrcBlend
Make GL_PRIMITIVE_RESTART[_FIXED_INDEX] actually take effect at draw time,
following the detect-at-init / POST / fallback-or-hard-fail discipline.

DirectVulkan:
- Thread primitiveRestartEnable through the pipeline (payload + hash +
  input-assembly), set from the GL_PRIMITIVE_RESTART / _FIXED_INDEX caps.
- Detect and enable primitiveTopologyListRestart
  (VK_EXT_primitive_topology_list_restart) at device creation; cache it.
  Strip/fan restart needs no feature; a *list* topology with restart and
  no feature hard-fails at the draw with the reason.
- Vulkan only restarts on the fixed all-ones index value, so an arbitrary
  GL_PRIMITIVE_RESTART index that is not that value hard-fails in
  UploadAndBindIndexBuffer (where the index type is known).
- Also detect+enable and cache the dualSrcBlend base feature (groundwork
  for GL_SRC1_* dual-source blending).

DirectGLES:
- Sync GL_PRIMITIVE_RESTART_FIXED_INDEX from either restart cap (GLES core
  has only the fixed-index form); an arbitrary non-fixed index hard-fails
  in the indexed draw paths with the reason.

POST: dualSrcBlend and primitiveTopologyListRestart capability rows (Pass
when supported, Warn with the fallback/hard-fail consequence otherwise).

Library builds clean; SanityTest 31/31. (The actual restart rendering and
the hard-fail paths need a real GPU and are not runtime-testable here.)
2026-07-11 01:11:23 -04:00
swung0x48 4dd2b2216c [Feat] (MG_Impl, MG_State, MG_Backend, MG_Util): packed 2_10_10_10 and GL_BGRA vertex array formats
glVertexAttribPointer now accepts the GL 3.3 Core packed types
GL_INT_/GL_UNSIGNED_INT_2_10_10_10_REV and the GL_BGRA size, clearing the
two long-standing "// TODO: implement GL_BGRA support" markers. Adds the
format end to end across the frontend, VAO state, and both backends.

- DataType: add Int2101010Rev / Uint2101010Rev with GLToMG / MGToGL /
  MGToStr converter cases.
- Validation (ValidateVertexAttribFormat): the full glVertexAttribPointer /
  glVertexAttribIPointer error table -- size is 1..4 or GL_BGRA (else
  INVALID_VALUE, which takes precedence); a packed type requires size 4 or
  GL_BGRA (else INVALID_OPERATION); GL_BGRA requires GL_UNSIGNED_BYTE or a
  packed type AND normalized == GL_TRUE (else INVALID_OPERATION); the
  integer path rejects packed types (INVALID_ENUM) and GL_BGRA size
  (INVALID_VALUE).
- VAO: store GL_BGRA as size 4 plus a new IsBgra flag (reset on the
  binding-format path).
- DirectVulkan: map the packed/BGRA formats to
  VK_FORMAT_A2B10G10R10_* (normal) and VK_FORMAT_A2R10G10B10_* /
  VK_FORMAT_B8G8R8A8_UNORM (BGRA reversed), fold IsBgra into the pipeline
  hash, and size packed/BGRA elements as one 4-byte word via
  GetAttributeByteSize. (Vulkan *_SNORM decodes with the GL 4.2 symmetric
  rule, a documented deviation from the 3.3 signed formula.)
- DirectGLES: round-trip the packed enum through the loader, pass GL_BGRA
  as the driver size argument, and size client uploads with the packed
  4-byte word.

Tests: 4 VertexArrayTest cases covering packed/BGRA storage and the full
float/integer error table; the packed-size hard-fail is mutation-verified.
VertexArrayTest 42/42, SanityTest 30/30, library builds clean.
2026-07-10 23:25:25 -04:00
swung0x48 3ff8cafac6 [Feat] (MG_Util/SelfTest): POST rows for polygon-mode and indexed-color-mask capabilities
Surface the device features that glPolygonMode and glColorMaski depend on,
so a missing capability (and the resulting FILL / draw-buffer-0 fallback)
is visible in the driver POST instead of silently degrading.

- DirectVulkan checklist: fillModeNonSolid (GL_LINE/GL_POINT rasterization)
  and independentBlend (per-draw-buffer color masks) rows, read from the
  physical device features already queried by the probe.
- DirectGLES checklist: "Polygon mode" (GL_NV/ANGLE_polygon_mode) and
  "Indexed color mask" (ES 3.2 core or draw_buffers_indexed) rows, read
  from the cached GLESCapabilities flags.

Each row passes when supported and warns (not fails) when absent, since
the fallback still renders correctly. Builds clean; SanityTest sweep green
(30/30).
2026-07-10 22:31:20 -04:00
swung0x48 041de6cba3 [Feat] (MG_Backend/DirectGLES, MG_Util/Loader): wire glPolygonMode and glColorMaski into GLES sync
Neither entry point exists in unextended OpenGL ES core, so both are
gated on optional extensions detected and cached at init, with a runtime
fallback when absent.

Loader:
- Add glPolygonModeNV/glPolygonModeANGLE and glColorMaskiEXT/glColorMaskiOES
  to the GLES function table, loaded via a new INIT_GLES_FUNC_OPTIONAL
  macro that does not log an error when the driver lacks them.
- Cache GLESCapabilities.SupportsPolygonMode and SupportsIndexedColorMask
  from whether the entry points loaded (glColorMaski is GLES 3.2 core with
  no extension string, so pointer presence is the reliable signal).

Sync (SyncRenderState):
- Color mask: uniform masks keep using the non-indexed glColorMask (works
  everywhere); divergent per-draw-buffer masks use glColorMaski (core /
  EXT / OES, whichever loaded) when SupportsIndexedColorMask, else fall
  back to broadcasting draw buffer 0. Mirrors the existing indexed-blend
  block's all-same-vs-per-buffer structure.
- Polygon mode: new sync block calls glPolygonModeNV/ANGLE(GL_FRONT_AND_BACK,
  mode) when SupportsPolygonMode; without the extension the mode stays FILL
  and non-FILL requests are dropped.

Library builds clean; full SanityTest sweep green (30/30).
2026-07-10 22:27:21 -04:00
swung0x48 aa5c33a42d [Feat] (MG_Backend/DirectVulkan): wire glPolygonMode and glColorMaski into pipeline creation
Consume the polygon mode and per-draw-buffer color write masks that the
frontend already tracks, with runtime fallback for the device features
they require.

glPolygonMode:
- Add ConvertPolygonModeToVkEnum (GL_FILL/LINE/POINT -> VkPolygonMode).
- Thread a polygonMode field through PipelineCreatePayload, fold it into
  the pipeline cache hash (distinct modes need distinct pipelines), and
  apply it in PipelineFactory instead of the hardcoded VK_POLYGON_MODE_FILL.
- LINE/POINT require the fillModeNonSolid device feature: detect and
  enable it at device creation, cache m_fillModeNonSolidFeatureEnabled,
  and fall back to FILL at pipeline-build time when it is absent.

glColorMaski:
- The per-attachment color-blend loop now reads GetColorMaskIndexed(i)
  instead of the broadcast GetColorMask(), so each draw buffer gets its
  own write mask (already covered by the pipeline hash).
- Divergent per-attachment masks require independentBlend: cache
  m_independentBlendFeatureEnabled (was enabled but never recorded) and
  fall back to draw buffer 0's mask for every attachment when it is absent.

The internal depth-mipmap utility pipeline keeps VK_POLYGON_MODE_FILL (not
GL-driven). Library builds clean; full SanityTest sweep green (30/30).
2026-07-10 22:16:47 -04:00
swung0x48 d40f753983 [Fix] (MG_State, MG_Impl, MG_Backend): conformant current generic vertex attribute values
GL 3.3 Core: a shader input whose generic attribute array is disabled reads that
attribute's current value (per-context state, default (0,0,0,1)). Four defects made
that path non-conformant, three of them silently.

* Out-of-bounds current-value reads. m_currentVertexAttributes held 16 entries while
  the DirectVulkan draw path walked shader input locations 0..31 and GL_MAX_VERTEX_ATTRIBS
  was advertised straight from the device (commonly 32). The only guard was MOBILEGL_ASSERT,
  which expands to nothing outside debug builds. Grow the storage capacity to 32, advertise
  min(device limit, capacity), validate against that dynamic limit, and give the accessors
  real runtime bounds checks. Replace the literal 32 loops with the constant, and pin
  MAX_VERTEX_ATTRIBS to the Uint32 mask width and to vertexInputTypes' bound with
  static_asserts so the two can no longer drift apart -- that drift was the bug.

* DirectGLES never fed current values to the driver. Values were stored in MG_State only,
  so a disabled attribute always rendered as the ES driver's own untouched (0,0,0,1) while
  DirectVulkan rendered it correctly: identical GL code, different pixels per backend.
  Add SyncCurrentVertexAttributeValues() to the draw prologue, and hoist the
  glType -> (base type, component count) dispatch into MG_State::GLState so both backends
  resolve the semantics from one place instead of it living inside VulkanRenderer.

* Enabled arrays the backend could not map were silently demoted to the current value.
  ToVkVertexFormat had no DataType::Float16 case, so a GL_HALF_FLOAT array fell to
  VK_FORMAT_UNDEFINED, dropped out of the vertex input state, and became indistinguishable
  from a disabled array: the geometry rendered a constant colour with GL_NO_ERROR. Add the
  Float16 mapping, track an unsupportedAttribMask, and hard-fail the draw before pipeline
  creation so no synthetic attribute is baked into a cached VkPipeline.

* glGetVertexAttrib{fv,iv,Iiv,Iuiv}(GL_CURRENT_VERTEX_ATTRIB) returned before any index
  validation, reading past the array instead of raising GL_INVALID_VALUE.

Also resolve ProgramObject::DoReflection's "TODO: get from backend" 16-location clamp,
which capped the new DirectGLES sync at locations 0..15; report GL_MAX_VERTEX_ATTRIBS
through the same helper the validators use, so the clamp cannot be bypassed; and bound
vertex binding indices by the same dynamic limit, since the default attribute -> binding
mapping is the identity.

Add a "Vertex attributes" driver POST row to both backends: FAIL below the GL 3.3 Core
minimum of 16, WARN above MobileGL's storage capacity (clamped, extra attributes unusable),
PASS in between -- making the driver/host mismatch that caused the out-of-bounds read
visible instead of silently swallowed.

Covered by 7 new regression tests (each verified to fail against the previous behaviour).
2026-07-10 11:23:16 -04:00
swung0x48andClaude Fable 5 7b00255b11 [Feat] (MG_Util/SelfTest): grouped POST rows, merged probes, MobileGL-reported strings
Rows in each backend section now sort FAIL -> WARN -> PASS -> INFO
(stable within groups), with identity strings always last: the device
strings renamed to 'Backend driver reported GL_*' and a new bottom
group 'MobileGL reported GL_VENDOR/GL_VERSION/GL_RENDERER/GL_EXTENSIONS'
showing exactly what MobileGL advertises to applications on that
backend, assembled from the same sources as GL_Getter and the backend
objects (extension-list construction extracted into shared helpers so
POST cannot drift from the real advertisement).

Rows probing the same subject are merged into single verdicts whose
details keep every sub-fact and causal chain: the six EGL setup steps
become one 'ES3 context' row, extension presence + functional probe
become one 'Timer queries' row per backend (including the
MOBILEGL_DISABLE_TIMERQUERY override explanation), and the Vulkan
loader/instance, surface-extension pair, and physical-device/queue/API
chains each collapse into one row.

Capability rows previously dumped as INFO now carry verdicts: index
type uint8 (WARN when absent - uint8 index buffers have no conversion
fallback), VK_KHR_draw_indirect_count (WARN when absent - count draws
degrade to CPU readback loops); buffer_storage/base_instance stay
honest INFO when absent since no MobileGL path degrades.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 03:28:50 +00:00
swung0x48andClaude Fable 5 f0dd5d667b [Feat] (MG_Util/SelfTest): timer-query rows and functional probes in POST
GLES section reports GL_EXT_disjoint_timer_query and, when present,
runs a real TIME_ELAPSED span (paced availability polling matching the
runtime path) and reports the observed nanoseconds. Vulkan section
reports timestampValidBits/timestampPeriod and runs a full functional
probe - logical device, command buffer, two vkCmdWriteTimestamp into a
fresh query pool, submit, fenced wait, read-back - with hung-GPU-safe
teardown (a timed-out fence skips vkDeviceWaitIdle and leaks
deliberately rather than hanging the POST). Both sections note when
MOBILEGL_DISABLE_TIMERQUERY suppresses the feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 01:08:59 +00:00
swung0x48andClaude Fable 5 7eb3994b02 [Feat] (MG_Impl, MG_Backend): GL_ARB_timer_query on both backends
Implements GL timer queries end to end: a frontend query registry
(modeled on the sync module - mutex-guarded objects wrapping opaque
backend handles behind optional function pointers) serving
glGenQueries/glBeginQuery/glEndQuery(GL_TIME_ELAPSED)/glQueryCounter
(GL_TIMESTAMP)/glGetQueryObject*/glGetQueryiv with GL 3.3 error
semantics and a graceful zero-result fallback when a backend cannot
time.

DirectGLES backs spans with GL_EXT_disjoint_timer_query (context-
generation-stamped handles, bounded result waits). DirectVulkan gets a
VkTimerQueryManager: per-frame-in-flight timestamp query pools reset at
command-buffer begin (outside render passes), records harvested by
frame serial before their pool recycles, elapsed = masked tick delta x
timestampPeriod; handles are stamped with a renderer generation that
also now guards fence syncs across renderer recreation. GL_QUERY_
COUNTER_BITS reports 0 unless the live backend can actually time
(dynamic IsTimerQuerySupported hook), and a failed blocking read keeps
the handle alive so the real value stays reachable once the frame
submits.

GL_ARB_timer_query is advertised only when the device supports timing
and MOBILEGL_DISABLE_TIMERQUERY is unset - LWJGL keys Minecraft's F3
'GPU: x%' line off exactly that extension string; verified on device
(Adreno 830) on both backends.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 01:08:38 +00:00
swung0x48andClaude Fable 5 7d31a6fcd7 [Refactor] (MG_Config): centralize env-var and driver-feature reads
MG_Config::FeaturesTable snapshots every MOBILEGL_* toggle once in
ConfigLoader::Init with a single truthy rule (non-empty, not '0', not
'false' case-insensitively), replacing 13 scattered std::getenv sites
that used four different parsing conventions. Renderer-derived bits
(IsAngleRenderer/IsAngleLlvmpipeRenderer/AvoidSamplerMipmapMinFilter)
move into GLESCapabilities, set once in FillInGLESCapabilities, so hot
paths (glMemoryBarrier ANGLE flush, sampler min-filter sync) stop doing
per-call string scans. MOBILEGL_PRESENT_DUMP_CALL/_CURRENT_CALL stay
live getenv (the retrace harness mutates them at runtime) and
MOBILEGL_LOG_FILE_PATH stays in Log.cpp (log init precedes config
init); both are documented in Config.h. Known semantic unification:
MOBILEGL_DISABLE_SUBGROUP previously required exactly 'true' and
MOBILEGL_PRESENT_STATS exactly '1'; both now follow the shared rule
(CI's 0/1 values parse identically). Also bumps CoreVersion to 26.07.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 01:08:06 +00:00
swung0x48andClaude Fable 5 a394fe1af3 [Test] (MG_Test): cover the indirect gl_InstanceID probe and shader rewrite
BackendLoaderTest drives ProbeIndirectInstanceIdIncludesBaseInstance
(now externally linked) against a fake GLES function table: conforming
and ANGLE-style leaking drivers, the no-vertex-SSBO skip, draw-error
inconclusiveness, object cleanup, and the FillInGLESCapabilities wiring
end-to-end. SanityTest gains PromoteDrawParameterGlobalsToUniforms
cases pinning the mg_ZeroBasedInstanceID rewrite and the
last-SSBO-binding computation against a non-default binding count,
with RAII capability restoration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:44:25 +00:00
swung0x48andClaude Fable 5 d16b7ccd6a [Feat] (MG_Util/SelfTest, android-plugin): driver POST self-test screen
Opening a MobileGL plugin APK now shows a POST screen that probes the
device's GLES and Vulkan drivers independently against MobileGL's
expectations - a device may satisfy only one backend - and reports a
per-backend verdict (OK / DEGRADED / UNSUPPORTED) with per-check rows.

The GLES probe builds its own ES3 pbuffer context on the system driver
and reuses FillInGLESCapabilities, including the indirect-draw
gl_InstanceID semantics probe; the Vulkan probe checks instance/device
requirements and the optional features each DirectVulkan path degrades
without. Results serialize as ASCII-safe JSON through a JNI entry in
libMobileGL.so; PostActivity renders them and caches the run per
process (single-flight, rotation-safe). PluginActivity keeps its
NoDisplay stub but the launcher entry moves to the POST screen; FCL
plugin discovery reads application meta-data and is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:44:14 +00:00
swung0x48andClaude Fable 5 85b68a9969 [Fix] (MG_Backend/DirectGLES): rebase gl_InstanceID for native indirect draws on ANGLE
ES keeps gl_InstanceID zero-based and ignores the indirect command's
'reserved, must be zero' word, but ANGLE-on-Vulkan forwards the command
verbatim to vkCmdDraw*Indirect and compiles gl_InstanceID to SPIR-V
InstanceIndex, which includes firstInstance. Shaders computing
gl_BaseInstance + gl_InstanceID (Flywheel indirect) then add the base
twice, scrambling instance-to-mesh association.

Probe the actual driver semantics at capability-fill time with a tiny
indirect draw (an ES indirect draw needs a non-default VAO) and, on
leaking drivers, rewrite vertex shaders that use the native indirect
SSBO machinery so gl_InstanceID subtracts the command's baseInstance
word during native indirect draws.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:14:02 +00:00
swung0x48andClaude Fable 5 bd208783d7 [Perf] (MG_Backend/DirectVulkan, MG_State): stop re-deriving per-draw state
- Memoize the program content hash on ProgramObject (keyed by the backend
  state version + compile flags; relinking and binding changes invalidate
  it) and the vertex-input hash on VertexArrayObject (keyed by a new
  aggregate config version bumped by every attribute mutation). Full-SPIRV
  XXH64 hashing fell from 13.7% to 1.4% of the render thread.
- ProgramObject also gains a link version and a global-UBO content version
  (bumped by uniform writes and on relink, wrap-safe around the backends'
  "never uploaded" sentinel) for backends to gate uploads and link caches.
- Reuse member scratch vectors in SetupDraw, UploadAndBindVertexBuffers,
  GetOrCreatePipeline and BindProgramUniformBuffers instead of allocating
  per draw (~12% of render-thread time was in the allocator).
- Replace hot-path dynamic_cast with AsMipmapTexture (storage-type tag +
  static_cast); TextureObjectMipmap is the only Mipmap-tagged branch.
- Register/prune texture aliases only when a new (texture, lifetimeId)
  identity appears instead of scanning the entire alive map on every
  sampled-texture sync.
- Make the fallback VkPresentModeKHR log strings report the actual mode.

Vanilla render-thread share of libMobileGL dropped from 48% to 35% on
DirectVulkan (simpleperf, Adreno 830).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:12:50 +00:00
swung0x48andClaude Fable 5 139de76347 [Fix] (MG_State/MG_Impl/MG_Backend): render Flywheel instanced+indirect on both backends
Create 6 / Flywheel 1.0.6 now renders correctly with both flywheel:instancing
and flywheel:indirect on DirectGLES and DirectVulkan (verified in-game on
Adreno 830: waterwheels and cogwheels solid, animated, correct pairing, no
crashes across all four combinations).

- MG_State/MG_Impl: sync explicitly-ranged SSBO bindings of FLUSH_EXPLICIT
  persistent maps to the backend before compute dispatches. Flywheel writes
  its scatter-copy descriptors into the staging ring's persistent map and
  never flushes that span (UB per spec, works on drivers whose maps alias
  GPU-visible memory); our maps alias the CPU shadow, so the descriptors
  never reached the GPU: the scatter compute copied nothing (GLES: empty
  draw commands) or stale garbage (Vulkan: wild indirect commands ending in
  VK_ERROR_DEVICE_LOST).
- MG_Impl/MG_Backend: real glFenceSync objects backed by backend fences
  (GLES: native ES syncs guarded by context generation and owner thread;
  Vulkan: buffer-manager frame serials), replacing always-signaled stubs
  that let Flywheel reclaim staging memory the GPU still reads.
- MG_Backend/DirectGLES: compute dispatches now run the same per-program
  resource sync as draws (uniform-block bindings and sampler units must be
  re-established through the API because layout(binding) is stripped from
  transpiled ESSL) and rebind texture units afterwards; the cull shader
  used to read a stale _FlwFrameUniforms binding and the depth-pyramid
  downsample sampled a stale unit-0 texture, zeroing the Hi-Z pyramid and
  occlusion-culling all Flywheel geometry. Image uniforms are excluded from
  glUniform1i (ES bakes their unit via layout(binding)); image-unit sync is
  clamped to the device limit; eliminated/SSBO-classified uniform blocks
  are skipped.
- MG_Backend/DirectGLES: gl_BaseInstance in native indirect draws reads the
  GPU-written command buffer through an injected mg_IndirectParams SSBO
  view addressed per draw instead of the zero CPU shadow; layout(binding)
  is preserved for SSBO/image declarations (ES has no API rebinding for
  them); the ES context ownership claim moved to a global atomic owner
  thread with an EGL ground-truth check, and deferred buffer op state is
  mutex-guarded, so ops cannot silently no-op after context migration.
- MG_Backend/DirectVulkan: new RebaseInstanceIndexPass rewrites vertex
  InstanceIndex loads to (InstanceIndex - BaseInstance). glslang's relaxed
  Vulkan mode aliases gl_InstanceID to InstanceIndex, which includes
  firstInstance, but GL's gl_InstanceID is zero-based - draws with nonzero
  baseInstance paired meshes with wrong instance data (cogwheel drawn as a
  waterwheel, another wheel collapsed invisible). Gated on the
  shaderDrawParameters device feature. Sampled-read barriers additionally
  cover the compute stage (the Hi-Z downsample samples the depth
  attachment from compute), and short uniform-buffer ranges keep the
  existing zero-padding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 06:30:10 +00:00
swung0x48andClaude Fable 5 5ea49f3c50 [Feat] (MG_Backend/DirectGLES): support Flywheel indirect rendering
- Advertise ARB_gpu_shader5 / ARB_multi_bind / ARB_shading_language_420pack /
  ARB_vertex_attrib_binding / ARB_shader_image_size so LWJGL reports
  SUPPORTS_INDIRECT.
- New LowerDrawParametersPass demotes DrawIndex/BaseInstance/BaseVertex
  builtins to Private globals (mg_DrawID/mg_BaseInstance/mg_BaseVertex) for
  the ESSL transpile; SPIRV-Cross otherwise throws for ES profiles. The
  program manager promotes the emitted globals to uniforms and feeds them
  per (sub-)draw.
- Indirect draws now execute natively on the GPU (glDrawElementsIndirect /
  glDrawArraysIndirect per command) when an indirect buffer is bound, so
  compute-written command fields (Flywheel culling updates instanceCount)
  are honored; detects GL_EXT_base_instance and falls back to the CPU loop
  when the command's baseInstance cannot be consumed natively.
- Sync SSBO binding points for graphics draws, not just compute (Flywheel
  vertex shaders read instance data from SSBOs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 13:33:05 +00:00
swung0x48 0bee379b61 [Fix] (MG_Util/ShaderTranspiler): keep decomposed workgroup types before globals 2026-07-06 03:58:26 +08:00
swung0x48 d35e452368 [Fix] (MG_Util/ShaderTranspiler): rewrite workgroup vec3 composite loads 2026-07-06 01:25:44 +08:00
swung0x48 86f322e252 [Fix] (MG_Util/ShaderTranspiler): keep workgroup vec3 pass no-op clean 2026-07-06 00:25:49 +08:00
swung0x48 b40def47eb [Refactor] (MG_Util/ShaderTranspiler): replace Photon shared vec3 regex hack with SPIR-V pass
Replace the application-specific PackPhotonSharedVec3Memory GLSL regex
patch with a general DecomposeWorkgroupVec3Pass SPIR-V optimization pass.

The new pass decomposes vec3/ivec3/uvec3/bvec3 Workgroup (shared) memory
variables into scalar arrays (e.g. shared vec3 arr[N][M] -> shared float
arr[N][M][3]), rewriting whole-vector loads/stores into per-component
scalar loads/stores. Component-level accesses (e.g. arr[i].x) are
unchanged since a trailing component index into a float[3] yields the
same scalar pointer as it did for a vec3.

Unlike the regex hack, the pass is application-agnostic: it does not
match on variable names, array dimensions, or shader pack identity, and
runs at the SPIR-V level before SPIRV-Cross decompilation.

Registered in SanitizeAndOptimizeBinary after AggressiveDCE so dead
workgroup accesses are already eliminated. Asserts on unsupported
OpAtomic*/OpCopyMemory targeting vec3 workgroup pointers.

Adds ProgramUtilTest.DecomposeWorkgroupVec3InSpirvPass covering array
declaration, +=, whole load/store, component access, and row-copy loop.
2026-07-05 23:48:27 +08:00
swung0x48 8cce59302b [Fix] (MG_Util/ShaderTranspiler): keep GL shaders off Vulkan macro path 2026-07-05 09:15:47 +08:00
swung0x48 edec6e4e62 [Fix] (MG_State/GLState, MG_Util/ShaderTranspiler): validate compute local size [skip ci] 2026-07-02 22:18:56 +08:00
swung0x48 b9de562491 [Fix] (MG_Impl/GLImpl, MG_State/GLState): fix frag data location queries [skip ci] 2026-07-02 16:20:43 +08:00
swung0x48 fea8e615f9 [Fix] (MG_Impl/GLImpl): fix indexed shader storage buffer queries [skip ci] 2026-07-02 13:44:11 +08:00
swung0x48 6c7c5a1bc7 [Fix] (MG_Util/ShaderTranspiler): rename "sampler" in parameter name to avoid driver compiler issues 2026-06-30 22:31:49 +08:00
swung0x48 2f1949e093 [Feat] (iOS): support MobileGL builds and Metal surfaces 2026-06-30 07:25:20 +08:00
swung0x48 e7e6888768 [Fix] (MG_Util/PixelStoreProcessor, MG_Backend/DirectVulkan): normalize packed RGBA uploads 2026-06-29 16:02:27 +08:00
swung0x48 f121aeb57f [Feat] (MG_Impl): add macOS CGL and NSOpenGL frontends 2026-06-28 20:33:14 +08:00
swung0x48 5d6cb7dfed [Fix] (MG_Backend/DirectGLES): fix Super Duper Vanilla on Adreno
- emulate RGB16 SNORM fallback writes
2026-06-27 00:06:13 +08:00
swung0x48 195330ccea [Fix] (MG_Backend/DirectGLES): fix Complementary shaders on Mali
- emulate RGBA8 SNORM fallback writes
2026-06-26 23:33:46 +08:00
swung0x48 76acae9889 [Fix] (MG_Backend/DirectGLES): make format caveats probe-driven 2026-06-26 14:12:24 +08:00
swung0x48 18d19a9a8b [Fix]: fix Complementary
- add a DirectGLES ANGLE fallback control for 8-bit SNORM texture formats

- normalize SNORM8 textures to float storage so ANGLE can render Complementary intermediate framebuffers

- reuse the normalized upload conversion path for SNORM8 and existing norm16 float fallbacks
2026-06-23 07:37:25 +08:00
swung0x48 3f53041ed2 [Fix]: fix BSL
- keep ANGLE RGBA16 textures on the native norm16 path

- add separate RGB16 and SNORM16 fallback controls for DirectGLES format normalization

- convert RGB16 fallback uploads to float when using RGB32F storage
2026-06-22 23:00:15 +08:00
swung0x48 c08ac7db72 [Fix]: fix Sundial Lite
- detach source texture from synced GLES framebuffers before mipmap generation

- bind a complete scratch framebuffer while calling glGenerateMipmap

- force ANGLE norm16 texture fallback and convert 16-bit normalized uploads

- raise Sundial Lite retrace tolerance for software DirectGLES validation
2026-06-22 22:07:06 +08:00
swung0x48 252e59334d [Fix] (trace-replay): pass ANGLE path to APK retrace 2026-06-22 12:30:10 +08:00
swung0x48 565dc90bf0 [Fix] (trace-replay): use ANGLE for DirectGLES APK retrace 2026-06-22 12:18:33 +08:00
swung0x48 84dba77275 [Fix]: fix Bliss
- enable glslang NaN min/max/clamp semantics

- register Bliss retrace fixture

- track remaining fixture validation queue
2026-06-19 21:20:02 +08:00
swung0x48 19e4ba386d [Fix]: fix Chocapic V6 Lite
- prune unused SPIR-V interface variables before GLES transpilation

- remap shader varyings through glslang IO resolver bindings

- initialize opaque uniforms from explicit sampler bindings only

- avoid side effects in texture binding assertions

- register Chocapic V6 Lite retrace fixture
2026-06-19 18:42:32 +08:00
swung0x48 e7bb46e819 [Fix] (MG_Backend/DirectGLES): support Sundial Lite shader pack 2026-06-17 10:58:12 +08:00
BZLZHH a4261f8b19 [Fix] (MG_Backend/DirectVulkan, MG_State/TextureState, MG_Test): harden default-fbo clear lifetime tracking 2026-06-10 09:49:16 +08:00