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
- Set gl.PixelStore(PixelStorePname.PackAlignment, 1) and gl.PixelStore(PixelStorePname.UnpackAlignment, 1) before ReadPixels to rule out row-stride padding as the mismatch source.
- 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.
- 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.
- Print the index i and the surrounding pixel so you can tell whether the divergence is at row boundaries (alignment) or scattered (data corruption).
- 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
- Set PackAlignment and UnpackAlignment to 1 before TexImage2D and ReadPixels to rule out row padding.
- Match the TexImage2D internal format / pixel format / type exactly with the ReadPixels format / type.
- Watch for sRGB framebuffers/textures that silently gamma-convert on read/write.
- Verify TexSize * 4 is a multiple of the default 4-byte alignment (it is at width 64).
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
- MapBufferRange(PixelPackBuffer) returned null
- The framebuffer readback failed with {readbackError} while c
- Readback FBO is not complete
- Program link failed: {infoLog}
- {type} compile failed: {infoLog}
AI-assisted analysis of unoplatform/uno@0418340488 (2026-08-13).
Data as JSON: /api/errors/4d80f08e703d7155.
Report an issue: GitHub.