# macOS: crash in glBufferSubData when a single buffer upload is larger than 2 GB (Apple OpenGL driver), proposal to chunk uploads in vtkOpenGLBufferObject

**URL:** <https://discourse.vtk.org/t/macos-crash-in-glbuffersubdata-when-a-single-buffer-upload-is-larger-than-2-gb-apple-opengl-driver-proposal-to-chunk-uploads-in-vtkopenglbufferobject/16573>\
**Category:** Development\
**Created:** [October 28, 2026, 2:17pm UTC](https://discourse.vtk.org/t/macos-crash-in-glbuffersubdata-when-a-single-buffer-upload-is-larger-than-2-gb-apple-opengl-driver-proposal-to-chunk-uploads-in-vtkopenglbufferobject/16573 "2026-10-28T14:17:56Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![hherhold](https://discourse.vtk.org/letter_avatar_proxy/v4/letter/h/c6cbf5/32.png) [@hherhold](https://discourse.vtk.org/u/hherhold)\
**Post date:** [October 28, 2026, 2:17pm UTC](https://discourse.vtk.org/t/macos-crash-in-glbuffersubdata-when-a-single-buffer-upload-is-larger-than-2-gb-apple-opengl-driver-proposal-to-chunk-uploads-in-vtkopenglbufferobject/16573/1 "2026-10-28T14:17:56Z")

</div>

While using 3D Slicer, I ran into an issue with VTK when using very large meshes on Apple Silicon OpenGL. I realize OpenGL is deprecated on apple silicon, but I think this is a relatively straightforward work-around for an issue with Apple’s OpenGL driver. Below is Claude’s summary and suggestions, let me know what you think and I’m happy to open a merge request.

(And thanks much for your help with the earlier issue!)

**Summary**

On macOS (Apple Silicon), rendering a polydata whose index or vertex buffer is larger than 2³¹ bytes crashes with `EXC_BAD_ACCESS` in `_platform_memmove` inside Apple’s OpenGL driver. The crash happens in `glBufferSubData` when a single call transfers more than 2³¹ bytes. Splitting the same upload into several smaller `glBufferSubData` calls works. I’d like to propose doing that in `vtkOpenGLBufferObject::UploadRangeInternal`, and to ask whether that’s an acceptable approach before opening a merge request.

I hit this in 3D Slicer with a segmentation surface of ~234M triangles (a 2.8 GB index buffer). The Slicer developers suggested fixing it in VTK.

**Reproducer (pure VTK, stock 9.7.1 wheel from PyPI)**

A 10000 × 10000 `vtkPlaneSource` is 100M quads, which the mapper triangulates into 600M indices, ~2.4 GB uploaded in one call. It needs ~13 GB of RAM.

```python
import vtkmodules.vtkRenderingOpenGL2 # noqa: F401
from vtkmodules.vtkFiltersSources import vtkPlaneSource
from vtkmodules.vtkRenderingCore import (
    vtkActor, vtkPolyDataMapper, vtkRenderer, vtkRenderWindow,
)

plane = vtkPlaneSource()
plane.SetResolution(10000, 10000)

mapper = vtkPolyDataMapper()
mapper.SetInputConnection(plane.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)

renderer = vtkRenderer()
renderer.AddActor(actor)
window = vtkRenderWindow()
window.AddRenderer(renderer)
window.SetSize(400, 400)
renderer.ResetCamera()
window.Render() # crashes in glBufferSubData on macOS
print("Rendered without crashing")

```

It crashes within a few seconds:

```auto
EXC_BAD_ACCESS (KERN_PROTECTION_FAILURE)
  libsystem_platform.dylib _platform_memmove
  AppleMetalOpenGLRenderer gldBufferSubData
  GLEngine glBufferSubData_Exec
  libvtkRenderingOpenGL2.dylib vtkOpenGLBufferObject::UploadInternal(...)
  libvtkRenderingOpenGL2.dylib vtkOpenGLIndexBufferObject::CreateTriangleIndexBuffer(...)
  libvtkRenderingOpenGL2.dylib vtkOpenGLPolyDataMapper::BuildIBO(...)

```

**It’s the driver, not VTK**

A standalone CGL / OpenGL 3.2 core program with no VTK shows:

- `glBufferData` of 2.4 GB succeeds: `glGetError()` returns 0 and `GL_BUFFER_SIZE` reports the full size.
- A single `glBufferSubData` of exactly 2³¹ bytes works. Anything larger crashes (tested 2³¹ + 4096 and 2.4 GB).
- The same 2.4 GB uploaded as `glBufferSubData` calls of ≤1 GB works, `glGetError()` stays clean, and reading the buffer back with `glMapBufferRange` matches the source byte for byte.

It isn’t a real hardware limit: `MTLDevice.maxBufferLength` on this machine is ~86 GB. The driver also reports no error before crashing, so VTK has nothing to check for.

> **Standalone OpenGL test program**
>
> ```c
> // Build: clang -w gl_buffersubdata_2gb.c -framework OpenGL -o gl_buffersubdata_2gb
> // Usage: ./gl_buffersubdata_2gb <total bytes> [chunk bytes]
> #define GL_SILENCE_DEPRECATION
> #include <OpenGL/OpenGL.h>
> #include <OpenGL/gl3.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> 
> int main(int argc, char** argv)
> {
> setvbuf(stdout, NULL, _IONBF, 0);
> CGLPixelFormatAttribute attrs[] = { kCGLPFAOpenGLProfile,
> (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core, kCGLPFAAccelerated, 0 };
> CGLPixelFormatObj pf;
> GLint npf;
> CGLChoosePixelFormat(attrs, &pf, &npf);
> CGLContextObj ctx;
> CGLCreateContext(pf, NULL, &ctx);
> CGLSetCurrentContext(ctx);
> 
> size_t size = strtoull(argv[1], NULL, 10);
> size_t chunk = argc > 2 ? strtoull(argv[2], NULL, 10) : size;
> unsigned char* data = malloc(size);
> for (size_t i = 0; i < size; i += 4096)
> {
> data[i] = (unsigned char)(i >> 12);
> }
> data[size - 1] = 0xAB;
> 
> GLuint buffer;
> glGenBuffers(1, &buffer);
> glBindBuffer(GL_ARRAY_BUFFER, buffer);
> glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)size, NULL, GL_STATIC_DRAW);
> GLint64 reported = 0;
> glGetBufferParameteri64v(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &reported);
> printf("glBufferData(%zu): error 0x%x, GL_BUFFER_SIZE %lld\n", size, glGetError(), reported);
> 
> for (size_t offset = 0; offset < size; offset += chunk)
> {
> size_t len = size - offset < chunk ? size - offset : chunk;
> glBufferSubData(GL_ARRAY_BUFFER, (GLintptr)offset, (GLsizeiptr)len, data + offset);
> }
> printf("glBufferSubData in %zu-byte chunks: error 0x%x", chunk, glGetError());
> unsigned char* mapped = glMapBufferRange(GL_ARRAY_BUFFER, 0, (GLsizeiptr)size, GL_MAP_READ_BIT);
> printf(", read-back %s\n", mapped && memcmp(mapped, data, size) == 0 ? "matches" : "MISMATCH");
> return 0;
> }
> 
> ```

**Proposed change**

All polydata VBO and IBO uploads go through `UploadRangeInternal`, so chunking there covers them:

```diff
--- a/Rendering/OpenGL2/vtkOpenGLBufferObject.cxx
+++ b/Rendering/OpenGL2/vtkOpenGLBufferObject.cxx
@@ -5,6 +5,8 @@
 #include "vtk_glad.h"
 
+#include <algorithm>
+
@@ -260,8 +262,16 @@ bool vtkOpenGLBufferObject::UploadRangeInternal(
   glBindBuffer(this->Internal->Type, this->Internal->Handle);
   vtkDebugMacro(<< "glBufferSubData: "
                 << "(offset: " << offset << ", size: " << size << ")");
- glBufferSubData(
- this->Internal->Type, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), buffer);
+ constexpr ptrdiff_t maxChunkSize = ptrdiff_t(1) << 30;
+ const char* data = static_cast<const char*>(buffer);
+ for (ptrdiff_t chunkOffset = 0; chunkOffset < size; chunkOffset += maxChunkSize)
+ {
+ const ptrdiff_t chunkSize = std::min(maxChunkSize, size - chunkOffset);
+ glBufferSubData(this->Internal->Type, static_cast<GLintptr>(offset + chunkOffset),
+ static_cast<GLsizeiptr>(chunkSize), data + chunkOffset);
+ }
   this->Dirty = false;

```

Uploads of 1 GB or less are unchanged (one call). With this patch, the reproducer renders correctly: the 200M-triangle plane gives a pixel-identical image to the same plane at 100 × 100 resolution. I’ve been running it in my Slicer builds since September, and it fixes the original crash there.

In the interest of full disclosure: while retesting the standalone program, one run of the _chunked_ 2.4 GB upload also crashed in `gldBufferSubData`. I couldn’t reproduce that in ~30 further runs (standalone and through VTK), including immediately after deliberate crashes, so I don’t know whether it’s a rare driver issue or something about that particular run.

**Questions**

1. Is chunking in `UploadRangeInternal` acceptable, or would you prefer it only on Apple (`#ifdef __APPLE__ `)? It’s harmless elsewhere, since buffers up to 1 GB take the same single call as before.
2. Any preference for the chunk size? 1 GB is well under the 2³¹-byte limit; I didn’t find a performance difference.
3. Should I also report it to Apple (Feedback Assistant)? Given OpenGL’s deprecated status on macOS, I’m not expecting a driver fix.

Environment: Apple M5 Max, 128 GB RAM, macOS 26.5 (25F71), VTK 9.7.1 (PyPI) and Slicer’s VTK fork (9.6.1).

---

<div class="post-metadata">

**Author:** ![jaswantp](https://discourse.vtk.org/user_avatar/discourse.vtk.org/jaswantp/32/10046_2.png) [@jaswantp](https://discourse.vtk.org/u/jaswantp)\
**Post date:** [October 28, 2026, 2:54pm UTC](https://discourse.vtk.org/t/macos-crash-in-glbuffersubdata-when-a-single-buffer-upload-is-larger-than-2-gb-apple-opengl-driver-proposal-to-chunk-uploads-in-vtkopenglbufferobject/16573/2 "2026-10-28T14:54:18Z")

</div>

Hello @hherhold thanks for sharing your findings.

Yes, an apple only path that does this is fine. I’d prefer (1 \<\< 31) - 1 if that works instead.
