unoplatform/uno · error · Exception

GetUniform readback mismatch: got {firstComponent}, expected

Error message

GetUniform readback mismatch: got {firstComponent}, expected {_palette[0]}

What it means

QuerySyncGlCanvasElement uploads a 4-element vec3 palette via gl.Uniform3(loc, 4, _palette) then immediately reads the first component back via gl.GetUniform and asserts it matches the uploaded value within 0.001. A mismatch means the uniform upload or readback is silently wrong — the GL driver stored or returned a different value, which would corrupt the bar coloring.

Source

Thrown at src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/QuerySyncGlCanvasElement.cs:137

			gl.BindVertexArray(_barsVao);
			gl.BindBuffer(BufferTargetARB.ArrayBuffer, _quadVbo);
			gl.VertexAttribPointer(0, 2, GLEnum.Float, false, 2 * sizeof(float), (void*)0);
			gl.EnableVertexAttribArray(0);

			_probeVao = gl.GenVertexArray();
			gl.BindVertexArray(_probeVao);
			_probeVbo = _quadVbo;
			gl.BindBuffer(BufferTargetARB.ArrayBuffer, _probeVbo);
			gl.VertexAttribPointer(0, 2, GLEnum.Float, false, 2 * sizeof(float), (void*)0);
			gl.EnableVertexAttribArray(0);

			// The palette never changes; upload once and read it back as a sanity check.
			gl.UseProgram(_barsProgram);
			gl.Uniform3(_uPaletteLoc, 4, new ReadOnlySpan<float>(_palette));
			gl.GetUniform(_barsProgram, _uPaletteLoc, out float firstComponent);
			if (Math.Abs(firstComponent - _palette[0]) > 0.001f)
			{
				throw new Exception($"GetUniform readback mismatch: got {firstComponent}, expected {_palette[0]}");
			}

			// Program introspection: enumerate active uniforms and validate the program.
			gl.GetProgram(_barsProgram, ProgramPropertyARB.ActiveUniforms, out int activeUniforms);
			var foundWave = false;
			for (uint i = 0; i < activeUniforms; i++)
			{
				var name = gl.GetActiveUniform(_barsProgram, i, out int size, out UniformType type);
				// Array uniforms report as "uWave[0]" with size = element count.
				foundWave |= name.StartsWith("uWave", StringComparison.Ordinal) && size == BarCount && type == UniformType.Float;
			}
			if (!foundWave)
			{
				throw new Exception($"GetActiveUniform did not report uWave[{BarCount}] (saw {activeUniforms} active uniforms)");
			}
			gl.ValidateProgram(_barsProgram);
			gl.GetProgram(_barsProgram, ProgramPropertyARB.ValidateStatus, out int validateStatus);
			if (validateStatus != (int)GLEnum.True)

View on GitHub (pinned to 0418340488)

Solutions

  1. Call gl.GetError() between Uniform3 and GetUniform to detect whether the upload itself failed (e.g. 0x502 invalid_operation if the location is wrong).
  2. Confirm _uPaletteLoc is non-negative (-1 means the uniform was optimized out and Uniform3 is a no-op).
  3. Read back the full array with GetUniformfv-style calls (4 vec3 = 12 floats) and compare element-by-element, not just the first component — a stride mismatch shows up at elements 1-3.
  4. If the driver reports the location for `uPalette[0]` vs `uPalette` differently, request the location with GetUniformLocation(program, "uPalette[0]") explicitly.
  5. On WebGL2/GLES, verify array uniform upload stride — Uniform3 with count=4 expects a tightly-packed 12-float span, which _palette is (3*4 floats).

Example fix

// before
gl.Uniform3(_uPaletteLoc, 4, new ReadOnlySpan<float>(_palette));
gl.GetUniform(_barsProgram, _uPaletteLoc, out float firstComponent);
if (Math.Abs(firstComponent - _palette[0]) > 0.001f)
    throw new Exception($"GetUniform readback mismatch: got {firstComponent}, expected {_palette[0]}");

// after — verify location + read back the full array
if (_uPaletteLoc < 0)
    throw new Exception("uPalette uniform was optimized out by the compiler");
gl.Uniform3(_uPaletteLoc, 4, new ReadOnlySpan<float>(_palette));
var back = new float[12];
gl.GetUniform(_barsProgram, _uPaletteLoc, back);
for (int i = 0; i < 12; i++)
    if (Math.Abs(back[i] - _palette[i]) > 0.001f)
        throw new Exception($"GetUniform readback mismatch at [{i}]: got {back[i]}, expected {_palette[i]}");
Defensive patterns

Strategy: validation

Validate before calling

// Validate the uniform location is active before uploading, then read the full array back
if (_uPaletteLoc < 0) return; // uniform optimized out; upload is a no-op
gl.Uniform3(_uPaletteLoc, 4, new ReadOnlySpan<float>(_palette));
var back = new float[12];
gl.GetUniform(_barsProgram, _uPaletteLoc, back);
for (int i = 0; i < 12; i++)
    if (Math.Abs(back[i] - _palette[i]) > 0.001f) return; // driver quirk; do not throw

Try / catch

try { /* readback assertion */ }
catch (Exception ex) when (ex.Message.Contains("GetUniform readback"))
{
    // Non-fatal: the uniform upload likely worked; the readback semantics differ on this driver
    App.MainWindow?.LogError(ex.Message);
}

Prevention

When it happens

Trigger: After gl.UseProgram(_barsProgram) + gl.Uniform3(_uPaletteLoc, 4, palette) at line 133, then gl.GetUniform(_barsProgram, _uPaletteLoc, out float firstComponent) at line 134. The assertion Math.Abs(firstComponent - _palette[0]) > 0.001f at line 135 fires.

Common situations: GetUniform reading an array uniform returns only the first array element — but if _uPaletteLoc resolved to the array root vs the [0] element differently across drivers, the read value differs; the uniform was optimized out by the GLSL compiler (uPalette is used in the vertex shader so it should stay active, but a driver that prunes aggressively could); a silent GL error from the Uniform3 call left the value unchanged; the vec3 array stride in the upload does not match what the driver expects.

Related errors


AI-assisted analysis of unoplatform/uno@0418340488 (2026-08-13). Data as JSON: /api/errors/10fa1a13cfd50d7c. Report an issue: GitHub.