{"record":{"id":"10fa1a13cfd50d7c","repo":"unoplatform/uno","slug":"getuniform-readback-mismatch-got-firstcomponent","errorCode":null,"errorMessage":"GetUniform readback mismatch: got {firstComponent}, expected {_palette[0]}","messagePattern":"GetUniform readback mismatch: got (.+?), expected (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/QuerySyncGlCanvasElement.cs","lineNumber":137,"sourceCode":"\t\t\tgl.BindVertexArray(_barsVao);\n\t\t\tgl.BindBuffer(BufferTargetARB.ArrayBuffer, _quadVbo);\n\t\t\tgl.VertexAttribPointer(0, 2, GLEnum.Float, false, 2 * sizeof(float), (void*)0);\n\t\t\tgl.EnableVertexAttribArray(0);\n\n\t\t\t_probeVao = gl.GenVertexArray();\n\t\t\tgl.BindVertexArray(_probeVao);\n\t\t\t_probeVbo = _quadVbo;\n\t\t\tgl.BindBuffer(BufferTargetARB.ArrayBuffer, _probeVbo);\n\t\t\tgl.VertexAttribPointer(0, 2, GLEnum.Float, false, 2 * sizeof(float), (void*)0);\n\t\t\tgl.EnableVertexAttribArray(0);\n\n\t\t\t// The palette never changes; upload once and read it back as a sanity check.\n\t\t\tgl.UseProgram(_barsProgram);\n\t\t\tgl.Uniform3(_uPaletteLoc, 4, new ReadOnlySpan<float>(_palette));\n\t\t\tgl.GetUniform(_barsProgram, _uPaletteLoc, out float firstComponent);\n\t\t\tif (Math.Abs(firstComponent - _palette[0]) > 0.001f)\n\t\t\t{\n\t\t\t\tthrow new Exception($\"GetUniform readback mismatch: got {firstComponent}, expected {_palette[0]}\");\n\t\t\t}\n\n\t\t\t// Program introspection: enumerate active uniforms and validate the program.\n\t\t\tgl.GetProgram(_barsProgram, ProgramPropertyARB.ActiveUniforms, out int activeUniforms);\n\t\t\tvar foundWave = false;\n\t\t\tfor (uint i = 0; i < activeUniforms; i++)\n\t\t\t{\n\t\t\t\tvar name = gl.GetActiveUniform(_barsProgram, i, out int size, out UniformType type);\n\t\t\t\t// Array uniforms report as \"uWave[0]\" with size = element count.\n\t\t\t\tfoundWave |= name.StartsWith(\"uWave\", StringComparison.Ordinal) && size == BarCount && type == UniformType.Float;\n\t\t\t}\n\t\t\tif (!foundWave)\n\t\t\t{\n\t\t\t\tthrow new Exception($\"GetActiveUniform did not report uWave[{BarCount}] (saw {activeUniforms} active uniforms)\");\n\t\t\t}\n\t\t\tgl.ValidateProgram(_barsProgram);\n\t\t\tgl.GetProgram(_barsProgram, ProgramPropertyARB.ValidateStatus, out int validateStatus);\n\t\t\tif (validateStatus != (int)GLEnum.True)","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/unoplatform/uno/blob/04183404888ea21b4e5bfa3493e8d5f15e972c3a/src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/QuerySyncGlCanvasElement.cs#L119-L155","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call gl.GetError() between Uniform3 and GetUniform to detect whether the upload itself failed (e.g. 0x502 invalid_operation if the location is wrong).","Confirm _uPaletteLoc is non-negative (-1 means the uniform was optimized out and Uniform3 is a no-op).","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.","If the driver reports the location for `uPalette[0]` vs `uPalette` differently, request the location with GetUniformLocation(program, \"uPalette[0]\") explicitly.","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)."],"exampleFix":"// before\ngl.Uniform3(_uPaletteLoc, 4, new ReadOnlySpan<float>(_palette));\ngl.GetUniform(_barsProgram, _uPaletteLoc, out float firstComponent);\nif (Math.Abs(firstComponent - _palette[0]) > 0.001f)\n    throw new Exception($\"GetUniform readback mismatch: got {firstComponent}, expected {_palette[0]}\");\n\n// after — verify location + read back the full array\nif (_uPaletteLoc < 0)\n    throw new Exception(\"uPalette uniform was optimized out by the compiler\");\ngl.Uniform3(_uPaletteLoc, 4, new ReadOnlySpan<float>(_palette));\nvar back = new float[12];\ngl.GetUniform(_barsProgram, _uPaletteLoc, back);\nfor (int i = 0; i < 12; i++)\n    if (Math.Abs(back[i] - _palette[i]) > 0.001f)\n        throw new Exception($\"GetUniform readback mismatch at [{i}]: got {back[i]}, expected {_palette[i]}\");","handlingStrategy":"validation","validationCode":"// Validate the uniform location is active before uploading, then read the full array back\nif (_uPaletteLoc < 0) return; // uniform optimized out; upload is a no-op\ngl.Uniform3(_uPaletteLoc, 4, new ReadOnlySpan<float>(_palette));\nvar back = new float[12];\ngl.GetUniform(_barsProgram, _uPaletteLoc, back);\nfor (int i = 0; i < 12; i++)\n    if (Math.Abs(back[i] - _palette[i]) > 0.001f) return; // driver quirk; do not throw","typeGuard":null,"tryCatchPattern":"try { /* readback assertion */ }\ncatch (Exception ex) when (ex.Message.Contains(\"GetUniform readback\"))\n{\n    // Non-fatal: the uniform upload likely worked; the readback semantics differ on this driver\n    App.MainWindow?.LogError(ex.Message);\n}","preventionTips":["Confirm GetUniformLocation returns >= 0 before Uniform3.","Read back the full array, not just the first component, to catch stride issues.","Tolerate driver quirks in GetUniform readback — the upload is what matters for rendering.","Drain GetError between upload and readback to detect upload failure."],"tags":["opengl","silk-net","uniform","uniform-array","glsl","driver-quirk"],"backgroundTag":null,"analyzedSha":"04183404888ea21b4e5bfa3493e8d5f15e972c3a","analyzedAt":"2026-08-13T21:08:11.651Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}