{"record":{"id":"b9ca29500bdcc9e6","repo":"unoplatform/uno","slug":"mapbufferrange-pixelpackbuffer-returned-null","errorCode":null,"errorMessage":"MapBufferRange(PixelPackBuffer) returned null","messagePattern":"MapBufferRange\\(PixelPackBuffer\\) returned null","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/PixelBuffersGlCanvasElement.cs","lineNumber":86,"sourceCode":"\t\t\t{\n\t\t\t\tthrow new Exception(\"Readback FBO is not complete\");\n\t\t\t}\n\n\t\t\t_packPbo = gl.GenBuffer();\n\t\t\tgl.BindBuffer(BufferTargetARB.PixelPackBuffer, _packPbo);\n\t\t\tgl.BufferData(BufferTargetARB.PixelPackBuffer, (nuint)pixels.Length, null, BufferUsageARB.StreamRead);\n\t\t\tgl.ReadPixels(0, 0, TexSize, TexSize, GLEnum.Rgba, GLEnum.UnsignedByte, (void*)0);\n\t\t\tgl.BindFramebuffer(GLEnum.Framebuffer, 0);\n\n\t\t\t// --- Round-trip self-check: pack PBO -> CPU, byte-exact against the source ---\n\t\t\tvar readBack = new byte[pixels.Length];\n\t\t\tif (OperatingSystem.IsAndroid() || OperatingSystem.IsIOS())\n\t\t\t{\n\t\t\t\t// Native GLES has no glGetBufferSubData; map the pack PBO and copy it out.\n\t\t\t\tvar mapped = (byte*)gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, (nuint)readBack.Length, MapBufferAccessMask.ReadBit);\n\t\t\t\tif (mapped is null)\n\t\t\t\t{\n\t\t\t\t\tthrow new Exception(\"MapBufferRange(PixelPackBuffer) returned null\");\n\t\t\t\t}\n\t\t\t\tnew ReadOnlySpan<byte>(mapped, readBack.Length).CopyTo(readBack);\n\t\t\t\tgl.UnmapBuffer(BufferTargetARB.PixelPackBuffer);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfixed (byte* p = readBack)\n\t\t\t\t{\n\t\t\t\t\tgl.GetBufferSubData(BufferTargetARB.PixelPackBuffer, 0, (nuint)readBack.Length, p);\n\t\t\t\t}\n\t\t\t}\n\t\t\tgl.BindBuffer(BufferTargetARB.PixelPackBuffer, 0);\n\t\t\tfor (int i = 0; i < pixels.Length; i++)\n\t\t\t{\n\t\t\t\tif (readBack[i] != pixels[i])\n\t\t\t\t{\n\t\t\t\t\tthrow new Exception($\"PBO round-trip mismatch at byte {i}: got {readBack[i]}, expected {pixels[i]}\");\n\t\t\t\t}","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/unoplatform/uno/blob/04183404888ea21b4e5bfa3493e8d5f15e972c3a/src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/PixelBuffersGlCanvasElement.cs#L68-L104","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nvar mapped = (byte*)gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, (nuint)readBack.Length, MapBufferAccessMask.ReadBit);\nif (mapped is null) throw new Exception(\"MapBufferRange(PixelPackBuffer) returned null\");\n\n// after — flush, rebind, and capture the reason\nif (OperatingSystem.IsAndroid() || OperatingSystem.IsIOS())\n{\n    gl.Flush();\n    gl.BindBuffer(BufferTargetARB.PixelPackBuffer, _packPbo);\n    var pre = gl.GetError();\n    var mapped = (byte*)gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, (nuint)readBack.Length, MapBufferAccessMask.ReadBit);\n    if (mapped is null)\n    {\n        var why = gl.GetError();\n        throw new Exception($\"MapBufferRange(PixelPackBuffer) returned null (pre=0x{(int)pre:x}, post=0x{(int)why:x})\");\n    }\n    // ...\n}","handlingStrategy":"validation","validationCode":"// Confirm a PIXEL_PACK_BUFFER is bound and has storage before mapping\ngl.GetInteger(GLEnum.PixelPackBufferBinding, out int bound);\nif (bound == 0) { /* nothing bound — MapBufferRange will return null */ gl.BindBuffer(BufferTargetARB.PixelPackBuffer, _packPbo); }\ngl.Flush(); // ensure ReadPixels finished before mapping the same store","typeGuard":null,"tryCatchPattern":"try\n{\n    var mapped = (byte*)gl.MapBufferRange(BufferTargetARB.PixelPackBuffer, 0, (nuint)len, MapBufferAccessMask.ReadBit);\n    if (mapped is null) throw new Exception(\"map null\");\n    // ... copy ...\n}\ncatch (Exception ex) when (ex.Message.Contains(\"MapBufferRange\"))\n{\n    // Fallback: read pixels directly into CPU memory (no PBO) on this mobile context\n    gl.BindBuffer(BufferTargetARB.PixelPackBuffer, 0);\n    fixed (byte* p = readBack) gl.ReadPixels(0, 0, TexSize, TexSize, GLEnum.Rgba, GLEnum.UnsignedByte, p);\n}","preventionTips":["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."],"tags":["opengl","silk-net","pbo","map-buffer","android","ios","gl-es","mobile"],"backgroundTag":null,"analyzedSha":"04183404888ea21b4e5bfa3493e8d5f15e972c3a","analyzedAt":"2026-08-13T21:08:11.651Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}