- The generic export stub returned (GLenum)1; dEQP reads any non-zero status as a lost device
- It is polled after every case (gl3cTestPackages.cpp:121) and sets QP_TEST_RESULT_DEVICE_LOST
- Under the default --deqp-terminate-on-device-lost=enable that tears the whole CTS run down
- MobileGL tracks no GPU resets, so GL_NO_ERROR ("no reset detected") is the honest, spec-correct answer
- Routed through GLImpl::GetGraphicsResetStatus like every other entry point, no inline body in Definitions.cpp
Store the primitive restart index as render state and report it through
glGetIntegerv(GL_PRIMITIVE_RESTART_INDEX), replacing the stub and the
hardcoded 0 in the getter.
- RenderState gains a PrimitiveRestartIndex field (default 0) with
set/get accessors and GLContext wrappers.
- glPrimitiveRestartIndex accepts any GLuint and generates no error.
- glGetIntegerv(GL_PRIMITIVE_RESTART_INDEX) now reads the stored value.
This is the state layer only. The backends do not yet honor an arbitrary
restart index at draw time -- Vulkan and GLES support only the fixed
all-ones restart value (GL_PRIMITIVE_RESTART_FIXED_INDEX) -- so a non-
default index is tracked and queryable but not yet applied to indexed
draws.
Tests: RenderStateSanity round-trip (default 0, mid value, and the full
32-bit range). Full SanityTest sweep green (31/31).
Bind a fragment output to both a color number and a color index (0 or 1
for dual-source blending), and report the bound index back through
glGetFragDataIndex.
- ProgramObject now tracks a per-output color index alongside the
location: SetExplicitFragmentOutIndex stores it, it is snapshotted into
the linked map at link time (like the location map), and
GetFragmentDataIndex returns it (0 by default) for an active output.
- glBindFragDataLocation becomes glBindFragDataLocationIndexed with index
0, matching the GL definition, so it also resets a previously-bound
index to 0.
- Validation: index must be 0 or 1 (GL_INVALID_VALUE); colorNumber is
bounded by GL_MAX_DRAW_BUFFERS for index 0 and GL_MAX_DUAL_SOURCE_DRAW_BUFFERS
(reported as 1) for index 1 (GL_INVALID_VALUE); a gl_ name is
GL_INVALID_OPERATION.
- glGetFragDataIndex now returns the real bound index instead of a
hardcoded 0.
The index is tracked for reflection but is not yet plumbed into dual-source
blend rendering, and shader-side layout(index=) qualifiers are not
reflected -- both documented at the call sites.
Tests: index round-trip through a re-link (bind 1 -> GetFragDataIndex == 1;
glBindFragDataLocation resets to 0), plus the validation error table;
mutation-verified end to end. ProgramTest 24/24.
Fill the stubbed GL 3.3 Core glGetFragDataIndex, mirroring its already-
implemented sibling glGetFragDataLocation: validate the program object and
link status, then return the fragment color index the name binds to.
Every active user-defined output uses color index 0. MobileGL does not yet
track dual-source (index 1) bindings -- glBindFragDataLocationIndexed and
the layout(index = 1) qualifier are unsupported -- so the result is exact
for any program that does not use dual-source blending; a name that is not
an active output (including gl_ built-ins) returns -1.
Tests: assertions on the existing linked-program test (valid output -> 0,
unknown name -> -1) plus a standalone invalid-handle case. The invalid-
handle test drains the error queue it produces so no stale error leaks
into a later test (the ProgramTest fixture does not reset it). ProgramTest
24/24.
Two previously-stubbed GL 3.3 Core entry points.
glMultiDrawArrays: mirrors the existing glMultiDrawElements(BaseVertex)
architecture end to end -- a new MultiDrawArrays backend function-table
slot dispatched from the frontend after program/primitive-mode validation
(plus a drawcount < 0 -> GL_INVALID_VALUE guard).
- DirectGLES: PrepareForDraw once, then loop native glDrawArrays with the
same per-range client-side array upload the single DrawArrays does.
- DirectVulkan: build a MultiDrawCmd payload and hand it to a new
VulkanRenderer::MultiDrawArrays, which does one SetupDraw over the union
of the sub-draw vertex ranges and then a vkCmdDraw per range (mirrors
VulkanRenderer::MultiDrawElements).
glGetBufferSubData: reads a range of the bound buffer's CPU shadow into
client memory via a new BufferObject::DownloadSubData, with the same
validation shape as BufferSubData (INVALID_VALUE for negative/overflowing
range, INVALID_OPERATION for no bound buffer or a non-persistent mapped
buffer). The shadow reflects CPU writes and backend write-backs but not
arbitrary GPU-side writes, which is documented on the method.
Tests: 2 BufferTest cases for glGetBufferSubData (round-trip read of a
middle range and the whole buffer, plus out-of-range/negative/no-buffer
errors). BufferTest 32/32, SanityTest 30/30, VertexArrayTest 42/42;
library builds clean. (The glMultiDrawArrays draw paths are not
runtime-testable on this host and are compile-verified against the tested
MultiDrawElements pattern.)
glVertexAttribP{1,2,3,4}ui and their *uiv forms set the CURRENT generic
vertex attribute value from a packed 2_10_10_10_REV word (they are the
packed members of the immediate VertexAttrib* family, not the array-format
path), so they funnel into SetCurrentVertexAttributeFloat and reuse the
existing index validation.
- Add DecodePacked2101010: unpacks x=[0..9], y=[10..19], z=[20..29] (10-bit)
and w=[30..31] (2-bit) from one 32-bit word. Signed fields are two's-
complement (sign-extended per width); normalized conversion uses the
GL 3.3 (2c+1)/(2^b-1) form (10-bit /1023, 2-bit /3), matching the
existing NormalizeSigned* helpers -- NOT the GL 4.2 clamp form.
- type accepts only GL_INT_2_10_10_10_REV / GL_UNSIGNED_INT_2_10_10_10_REV
(GL_INVALID_ENUM otherwise; the 4.4-era 10F_11F_11F_REV is not legal in
3.3). P1/P2/P3 consume the first 1/2/3 components; the rest take the
(0,0,0,1) defaults and are cleared each call. The *uiv forms dereference
a single packed word, not an array.
Tests: 4 VertexArrayTest cases (unsigned decode, signed GL-3.3 formula,
component-count/defaults, type/index/uiv validation). The signed test is
mutation-verified: z==0 -> 1/1023 fails against the GL 4.2 form.
VertexArrayTest 38/38, SanityTest 30/30.
Promote the color writemask to per-draw-buffer state and implement the
indexed glColorMaski entry point (previously a stub), plus its read-back
through glGetBooleani_v.
- RenderState: replace the single BoolVec4 ColorMask with an array of
MAX_DRAW_BUFFERS masks, all initialized to true. SetColorMask now
broadcasts to every draw buffer (glColorMask semantics); GetColorMask
returns draw buffer 0. Add indexed set/get accessors + GLContext
wrappers.
- glColorMaski sets only the addressed draw buffer; out-of-range index
raises GL_INVALID_VALUE (buf is a GLuint, so no GL_INVALID_ENUM path),
mirroring the indexed blend entry points' MAX_DRAW_BUFFERS bound.
- glGetBooleani_v(GL_COLOR_WRITEMASK, i) reports draw buffer i's four
booleans; the non-indexed glGetBooleanv still reports draw buffer 0.
- Fix GLboolean coercion in the color-mask path: any nonzero value
enables the component (was == GL_TRUE, which wrongly rejected e.g. 2).
- DirectGLES sync reads ColorMasks[0] (GLES core has only non-indexed
glColorMask).
Tests: ColorMaskIndexedStoresAndReadsBack covers the per-buffer vs
broadcast semantics, buffer-0 read-back, out-of-range INVALID_VALUE, and
the GLboolean coercion (mutation-verified: == GL_TRUE fails it). Full
SanityTest sweep green (30/30).
Six pure-state entry points that were stubs or empty // TODO bodies, all backed by new
context state and read back through glGet*.
* glHint: Hint_State was an empty TODO. Store the 4 GL 3.3 core hint targets (LINE_SMOOTH,
POLYGON_SMOOTH, TEXTURE_COMPRESSION, FRAGMENT_SHADER_DERIVATIVE), default GL_DONT_CARE.
Validate target and mode (FASTEST/NICEST/DONT_CARE) -> GL_INVALID_ENUM otherwise. The
compatibility-only targets (GL_PERSPECTIVE_CORRECTION_HINT, GL_POINT_SMOOTH_HINT, GL_FOG_HINT,
GL_GENERATE_MIPMAP_HINT) are rejected. The glGetIntegerv hint cases, previously hardcoded to
GL_DONT_CARE, now read the stored value; glGetBooleanv on a hint is always GL_TRUE.
* glPointParameter{f,i,fv,iv}: the scalar _State bodies were empty TODOs and the *v forms were
stubs. Only the 2 core pnames are accepted: GL_POINT_FADE_THRESHOLD_SIZE (float, default 1.0,
GL_INVALID_VALUE if negative) and GL_POINT_SPRITE_COORD_ORIGIN (GL_LOWER_LEFT/GL_UPPER_LEFT,
default GL_UPPER_LEFT, GL_INVALID_ENUM on a bad value -- note the different error code from the
fade case). The compat pnames (POINT_SIZE_MIN/MAX, POINT_DISTANCE_ATTENUATION) are rejected. All
four forms funnel through one (pname, float) handler. glGetIntegerv(GL_POINT_FADE_THRESHOLD_SIZE)
was hardcoded to 1; it now rounds the stored float, glGetFloatv reads the float directly (keeping
the fractional part), and GL_POINT_SPRITE_COORD_ORIGIN gained a getter case (it had none).
* glPixelStoref: funnels into the existing glPixelStorei state, but converts per type -- boolean
pnames (PACK/UNPACK_SWAP_BYTES/LSB_FIRST) by a zero-test so 0.4 -> TRUE, integer pnames by
round-to-nearest. A blanket round would wrongly turn a fractional true flag into false.
* glGetDoublev: funnels through glGetFloatv and widens, writing exactly the pname's component count
(1/2/4) so a single-component query cannot overrun the caller's buffer. MobileGL stores no native
double state (depth range/clear are float), so widening from float matches its real resolution.
State added to RenderStateParameters + RenderState Set/Get + GLContext wrappers, following the
existing LineWidth/DepthRange pattern. Covered by 4 SanityTest cases (set-then-get round trips, the
core-vs-compat enum rejections, the two different error codes, and the glPixelStoref boolean
zero-test, which was verified to fail against a blanket-round implementation).
Completes the uniform-block reflection chain: glGetUniformIndices, glGetActiveUniformName
and glGetActiveUniformBlockiv were already implemented; glGetActiveUniformsiv was the last
stub. Supports all 8 GL 3.3 Core pnames:
* GL_UNIFORM_TYPE / SIZE / NAME_LENGTH / BLOCK_INDEX / OFFSET / ARRAY_STRIDE come straight from
glslang's TObjectReflection (the same reflection the existing uniform queries use).
* GL_UNIFORM_IS_ROW_MAJOR from the member's TType layout qualifier, guarded by isMatrix() so a
scalar in a layout(row_major) block does not wrongly report 1.
* GL_UNIFORM_MATRIX_STRIDE is derived: glslang exposes no matrix stride, so it is computed from the
std140 rule (each column/row vector rounded up to a vec4), which matches the std140 layout
MobileGL's SPIR-V path emits. Evaluates to 16 for every GL 3.3 float matrix.
The -1-vs-0 distinction is handled explicitly: OFFSET / ARRAY_STRIDE / MATRIX_STRIDE / BLOCK_INDEX
return -1 for a default-block uniform (glslang gives arrayStride 0 there, so it is gated on block
membership), while ARRAY_STRIDE / MATRIX_STRIDE return 0 for a non-array / non-matrix member that IS
in a block. Errors: GL_INVALID_VALUE for uniformCount<0, any index >= active uniform count, or a
never-generated program name; GL_INVALID_OPERATION for a live shader name; GL_INVALID_ENUM for an
unaccepted pname (e.g. the GL 4.2 GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX). All validation runs before
any write, so params is untouched on error. There is no "not linked" error -- an unlinked program has
zero active uniforms, so any index raises GL_INVALID_VALUE.
Also fix GetActiveUniformArraySize, which returned glslang's TObjectReflection.size verbatim: that
field only carries the element count for a non-block array and reports 1 for a block array member,
so GL_UNIFORM_SIZE (and glGetActiveUniform's size out-param, and glGetProgramResourceiv's
GL_ARRAY_SIZE) wrongly reported 1 for an array inside a UBO. Take the count from the TType instead,
which is authoritative for both cases.
Covered by 3 ProgramTest cases (std140 block with scalar/array/mat4 + a default-block sampler, a
row_major variant, and the six error cases) that link real shaders and assert every pname value.
These set (or query) the current generic vertex attribute value, GL_CURRENT_VERTEX_ATTRIB.
All funnel into the existing, correct primitives -- VertexAttrib4f / VertexAttribI4i /
VertexAttribI4ui, and GetVertexAttribfv for the double query -- so the new bodies add only a
null-pointer guard; index validation (incl. the deliberate index-0 rejection) is inherited.
Families implemented (of the 49 core glVertexAttrib* setter stubs, all but the 8 packed
glVertexAttribP*ui, which need a real 2_10_10_10 DataType and are left for later):
* d / dv / s / sv and 4bv / 4iv / 4uiv / 4usv: value-preserving conversion to float. These do
NOT normalize -- only the N forms do.
* 4Nbv / 4Nsv / 4Niv / 4Nusv / 4Nuiv: normalized. Signed normalization uses the GL 3.3 Core
formula f = (2c + 1) / (2^b - 1), which maps the full signed range onto exactly [-1, 1] (byte
-128 -> -1.0, 127 -> +1.0) and cannot represent 0 exactly (0 -> 1/(2^b-1)). This is NOT the
GL 4.2 revision f = max(c/(2^(b-1)-1), -1); using that here would be a conformance bug.
Unsigned normalization is the version-independent c/(2^b-1). The 32-bit forms compute in double
because 2*INT_MAX overflows int32 and neither 2^32-1 nor 2^31-1 is representable as float.
* VertexAttribI{1,2,3}{i,iv,ui,uiv} and I4{bv,sv,ubv,usv}: integer forms, writing the integer
current-value view verbatim (never the float one). Signed sign-extend to VertexAttribI4i,
unsigned zero-extend to VertexAttribI4ui; w defaults to the integer 1. I4ubv/I4usv route to the
unsigned setter (distinct from the normalized-float 4Nubv).
* glGetVertexAttribdv mirrors GetVertexAttribfv: reads the float view as four doubles for
GL_CURRENT_VERTEX_ATTRIB (no bound VAO required), one value for the array pnames, same error rules.
Covered by 5 new round-trip tests whose boundary values (byte -128 -> -1.0 exact, 0 -> 1/255,
INT_MIN/MAX endpoints exact, ushort 65535 non-normalized -> 65535.0, integer w == 1) discriminate
the correct formulas; the signed-normalization test was verified to fail against the GL 4.2 form.
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>
The GLImpl implementation existed but the exported symbol was still a
stub; Flywheel's indirect OIT framebuffer attaches array-texture layers
through it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a separate binding-point model to VertexArrayObject with eager
resolution into the flat per-attribute view backends already consume.
Implements glBindVertexBuffer(s), glVertexAttrib(I)Format,
glVertexAttribBinding, glVertexBindingDivisor and the DSA variants
(glVertexArrayAttribBinding, glVertexArrayBindingDivisor,
glVertexArrayVertexBuffers), fixing glVertexArrayVertexBuffer which
previously conflated binding index with attribute index. Multi-bind
(glBindBuffersBase/Range) loops over the single-bind entry points.
Needed by Flywheel's indirect backend (GlVertexArrayDSA setup path).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>