Commit Graph
133 Commits
Author SHA1 Message Date
swung0x48 1469022b53 [Fix] (Shader): keep ESSL 3.10 block-array indexing and image-uniform rules for sources that declared an es version 2026-10-12 08:32:49 -04:00
swung0x48 e98c5b4a6a [Fix] (GL): validate program pipelines by the spec rules in glValidateProgramPipeline and on every pipeline draw 2026-10-12 08:32:49 -04:00
swung0x48 45f5bae8b3 [Fix] (GL): answer one for the name-length program queries of a program linked from SPIR-V 2026-10-12 08:32:44 -04:00
swung0x48 e70925d35b Pipe: per-program global-constants key and frontend-written spans replace the client shadow diff. 2026-10-09 18:26:43 -04:00
swung0x48 462318c636 [Fix] (GL): a detached shader leaves the GL-visible attach list at once, and re-attaching it before the next link is legal. 2026-10-06 10:14:53 -04:00
swung0x48 c3af0719f0 [Feat] (GL): implement GL_ARB_shader_objects over the core's shared shader / program name space. 2026-10-06 10:11:19 -04:00
swung0x48 90e63c9e2a [Feature] (EGL+GL): advertise EGL_EXT_create_context_robustness and GL robustness, with per-context reset notification and a pid-targeted device-loss knob. 2026-10-03 04:37:33 -04:00
swung0x48 0b6842ac8b [Fix] (EGL+GL): let ANGLE's GL backend run on MobileGL (GBM displays, foreign wl_display, precision query, ES-free GL_VERSION) 2026-10-02 02:58:55 -04:00
swung0x48 a196ada4c1 [Feat] (Impl): call MGP_FILL before every GLFunctionsTable entry - 83 statements over 69 verbs, a no-op in the pull build
- Every call through gBackendFunctionsTable.GL in the seven MG_Impl TUs (Drawing 36, Framebuffer 11, Texture 8, Getter 3, Program 1, Query 18, Sync 6) is preceded by MGP_FILL(<Verb>); placed after every early return the call sits behind - the conditional-render check, the null-entry guards, the loop bodies - so a verb whose entry is null on this backend never bumps the serial.
- Seven calls sit on a continuation line of a guarded expression (GetQueryResult64 x2, BeginXfbPrimitivesQuery, BeginTimeElapsedQuery, QueryCounterTimestamp, IsTimerQuerySupported, GetSyncStatus) and two more fold the null guard into the same expression (IsQueryResultAvailable, ClientWaitSync's guarded return); there the fill precedes the statement, so a null entry bumps the serial once with nothing to read it - harmless for the poison, recorded for the record.
- Each TU includes <MG_Impl/Pipe/PipeFill.h> after its last MG_State/MG_Backend include; under MOBILEGL_PIPE_PUSH=OFF the macro is ((void)0) and the pull library is symbol-identical with a .text delta of zero.
2026-09-06 01:51:16 -04:00
swung0x48 9ef33f4274 [Fix] (Review): bound copies by the requested level, reach every cube face, keep array layer counts, and give glSpecializeShader its spec error surface 2026-08-27 05:51:58 -04:00
swung0x48 6f299372c6 [Feat] (Program): implement GL_ARB_gl_spirv - glShaderBinary, glSpecializeShader and the SPIR_V_BINARY state, feeding the module into the ordinary compile pipeline 2026-08-27 05:37:17 -04:00
swung0x48 be7bf21eb8 [Fix] (ShaderTranspiler): enforce the layout(binding) range rule for samplers, images and uniform/atomic-counter blocks, not only for SSBOs 2026-08-27 05:37:17 -04:00
swung0x48 06744fde7f [Fix] (Getter): close the review findings - six-stage combined uniform blocks, bounded uniform-block bindings, honest vertex-stream count, per-format sample ceilings 2026-08-27 03:54:16 -04:00
swung0x48 6cc9faf772 [Feat] (Program): report the geometry and tessellation link properties glGetProgramiv had no source for 2026-08-27 03:49:38 -04:00
swung0x48 d4247db6c3 [Fix, Test] (ShaderTranspiler, GLImpl, ProgramState, DirectVulkan): keep fp64 where the backend consumes it natively 2026-08-22 00:57:06 -04:00
swung0x48 6e2a3b3496 [Fix, Test] (GLState): stop enumerating buffer variables as GL uniforms 2026-08-21 11:55:29 -04:00
swung0x48 f5a0779385 [Fix, Test] (GLState): keep the atomic-counter and storage blocks out of the GL uniform-block list 2026-08-21 11:54:45 -04:00
swung0x48 fdbe0b3117 [Fix, Test] (DirectGLES, DirectVulkan, GLImpl, GLState): ask the last link, not the live attach list, what stages a program has 2026-08-21 08:01:48 -04:00
swung0x48 4b41f01b68 [Merge] (DirectGLES, GLState, ShaderTranspiler): land GL43 wave5 with its two new passes inside the L2 boundary 2026-08-21 00:43:39 -04:00
swung0x48 7a7340ebe2 [Fix, Test] (GLImpl, ProgramState): answer the classic uniform queries for atomic counters at GL level 2026-08-21 00:07:07 -04:00
swung0x48 a8228ca287 [Merge] (ShaderTranspiler, GLState, DirectGLES): land dev GL43 wave2/wave3 under the translation cache 2026-08-20 18:03:06 -04:00
Swung0x48 14744f117c [Refactor] (ProgramInterface): build the program-resource model from the reflection snapshot, retiring GetReflection 2026-08-20 11:47:57 -04:00
Swung0x48 8329ab4264 [Refactor] (ProgramState): answer the GL query surface from an owned reflection snapshot, not the live TProgram 2026-08-20 11:43:42 -04:00
swung0x48 a9fb7ef0af [Fix, Test] (GLImpl, GLState): answer GL_ACTIVE_ATOMIC_COUNTER_BUFFERS and implement glGetActiveAtomicCounterBufferiv 2026-08-20 11:23:33 -04:00
swung0x48 6aed3b08f3 [Fix] (DirectVulkan, GLImpl, MG_State, ShaderTranspiler): second audit round over the remaining memo sites
Six more verified defects from the residual memo/cache mechanisms:

- Program resource cache (DirectVulkan reflection): glShaderStorageBlockBinding
  deliberately does not bump the backend state version, and the SSO pipeline
  composite is unnamed so the by-name in-place patch can never reach its slot -
  the composite kept serving pre-rebind SSBO bindings. The cache now keys on the
  program's block-binding version; a binding-only change re-applies the overrides
  by name instead of re-running spirv-reflect. SetShaderStorageBlockBinding also
  gains the equality bail-out its uniform-block sibling has, so the composite
  mirror's replay stops churning the version every draw.
- LinkProgram's allowVSOnlyPrograms function-static latch never set its own
  initialized flag (dead memo, re-read every call) - and completing it would have
  frozen a per-backend capability across re-initialization. Replaced with a fresh
  per-link read from the null-checked active backend.
- Query object registry: drained at full library teardown (DestroyAllQueryObjects,
  mirroring DestroyAllSyncObjects) - undeleted queries and their backend wrappers
  leaked across Destroy/Initialize cycles, stale ids stayed IsQuery == GL_TRUE in
  the re-initialized library, and a later delete could hand the old backend's
  wrapper to a different backend's DeleteBackendQuery.
- Converted vertex streams and the host-side EBO max-index scan now SyncGpuWrites
  before reading the coherent mapping: XFB/SSBO/image writes are merely recorded
  at that point, so the conversion read pre-write bytes (the restart-index
  rewrite already synced; these two host reads did not).
- Zero-stride converted bindings: both converters rejected stride 0, making the
  factory's documented single-element conversion unreachable and silently
  dropping every draw using such a binding; the stride is substituted with the
  element size for the one-element case.
- DemoteFloat64Pass block relayout: measurement queued into the module eagerly,
  so a mid-struct failure left a half-relaid-out block (compacted offsets before
  the failing member, 64-bit offsets after) while claiming the block was left
  alone. Decoration writes are now collected and committed only when the whole
  block measures successfully.
2026-08-19 16:41:28 -04:00
Swung0x48 dd2a62228f [Fix, Feat, Test] (MG_Util, MG_Backend, MG_Impl, MG_State): correct the log severity ordering and unwind the diagnostics it silenced 2026-08-13 02:02:49 -04:00
swung0x48 46fbd837b3 [Fix, Test] (MG_State, MG_Impl, MG_IntegrationTest): a double uniform initializer no longer reads zero 2026-08-12 06:41:54 -04:00
swung0x48 62a2dae5ba [Fix, Test] (MG_Impl, MG_State, MG_IntegrationTest): glUniform*d stores what the demoted shader reads 2026-08-12 06:20:11 -04:00
swung0x48 450eb209b6 [Fix, Test] (MG_State, MG_Impl): the program interface of a separable program is its own first and last stage, not vertex and fragment 2026-08-12 02:38:04 -04:00
swung0x48 ff324057ad [Fix, Perf, Test] (MG_State, MG_Impl/GLImpl, MG_Test, MG_IntegrationTest): a pipeline composite mirrors only the uniforms a stage was written to, and survives a sampler or block rebinding 2026-08-11 23:11:13 -04:00
swung0x48 2ce0595fab [Fix, Test] (MG_State, MG_Impl, MG_Backend): a program pipeline's compute stage is dispatched on its own, and the graphics composite draws its stage programs' uniform values 2026-08-11 20:10:17 -04:00
swung0x48 f7d63f88fa [Fix, Test] (MG_State, MG_Impl): a reserved program-pipeline name takes state from UseProgramStages and its siblings instead of rejecting them 2026-08-11 20:10:16 -04:00
swung0x48 ed29e63543 [Fix, Test] (MG_Impl): glGetProgramResourceiv reports a written length on every exit path 2026-08-11 02:27:26 -04:00
swung0x48 1958934594 [Feat] (MG_State, MG_Impl): buffer non-opaque glUniform writes across the SPIR-V window and replay them at its publish 2026-08-10 06:05:37 -04:00
swung0x48 6ea948779e [Feat, Test] (MG_Impl, MG_State, MG_Util): opt-in MOBILEGL_ASYNC_OPTIMISTIC_SHADER_STATUS - shader compile status/log answer without joining, latched per compile 2026-08-10 02:06:17 -04:00
BZLZHH 3e0460e472 [Feat, Fix] (MG_Impl, MG_State, MG_Backend): program interface queries from frontend reflection; Espryt state-shadow reset
Wave 2 of the advertised-extension conformance campaign.

PROGRAM INTERFACE QUERIES (the load-bearing piece). glGetProgramInterfaceiv
and the five glGetProgramResource* entry points were answered by the
BACKENDS - Espryt asked the real driver about SPIRV-Cross-generated ESSL
whose namespace is not the GL one (default-block uniforms live in
MGL_GLOBAL_UBO there), and Magma kept a second, partial reflection that
hardcoded types and diverged from the frontend. Both are now deleted; a new
frontend resource-model layer (ProgramInterface.{h,cpp}) answers every
interface - uniforms, uniform blocks, atomic-counter buffers (recovered
from glslang's synthesized gl_AtomicCounterBlock_<binding> lowering),
buffer variables, shader-storage blocks (classified by TType storage
qualifier since glslang reflects them as uniform blocks), program inputs/
outputs (built-ins' layoutLocationEnd sentinel mapped to -1), and
transform-feedback varyings including the gl_NextBuffer/gl_SkipComponentsN
pseudo-varyings - from the glslang reflection the frontend already trusts
for glGetActiveUniform. Name/index round-tripping, the "[0]" array
spelling, and the GL 4.6 table 7.2 prop/error matrix live in the new layer
only; GetActiveUniform*/GetActiveAttrib* are untouched.

glShaderStorageBlockBinding now takes the interface-layer index (the one
GetProgramResourceIndex returns, with a range check it never had), records
the binding on the program keyed by block NAME - the one coordinate all
three index spaces agree on - and delegates by name across the backend
boundary. Both backends reseed the recorded bindings on their own program
rebuilds, so an unrelated resync can no longer silently revert a rebound
block, and GL_BUFFER_BINDING reports the live binding, not the declared
one. The Espryt delegate applies only to an already-synced twin and can no
longer trigger SyncToBackend from a getter; the sync path's GL query
out-params are initialized and clamped (a load-dependent stack-garbage
Vector size crash caught by the gate, reproduced 3/50 pre-fix, 100/100
post-fix under saturating load).

ESPRYT RENDER-STATE SHADOW RESET (rides along because it shares
DirectGLES.cpp): the render-state shadow is file-static and survives
MobileGL context switches, so GL_FRAMEBUFFER_SRGB (and the whole synced-cap
class) leaked between contexts - the cross-test leakage class the CTS maps
have carried for a week. MakeCurrent now invalidates the shadow like it
already invalidates the program/FBO/buffer caches, and the resync resolves
a never-set scissor box to the current surface instead of pushing the
(0,0,0,0) sentinel verbatim (which scissored everything away - caught by
the retrace gate, bisected to the exact field via a bitmask probe, and
fixed by resolving like the viewport path rather than reverting).

Frontend riders exposed by the layer: glGetUniformLocation resolves
arrays-of-arrays element addressing ("a[2][1]"); transform-feedback capture
accepts element-addressed varying names ("b[1]") and snapshots the request
verbatim for the interface (Magma's decorate pass logs loudly that element
capture is unimplemented there - follow-up).

KNOWN GAPS, documented in code and tests: the 6 subroutines-* cases
(glslang refuses subroutine for SPIR-V; wave 3), the 5 separate-programs-*
cases (glslang's pipe-I/O reflection cannot see a separable non-vertex
stage's own inputs; needs stage-aware output validation first), and
uniform-block-types' per-instance stage masks (not derivable from the
reflection).

Gate: 593/593 unit at default and kill-switch, x10 each, plus the SSB race
case 100/100 under 20-way CPU load; ext caselist Espryt 77.87% -> 80.42%,
Magma 77.58% -> 79.39% (+212 fixed, 0 newly broken); program_interface_query
2/43 -> 31/43 unique on Espryt, 9/43 -> 31/43 on Magma, backends now
byte-identical; KHR-GL45.direct_state_access 370/371 + 371/371 with the 4
sRGB leak victims recovered in cross-test ordering; KHR-GL33 held at
9884/9886; full 39x2 CI retrace with zero wave-attributable failures (the
3 failing newly-added fixtures are bit-identical on the pristine baseline).
2026-08-09 08:12:30 -04:00
BZLZHH 33ff177bb2 [Fix, Test] (MG_Impl, MG_State): advertised-extension conformance wave 1 - uniforms, validators, getters
First wave of the advertised-extension CTS campaign (targeted caselist: the
glcts groups of every extension both backends advertise, 4867 cases across the
KHR-GL41..46 namespaces). All frontend, shared by both backends:

- Non-square float matrix uniforms actually upload: glUniformMatrix{2x3,3x2,
  2x4,4x2,3x4,4x3}fv and the six glProgramUniformMatrix* twins were
  validate-only no-ops; they now write column-at-a-time at the global UBO's
  16-byte std140 column stride, honouring transpose. glUniformMatrix2fv had
  the sibling bug - mat2 written as 4 contiguous floats put column 1 at byte
  8 instead of 16. The readback path only ever un-padded mat3, so
  glGetUniformfv is fixed for mat2, mat3x2 (previously mis-gathered) and
  every non-square shape, with the bounds check widened to the padded span.
- glBindBufferRange validates offset/size at last: size <= 0, offset < 0,
  SSBO and UBO offset alignment, transform-feedback offset AND size
  multiples of 4 - all before any state write (a negative offset used to
  reach Range1D unchecked). glBindBuffersRange inherits per element, with
  the ARB_multi_bind up-front [first, first+count) checks added to the
  BindBuffersBase/Range and BindSamplers prologues.
- BufferSubData's second, wrong mapped-overlap test deleted (it rejected
  every write at or after a mapped range's start, mapped or not); the state
  layer's assert relaxed to the same half-open intersection the frontend
  checks. BufferStorage error precedence fixed: no-bound-buffer now beats
  bad-size/flags.
- glSamplerParameteri accepts the full GL_NEVER..GL_ALWAYS compare-func
  range (NEVER/LESS/EQUAL were rejected by a wrong lower bound).
  glBindSampler's unit gate uses GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS instead
  of the frontend array capacity, shared with glBindSamplers by construction.
- Getters: GL_MAX_SHADER_STORAGE_BLOCK_SIZE in glGetIntegerv; atomic-counter
  buffer limits; all 11 per-unit GL_TEXTURE_BINDING_* plus GL_SAMPLER_BINDING
  in glGetIntegeri_v; GL_VERTEX_ATTRIB_BINDING/_RELATIVE_OFFSET across the
  vertex-attrib query family; glGetFloati_v/glGetDoublei_v implemented (were
  stubs); KHR_debug limits raised to spec floors.
- glCreateShader records INVALID_ENUM for an unknown type (it previously
  handed out a usable name with no error at all); glCreateShaderProgramv
  validates count up front. glDispatchCompute/Indirect validate work-group
  counts, offset alignment and indirect-buffer presence.
- glVertexAttribIFormat & friends take a positive integer-type whitelist -
  GL_FLOAT/GL_HALF_FLOAT/GL_DOUBLE/GL_FIXED no longer slip through as
  integer attributes.

Gate (headless Mesa, default config = async on): 570/570 unit at default and
with the kill switch; ext caselist Espryt 76.29% -> 77.87% (+81 fixed, 6
crashes -> 0, the whole list now runs in one glcts process), Magma 75.94% ->
77.58% (+80 fixed, 0 newly broken); KHR-GL33 full mustpass lost nothing
(9884/9886, the 2 known Mesa-drift failures); retrace smoke clean (the
bsl-GLES miss is the documented golden drift, bit-identical on the pristine
baseline). The 4 DirectGLES direct_state_access.renderbuffers_storage* cases
that turned red are a PRE-EXISTING GL_FRAMEBUFFER_SRGB cross-test leak,
A/B-proven on an unpatched 2e6fc1ff build - wave 1 removed the two accidental
maskers (a crash partition and a failing case whose error path reset the
state). Fixing the leak itself is queued.
2026-08-09 04:39:22 -04:00
BZLZHH bd0def6133 [Feat] (MG_Impl, MG_State, MG_Util): the GL_KHR_parallel_shader_compile surface (P1 stage 5)
GL_COMPLETION_STATUS_KHR in both object getters, reading the non-joining
node-direct state - the one query that must never block is asserted never
to reach a join gate. glMaxShaderCompilerThreadsKHR/ARB share one
implementation: a zero count suspends async FIRST and then joins every
outstanding compile and link this context owns (suspend-before-join is the
only order whose post-condition is 'nothing in flight'), a nonzero count
restores; the suspension is a process latch the extension controls, kept
distinct from the configuration flag that gates the ADVERTISEMENT - an app
that turned threading off has not made the extension disappear.
GL_MAX_SHADER_COMPILER_THREADS_KHR reports the thread count. DriverPost
gains the MobileGL-side async row (PASS/INFO naming the env knob) and an
informational host-driver row backed by a new GLES capability probe.

The extension string itself lands per backend in the two follow-up
commits, keeping this one green stand-alone.
2026-08-08 13:17:58 -04:00
BZLZHH 57aeeec053 [Perf] (MG_State, MG_Impl, MG_Backend): stop paying per draw and per upload for work already known
A per-draw CPU profile of a real Minecraft frame (perf on the render thread,
which sits at 100% of one core on both backends) said the deficit is translation
overhead, not the GPU, and named where it goes. This removes the largest items
it found, on both backends and in the shared frontend they both feed.

The single biggest one was not translation at all: IsBackendContextCurrentOnThisThread
called eglGetCurrentContext on every invocation, and glvnd answers that with a
getpid() fork check - a real syscall. The predicate sits two and three deep in
every draw (the deferred-release drain, the global-UBO ring availability check,
and the ring allocation), so it accounted for 16.3% of the render thread. EGL is
still the ground truth, but re-verifying it once per thread per frame catches an
external migration at the next frame boundary rather than the next call, which
recovers the same bookkeeping.

Texture uploads now carry a dirty region instead of a per-level flag. Minecraft
animates atlas sprites with 16x16 glTexSubImage2D calls into a 1024x512 atlas
and respecifies the lightmap every frame; a per-level flag turned each of those
into a full-level re-upload - about 3.6 MB a frame of texels nobody changed.
MipmapStorage accumulates the written box, Espryt uploads it with
UNPACK_ROW_LENGTH striding into the level shadow, and Magma stages just that box.
The box is a union, not a range list: repeated writes to one level widen it and
it degrades to exactly the old whole-level upload, which is the honest worst case.

glBufferData(NULL) is the orphaning idiom, and the backend was answering it by
uploading the stale CPU shadow - turning a rename the driver does for free into
a full synchronized upload. BufferObject now records that a NULL respecify leaves
the store undefined, and the upload is skipped until content is actually written.

The rest are smaller and of a kind: the deferred-release queue is probed without
taking its mutex, the UBO ring waits on the frame fence that frees the space it
needs instead of draining the whole pipeline with glFinish at the size cap, VAO
binds go through a shadow so a draw's second bind of the same object does not
reach the driver, the per-draw clean-texture probe short-circuits on the content
version before rebuilding shape info, glUniform drops byte-identical writes
(which otherwise dirty the whole UBO for the next draw), re-binding the texture
or VAO a slot already holds no longer bumps the generation counters a backend
fast path is keyed on, and the texture validators stopped taking shared_ptr by
value.

On Magma: descriptor-set reuse keeps four entries instead of one, because draws
alternating between two programs - the chunk/entity ping-pong - thrashed a single
slot into a full re-allocate and re-write every draw; a DynamicDraw buffer whose
contents survive two frame boundaries is promoted to resident storage instead of
being re-copied into the per-frame arena forever; and sampled-read barriers name
only the shader stages whose device feature is enabled, which also removes a
latent VUID violation (ALL_GRAPHICS names geometry and tessellation stages a
device need not have).

Measured with the Minecraft rig (render distance 32, p50 fps, same machine,
single sample each): vanilla 1.21.1 Espryt 10.8 -> 36.3 and Magma 31.3 -> 44.6;
26.2 snapshot Magma 114.5 -> 210.5. Fabric+Sodium moved inside noise on Magma
(854 -> 766) with the native baseline itself moving 838 -> 1031 between the two
sessions, so treat that cell as unresolved rather than a regression measured.
Unit tests 421/421. The CTS A/B was not run: these numbers and the test suite are
the whole of the evidence, and a conformance regression would not have been
caught here.
2026-08-06 06:24:37 -04:00
BZLZHH 4ce808b9f2 [Feat] (MG_State, MG_Impl, MG_Backend): let a bound program pipeline actually draw
The pipeline object bookkeeping landed already - names, stage slots, queries -
but nothing consumed it. Every draw asked the context for the current program,
got null because a pipeline is used with program zero, and drew nothing;
glCreateShaderProgramv was still a stub returning zero, so
direct_state_access.program_pipelines_functional could not even build its stage
programs and reported InternalError on both backends.

glCreateShaderProgramv is written as the exact call sequence the spec defines it
to be, with one deviation that matters: the link goes straight to
ProgramObject::Link(false) rather than through LinkProgram, because LinkProgram
injects a default fragment shader into a program that has none - correct for a
whole program, wrong for a separable vertex-stage one whose fragment stage comes
from the pipeline. glDetachShader defers removal to the next link, so the program
keeps the shader object it was built from while correctly no longer reporting it
attached. GL_PROGRAM_SEPARABLE joins glProgramParameteri and glGetProgramiv.

Everything downstream of a draw - both backends, the uniform plumbing, the draw
validation - is written against one linked program, so rather than teach all of
it about stages, the pipeline is flattened: GetProgramForDraw() composites the
stage programs' shaders into a single hidden program object and caches it against
a signature of each stage program's lifetime id and link generation, so it is
rebuilt exactly when a stage or a stage's link changes. The composite carries no
GL name - it must not answer glIsProgram, and it must not consume a name the
application could be handed.

Uniform entry points get their own resolver rather than sharing that one:
glUniform* addresses the pipeline's active program, not the composited draw
program. GL_CURRENT_PROGRAM still reads the program in use, which is zero here.

Fixes program_pipelines_functional on both backends.
2026-08-05 07:20:42 -04:00
BZLZHH 300b458132 [Feat] (MG_State, MG_Impl): give program pipelines their object and their state
Every program pipeline entry point was an export stub, and the stub macro's
`return (type)1` made glIsProgramPipeline answer GL_TRUE for anything - including
the names glGenProgramPipelines had never written. All four
direct_state_access.program_pipelines cases failed.

ProgramPipelineObject holds what GL 4.6 core 7.4 says a pipeline is: a program
reference per shader stage, the active program glProgramUniform* addresses, a
validate status and an info log. Its validate status starts false, unlike
ProgramObject's, because a pipeline that has never been validated must report
GL_VALIDATE_STATUS as 0.

The name rules follow the shape queries and transform feedbacks already use, and
which the CTS checks first: glGenProgramPipelines only RESERVES a name and
glIsProgramPipeline answers GL_FALSE for it; the object appears on first bind, or
immediately from glCreateProgramPipelines. Map membership is object existence -
a pipeline, unlike a transform feedback, has no stateful default object zero, so
no everBound flag is needed.

glGet(GL_PROGRAM_PIPELINE_BINDING) reports the real binding now instead of a
hardcoded zero whose comment said the entry points were stubbed.

This is the state half only. program_pipelines_functional needs mixed-stage
rendering - a vertex-only and a fragment-only program drawn together - and stays
failing; glCreateShaderProgramv is deliberately left stubbed until that lands, so
nothing can half-work in between.

Takes program_pipelines_creation, _defaults and _errors from failing to passing
on both backends.
2026-08-05 05:43:13 -04:00
BZLZHH 90ae0f048c [Fix] (MG_Impl): answer the GL_UNIFORM program interface from the frontend reflection
The GL_UNIFORM interface queries and glGetActiveUniform(s)iv describe the same
set of resources in two spellings, but they were reading it from two different
places: the latter from the frontend reflection, the former forwarded straight
to the backend program.

The backend program is not a source of truth for this. It does not exist at all
for a program whose types its shading language cannot express -- a
double-precision uniform has no ESSL form, so the program never links there --
and the interface queries then described a program with no uniforms, which is
how gpu_shader_fp64.fp64.state_query failed.

Route GL_ACTIVE_RESOURCES / GL_MAX_NAME_LENGTH, the resource index, the resource
name and the resource properties for GL_UNIFORM through the same reflection that
already answers glGetActiveUniformsiv, so the two spellings can no longer
disagree and neither depends on the backend having linked. The props that
reflection does not model (GL_ATOMIC_COUNTER_BUFFER_INDEX and the
GL_REFERENCED_BY_* stage bits) still come from the backend, looked up by the
uniform's name so the two index spaces do not have to agree.

GL_MAX_NAME_LENGTH counts the terminator; the stored maximum does not, as every
other caller of GetUniformMaxLength() already accounted for.
2026-08-04 19:36:49 -04:00
BZLZHH 38497174c8 [Feat] (MG_Impl): implement the double-precision uniform state
glUniform*d, glUniformMatrix*dv, their glProgramUniform twins and glGetUniformdv were
all stubs - 35 entry points - so a GL 4.0 program's double uniforms could be declared
and located but never set or read. Worse, glGetUniformfv on one did reach the storage:
the generic getter memcpy'd the uniform's declared size into the caller's buffer, so a
4-byte float pointer received 8 bytes. That overrun is what took the process down in
KHR-GL40.gpu_shader_fp64.fp64.state_query.

The upload path is already templated on the component type, so the vector forms are
wiring. A matrix is not: the column stride the linker used for a double matrix is not
the 16 bytes a float one gets. It is not guessed - the slot the uniform was given is
exactly `columns` columns wide, so dividing states the stride the rest of the pipeline
already agreed on, for both the upload and the readback.

The four getters now convert instead of reinterpreting when the uniform holds doubles,
following GL 4.6 core 7.6: round to nearest for the integer queries, and clamp into the
queried type's range so a negative double read through glGetUniformuiv is 0 rather than
its two's complement.

The case still fails one step further on, where it queries the same uniforms through
GL_ARB_program_interface_query: those calls are answered by the backend program, and an
fp64 shader has none - ESSL has no doubles, so it never links. Answering them from the
frontend reflection is a separate change.
2026-08-04 11:06:15 -04:00
BZLZHH 2dcc15bb0e [Feat] (MG_Impl, DirectGLES): advertise GL_ARB_get_program_binary with no binary format
glProgramParameteri is not core before GL 4.1, so in the 4.0 context the CTS runs it
only exists through GL_ARB_get_program_binary or GL_ARB_separate_shader_objects.
MobileGL advertised neither, so dEQP's loader left the entry point null - and
KHR-GL40.api.coverage, which registers glProgramParameteri from GL 3.2 upwards, called
straight through the null pointer and took the process down.

GL_NUM_PROGRAM_BINARY_FORMATS was already 0, and the extension explicitly allows an
implementation to support no binary format at all; that is the honest state of things
here, since a MobileGL program is a glslang link plus a per-backend translation with no
serialised form. So the extension is advertised for what it really provides:
glProgramParameteri stores GL_PROGRAM_BINARY_RETRIEVABLE_HINT (reported back by
glGetProgramiv alongside a GL_PROGRAM_BINARY_LENGTH of zero), glGetProgramBinary is the
INVALID_OPERATION the spec requires when that length is zero, and glProgramBinary
rejects every format with INVALID_ENUM and leaves the program's LINK_STATUS false.

Applications that ask for a binary get the documented "no formats" answer and fall
back, which is what they already had to do - only now they can ask.
2026-08-04 10:27:23 -04:00
BZLZHH 76f37a18e6 [Feat] (MG_State, MG_Impl, DirectGLES): transform feedback objects, pause/resume and the special capture names
GL 4.0 folds ARB_transform_feedback2 and _3 into core, and neither existed:
glGenTransformFeedbacks, glBindTransformFeedback, glDeleteTransformFeedbacks,
glIsTransformFeedback, glPause/ResumeTransformFeedback, the whole
glDrawTransformFeedback family and glBegin/EndQueryIndexed were all stubs, and
gl_NextBuffer / gl_SkipComponents1..4 failed the link as "not an output of the vertex
stage". Seven KHR-GL40.transform_feedback* cases failed on it, three of them by
leaving a capture open at deinit and taking the process down.

Objects. The capture state and the indexed GL_TRANSFORM_FEEDBACK_BUFFER bindings are
object state, but the context keeps one live copy of both, which is what every
existing reader - each backend's per-draw sync, the drawing and getter paths - is
written against. Rather than teach all of them about objects, a bind saves the live
copy into the outgoing object and restores the incoming one's. Object 0 is the
default object and needs no seeding; operator[] materialises the rest on first touch.

Pause. A paused span captures nothing, and three rules key off that: a draw is exempt
from the capture primitive-mode match, it feeds PRIMITIVES_GENERATED but not
TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, and glUseProgram is allowed again (that last
one was already refused for an active capture, correctly for GL 3.3, which has no
pause).

glDrawTransformFeedback replays the vertices the object captured in its last completed
span, recorded at End. "Has a completed span" is tracked separately from that count,
because a completed empty span draws nothing while an object that never ended one is
INVALID_OPERATION. Drawing from the object whose capture is currently open is
deliberately allowed - feeding a result straight into the next span is the point of
KHR-GL40.transform_feedback.draw_xfb_feedbackk_test.

DirectGLES gets a real driver object per frontend object. That is the only reason the
default one would not do: several objects can be paused at once, and a paused span
lives inside the driver's object. The deferred driver-side Begin (still needed - ES
wants the program current and the buffers bound) now also has to be held back while
the span is paused, or a pause taken before the first draw would open the span on that
draw and subject it to the primitive-mode rule it is exempt from.

Special names. gl_NextBuffer and gl_SkipComponents<n> are consumed during varying
resolution and never become varyings of their own, so they only move where the
following ones land - and stay out of the name list the backend declares on its own
driver. ES cannot express the resulting layout at all: it packs every captured varying
into one gap-free record. So when the layout has holes or spans several buffers,
DirectGLES captures into a scratch buffer bound in place of the application's, and
End distributes the records to the offsets GL asked for. Only the bytes a varying
occupies are written, which is exactly what makes the holes keep the contents the
application left there - the property KHR-GL40.transform_feedback3.skip_components
checks.

glBegin/EndQueryIndexed and glGetQueryIndexediv differ from the plain forms only in the
vertex stream they address, so they validate the index and forward. GL_MAX_VERTEX_STREAMS
stays at 1: multi-stream capture needs ARB_gpu_shader5 stream qualifiers that no ES
driver implements, and the CTS cases that need more than one stream check the limit and
skip.

KHR-GL40.transform_feedback, transform_feedback2 and transform_feedback3: 38/38.
2026-08-04 10:18:53 -04:00
BZLZHH d81a6a0998 [Fix] (MG_Impl): silently ignore program and shader name zero on delete
glDeleteProgram and glDeleteShader are the two entry points in the program/shader name
space where 0 is not "a name GL never handed out" but an explicit no-op: "if program is
zero, it is silently ignored" (GL 4.6 core 7.3, and 7.1 for shaders). Both went through
the shared name validator instead and recorded GL_INVALID_VALUE.

Only tests that never got as far as creating a program noticed, because they still run
their cleanup path: the five KHR-GL40.texture_gather.*-cube-array cases bail out of
Init with "GL_ARB_texture_cube_map_array not supported", then Cleanup deletes its
zero-initialised handles and the leftover error fails the case after the fact - the
downstream-error-misattribution shape. Every array-taking delete already skipped 0.
2026-08-04 09:52:40 -04:00
BZLZHH 07fa84fb8d [Fix] (MG_State, MG_Impl): defer deletion of the program in use
glDeleteProgram on the current program now only flags it: the name (and
every glGetProgram* query) stays valid until the program stops being
current, at which point UseProgram frees the slot and releases orphaned
attached shaders. Previously the name died immediately, so a second
glDeleteProgram - as issued by common CTS utility teardown - recorded
GL_INVALID_VALUE that poisoned the next test iteration's build
(KHR-GL33.clip_distance.functional now passes its build phase).

glIsProgram/glIsShader piggyback on the same rule: a flagged name is
still a program/shader while it stays GL-visible, which resolves the
long-standing FIXMEs there.
2026-07-31 18:21:14 -04:00
BZLZHH 48dd1c5956 [Feat] (MG_State, MG_Impl): transform feedback state, validation and reflection
First stage of GL 3.0 transform feedback: glTransformFeedbackVaryings /
glGetTransformFeedbackVarying / glBeginTransformFeedback /
glEndTransformFeedback were unimplemented stubs. This adds

- per-program capture state: requested varyings apply on the next link and
  resolve against the last vertex-processing stage's linker objects (with
  gl_Position/gl_PointSize handled as builtins), failing the link on
  unknown or duplicate names or exceeded interleaved/separate limits, with
  offsets and strides computed per GL rules;
- context Begin/End state with the GL 3.3 error semantics: invalid
  primitive modes, redundant Begin/End, missing program or capture-buffer
  bindings, primitive-mode compatibility at draw time, and the
  while-active prohibitions on rebinding capture buffers, switching
  programs, and relinking the captured program;
- GetProgramiv TRANSFORM_FEEDBACK_* queries and a 4-slot bound on indexed
  GL_TRANSFORM_FEEDBACK_BUFFER binding points.

KHR-GL33.transform_feedback api_errors/linking_errors/get_xfb_varying now
pass; GPU-side capture is the remaining stage.
2026-07-31 17:08:37 -04:00
BZLZHH a389477f78 [Fix] (MG_Impl): report INVALID_OPERATION for shader names in program APIs
Program entry points answered GL_INVALID_VALUE whenever the name did not
resolve to a program, including names that exist but belong to a shader
object. Programs and shaders share one name space, so the spec (and
KHR-GL33.get_uniform_tests.get_uniform) requires GL_INVALID_OPERATION for
the shader-name case and GL_INVALID_VALUE only for names GL never handed
out, matching the interface-query helper's existing behavior.
2026-07-31 16:47:45 -04:00
swung0x48 c30bd0fabb [Fix] (MG_Backend/DirectGLES, MG_Impl/GLImpl): draw/read-buffer state could land on the wrong FBO (glDrawBuffer's static-array latch; SyncToBackend emitting glDrawBuffers/glReadBuffer for the non-bound target; no resync on bound-FBO attachment/drawbuffer edits) leaving MC 26.3's OIT color clears as no-ops; apply per bound target and track the FBO object version 2026-07-17 11:45:54 -04:00