unoplatform/uno · error · Exception

PBO round-trip mismatch at byte {i}: got {readBack[i]}, expe

Error message

PBO round-trip mismatch at byte {i}: got {readBack[i]}, expected {pixels[i]}

What it means

The sample performs a full byte-exact round-trip self-check: a known checkerboard is uploaded to a texture via a PIXEL_UNPACK_BUFFER, then read back via ReadPixels into a PIXEL_PACK_BUFFER, then copied to CPU memory, and every byte is compared against the original. This throw fires on the first byte that differs, so it exposes a silent data corruption somewhere in the upload/readback pipeline — most often a packing/alignment, format, or colorspace transformation the GL driver applied.

Source

Thrown at src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/PixelBuffersGlCanvasElement.cs:103

				{
					throw new Exception("MapBufferRange(PixelPackBuffer) returned null");
				}
				new ReadOnlySpan<byte>(mapped, readBack.Length).CopyTo(readBack);
				gl.UnmapBuffer(BufferTargetARB.PixelPackBuffer);
			}
			else
			{
				fixed (byte* p = readBack)
				{
					gl.GetBufferSubData(BufferTargetARB.PixelPackBuffer, 0, (nuint)readBack.Length, p);
				}
			}
			gl.BindBuffer(BufferTargetARB.PixelPackBuffer, 0);
			for (int i = 0; i < pixels.Length; i++)
			{
				if (readBack[i] != pixels[i])
				{
					throw new Exception($"PBO round-trip mismatch at byte {i}: got {readBack[i]}, expected {pixels[i]}");
				}
			}

			// --- Geometry + shader ---
			_vao = gl.GenVertexArray();
			gl.BindVertexArray(_vao);
			_vbo = gl.GenBuffer();
			gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
			var quad = new float[]
			{
				-1f, -1f,     0f, 0f,
				 1f, -1f,     1f, 0f,
				 1f,  1f,     1f, 1f,
				-1f, -1f,     0f, 0f,
				 1f,  1f,     1f, 1f,
				-1f,  1f,     0f, 1f,
			};
			gl.BufferData(BufferTargetARB.ArrayBuffer, new ReadOnlySpan<float>(quad), BufferUsageARB.StaticDraw);

View on GitHub (pinned to 0418340488)

Solutions

  1. Set gl.PixelStore(PixelStorePname.PackAlignment, 1) and gl.PixelStore(PixelStorePname.UnpackAlignment, 1) before ReadPixels to rule out row-stride padding as the mismatch source.
  2. Inspect which bytes differ: if every component of every pixel is shifted by a constant, suspect BGRA/RGBA swap or a Y-flip; if only alpha differs, suspect premultiply; if values are clamped or gamma-coded, suspect sRGB.
  3. Confirm the texture internal format matches the readback format (TexImage2D used InternalFormat.Rgba8 + GLEnum.Rgba/GLEnum.UnsignedByte and ReadPixels used the same pair) — any mismatch in sized/unsized format pairing triggers silent conversion.
  4. Print the index i and the surrounding pixel so you can tell whether the divergence is at row boundaries (alignment) or scattered (data corruption).
  5. Verify no GL error was raised by TexImage2D/ReadPixels before the compare — a pending error usually means the readback wrote nothing and readBack is all zeros.

Example fix

// before
for (int i = 0; i < pixels.Length; i++)
{
    if (readBack[i] != pixels[i])
        throw new Exception($"PBO round-trip mismatch at byte {i}: got {readBack[i]}, expected {pixels[i]}");
}

// after — pin alignment, capture context on first divergence
gl.PixelStore(PixelStorePname.PackAlignment, 1);
gl.PixelStore(PixelStorePname.UnpackAlignment, 1);
for (int i = 0; i < pixels.Length; i++)
{
    if (readBack[i] != pixels[i])
    {
        int px = (i / 4) % TexSize, py = (i / 4) / TexSize, ch = i % 4;
        throw new Exception($"PBO mismatch at byte {i} (px={px},py={py},ch={ch}): got {readBack[i]}, expected {pixels[i]}");
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Set explicit pack/unpack alignment and verify no GL error preceded the readback
gl.PixelStore(PixelStorePname.PackAlignment, 1);
gl.PixelStore(PixelStorePname.UnpackAlignment, 1);
if (gl.GetError() != GLEnum.NoError) return; // an earlier error means readback is unreliable

Try / catch

try { /* round-trip compare */ }
catch (Exception ex) when (ex.Message.Contains("round-trip mismatch"))
{
    // Log the byte offset + pixel coordinates to diagnose alignment vs colorspace
    App.MainWindow?.LogError(ex.Message);
    _roundTripOk = false; // continue rendering with the (possibly mis-converted) texture
}

Prevention

When it happens

Trigger: After the readBack array is filled (either via MapBufferRange on mobile or GetBufferSubData on desktop), the loop at lines 99-105 finds readBack[i] != pixels[i] for some i. The pixels are RGBA8 UnsignedByte, TexSize=64, no PACK/UNPACK alignment is set so GL defaults apply (4-byte row alignment — 64*4 = 256 bytes per row is already 4-aligned).

Common situations: The GL driver premultiplies alpha or applies sRGB conversion because the framebuffer/texture is treated as sRGB-capable; BGRA vs RGBA byte order on some desktop GL drivers; a Y-flip between TexImage2D origin (bottom-left) and the readback; GL_PACK_ALIGNMENT defaulting to 4 conflicting with row stride on non-4-aligned widths (not the case at width 64, but would be at other widths); an earlier silent GL error left the texture partially uninitialized so ReadPixels returns zero/garbage.

Related errors


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