unoplatform/uno · error · Exception
MapBufferRange(PixelPackBuffer) returned null
Error message
MapBufferRange(PixelPackBuffer) returned null
What it means
Thrown on Android/iOS native GLES where gl.GetBufferSubData is unavailable, so the code maps the PIXEL_PACK_BUFFER with gl.MapBufferRange(ReadBit) to copy the read-back pixels to CPU memory. MapBufferRange returning a null pointer means the driver refused to expose the buffer's store — typically because no buffer of that target is bound, the buffer has no data store allocated, the GPU still owns it, or memory is exhausted. Dereferencing a null mapped pointer would crash, so the null is checked explicitly.
Source
Thrown at src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/PixelBuffersGlCanvasElement.cs:86
{
throw new Exception("Readback FBO is not complete");
}
_packPbo = gl.GenBuffer();
gl.BindBuffer(BufferTargetARB.PixelPackBuffer, _packPbo);
gl.BufferData(BufferTargetARB.PixelPackBuffer, (nuint)pixels.Length, null, BufferUsageARB.StreamRead);
gl.ReadPixels(0, 0, TexSize, TexSize, GLEnum.Rgba, GLEnum.UnsignedByte, (void*)0);
gl.BindFramebuffer(GLEnum.Framebuffer, 0);
// --- Round-trip self-check: pack PBO -> CPU, byte-exact against the source ---
var readBack = new byte[pixels.Length];
if (OperatingSystem.IsAndroid() || OperatingSystem.IsIOS())
{
// Native GLES has no glGetBufferSubData; map the pack PBO and copy it out.
var mapped = (byte*)gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, (nuint)readBack.Length, MapBufferAccessMask.ReadBit);
if (mapped is null)
{
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]}");
}View on GitHub (pinned to 0418340488)
Solutions
- Call gl.GetError() immediately before and after MapBufferRange to capture the exact code (0x505 out_of_memory, 0x502 invalid_operation when no buffer bound, 0x506 invalid_value when offset+length exceeds store).
- Insert gl.Flush() (or on WebGL2 a fence sync + ClientWaitSync) between ReadPixels and MapBufferRange so the GPU finishes the readback before the host maps the same store.
- Confirm a PIXEL_PACK_BUFFER is still bound at map time: re-issue gl.BindBuffer(BufferTargetARB.PixelPackBuffer, _packPbo) immediately before MapBufferRange.
- If the GLES version lacks MapBufferRange read support, fall back to a second ReadPixels directly into a CPU pointer (no PBO) on mobile, or use gl.ReadPixels with a PACK buffer only on desktop paths.
- Check the buffer actually has storage: BufferData was called with a null data pointer and StreamDraw/StreamRead usage — verify size is non-zero and matches pixels.Length.
Example fix
// before
var mapped = (byte*)gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, (nuint)readBack.Length, MapBufferAccessMask.ReadBit);
if (mapped is null) throw new Exception("MapBufferRange(PixelPackBuffer) returned null");
// after — flush, rebind, and capture the reason
if (OperatingSystem.IsAndroid() || OperatingSystem.IsIOS())
{
gl.Flush();
gl.BindBuffer(BufferTargetARB.PixelPackBuffer, _packPbo);
var pre = gl.GetError();
var mapped = (byte*)gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, (nuint)readBack.Length, MapBufferAccessMask.ReadBit);
if (mapped is null)
{
var why = gl.GetError();
throw new Exception($"MapBufferRange(PixelPackBuffer) returned null (pre=0x{(int)pre:x}, post=0x{(int)why:x})");
}
// ...
} Defensive patterns
Strategy: validation
Validate before calling
// Confirm a PIXEL_PACK_BUFFER is bound and has storage before mapping
gl.GetInteger(GLEnum.PixelPackBufferBinding, out int bound);
if (bound == 0) { /* nothing bound — MapBufferRange will return null */ gl.BindBuffer(BufferTargetARB.PixelPackBuffer, _packPbo); }
gl.Flush(); // ensure ReadPixels finished before mapping the same store Try / catch
try
{
var mapped = (byte*)gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, (nuint)len, MapBufferAccessMask.ReadBit);
if (mapped is null) throw new Exception("map null");
// ... copy ...
}
catch (Exception ex) when (ex.Message.Contains("MapBufferRange"))
{
// Fallback: read pixels directly into CPU memory (no PBO) on this mobile context
gl.BindBuffer(BufferTargetARB.PixelPackBuffer, 0);
fixed (byte* p = readBack) gl.ReadPixels(0, 0, TexSize, TexSize, GLEnum.Rgba, GLEnum.UnsignedByte, p);
} Prevention
- Insert gl.Flush() between ReadPixels-into-PBO and MapBufferRange so the GPU finishes the readback first.
- Re-bind the PIXEL_PACK_BUFFER immediately before mapping in case intervening state changes unbound it.
- On mobile, prefer the no-PBO ReadPixels path if MapBufferRange read support is uncertain.
- Always capture gl.GetError() before and after MapBufferRange to learn the refusal reason.
When it happens
Trigger: Only on OperatingSystem.IsAndroid() || OperatingSystem.IsIOS() branches (line 80-90): gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, readBack.Length, MapBufferAccessMask.ReadBit) returns null. Preceded by gl.BufferData(PixelPackBuffer, size, null, StreamRead) and gl.ReadPixels into offset 0, then gl.BindFramebuffer(Framebuffer, 0).
Common situations: The PIXEL_PACK_BUFFER got unbound between ReadPixels and MapBufferRange (e.g. the BindFramebuffer(0) on line 76 changed bound state but should not affect the PBO); the GLES context does not support mapping a buffer that is still the destination of an in-flight ReadPixels without a gl.Finish; MapBufferRange with only ReadBit is unsupported on some GLES2-only contexts (needs GL_EXT_map_buffer_range or GLES3); GL_OUT_OF_MEMORY on constrained mobile GPUs.
Related errors
- Readback FBO is not complete
- PBO round-trip mismatch at byte {i}: got {readBack[i]}, expe
- Offscreen 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/b9ca29500bdcc9e6.
Report an issue: GitHub.