vtkBuffer Python Integration and NumPy Memory Safety#
Overview#
VTK now provides improved Python/NumPy interoperability through the vtkBuffer class
with full buffer protocol support. This enables zero-copy data sharing between VTK
arrays and NumPy, along with improved memory safety when VTK arrays reallocate.
vtkBuffer and the Python Buffer Protocol#
Direct Buffer Access#
You can now use vtkBuffer directly from Python with seamless NumPy integration.
The buffer protocol implementation enables zero-copy views of VTK memory:
import numpy as np
from vtkmodules.vtkCommonCore import vtkBuffer
# Create a typed buffer
buf = vtkBuffer['float64']()
buf.Allocate(100)
# Create a numpy array that shares memory with the buffer (zero-copy)
arr = np.asarray(buf)
# Modifications through numpy are reflected in the VTK buffer
arr[:] = np.linspace(0, 1, 100)
# Multiple numpy arrays can share the same buffer
arr2 = np.asarray(buf)
assert np.shares_memory(arr, arr2)
Supported Data Types#
The buffer protocol supports all standard VTK scalar types with automatic NumPy dtype mapping:
VTK Type |
NumPy dtype |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Memory Views#
Standard Python memoryview objects also work with vtkBuffer:
buf = vtkBuffer['int32']()
buf.Allocate(4)
m = memoryview(buf)
print(m.shape) # (4,)
print(m.itemsize) # 4
print(m.format) # 'i'
vtkAbstractBuffer Base Class#
A new vtkAbstractBuffer base class provides the interface for buffer protocol
support. It defines virtual methods for type-agnostic buffer access:
GetVoidBuffer()- Returns the raw buffer pointerGetNumberOfElements()- Returns the number of elementsGetDataType()- Returns the VTK type identifier (e.g.,VTK_FLOAT)GetDataTypeSize()- Returns the size in bytes of each element
This abstraction enables Python wrapping code to support any buffer type without knowing the specific template instantiation.
Accessing Buffers from Data Arrays#
You can now access the underlying vtkBuffer objects from VTK data arrays,
enabling direct buffer protocol access to array memory:
import numpy as np
from vtkmodules.vtkCommonCore import vtkFloatArray
# Create a VTK array
arr = vtkFloatArray()
arr.SetNumberOfComponents(3)
arr.SetNumberOfTuples(100)
# Get the underlying buffer (for AOS arrays)
buf = arr.GetBuffer()
# Create a numpy view of the raw buffer
raw_data = np.asarray(buf)
print(raw_data.shape) # (300,) - flattened view of 100 tuples x 3 components
Array Type Methods#
Different array types provide appropriate buffer access methods:
vtkAOSDataArrayTemplate (e.g.,
vtkFloatArray):GetBuffer()returns the single contiguous buffervtkSOADataArrayTemplate:
GetComponentBuffer(int comp)returns the buffer for a specific componentvtkScaledSOADataArrayTemplate:
GetComponentBuffer(int comp)returns the buffer for a specific component
Memory Safety with BufferChangedEvent#
The Problem#
When a NumPy array references VTK buffer memory and the VTK array reallocates
(e.g., due to Resize() or InsertNextTuple()), the NumPy array may point to
invalid memory, causing crashes or data corruption.
The Solution#
VTK data arrays now fire a BufferChangedEvent whenever they reallocate their
internal buffers. Python wrapper classes like VTKArray observe this event and
mark themselves as stale, raising a RuntimeError if accessed after the buffer
has changed.
When using vtkBuffer directly, you should obtain fresh buffer references after
any operation that might reallocate the array.
Example#
import numpy as np
from vtkmodules.vtkCommonCore import vtkFloatArray
import vtkmodules.numpy_interface.dataset_adapter as dsa
arr = vtkFloatArray()
arr.SetNumberOfValues(10)
arr.SetValue(0, 42.0)
# Create a VTKArray wrapper (recommended for safety)
va = dsa.vtkDataArrayToVTKArray(arr)
print(va[0]) # 42.0
# Resize the VTK array - this may reallocate the buffer
arr.SetNumberOfValues(1000)
# VTKArray detects the stale buffer and raises RuntimeError
try:
print(va[0]) # Raises RuntimeError
except RuntimeError:
print("Buffer changed - get a fresh reference")
# Get a fresh VTKArray reference
va_new = dsa.vtkDataArrayToVTKArray(arr)
print(va_new.shape) # (1000,)
Improved numpy_support and dataset_adapter#
numpy_support Module#
The numpy_to_vtk() function now stores NumPy array references on the buffer
rather than the data array. This ensures the NumPy memory stays valid even if
the VTK array is modified:
from vtkmodules.util.numpy_support import numpy_to_vtk
import numpy as np
data = np.array([1.0, 2.0, 3.0], dtype=np.float32)
vtk_arr = numpy_to_vtk(data)
# The numpy array reference is stored on the buffer, keeping memory valid
dataset_adapter Module#
The VTKArray class in the dataset adapter now stores a reference to the
underlying buffer, ensuring memory validity throughout the VTKArray’s lifetime:
import vtkmodules.numpy_interface.dataset_adapter as dsa
from vtkmodules.vtkFiltersSources import vtkSphereSource
sphere = vtkSphereSource()
sphere.Update()
# Wrap the output
wrapped = dsa.WrapDataObject(sphere.GetOutput())
# Access point coordinates as VTKArray (numpy subclass)
points = wrapped.Points
# The VTKArray holds a buffer reference, ensuring memory safety
print(points.shape)
Best Practices#
Use VTKArray for automatic safety: The dataset adapter’s
VTKArrayclass automatically detects buffer changes and raisesRuntimeErroron stale access.Get fresh references after modifications: If you modify a VTK array’s size, obtain a new buffer reference and NumPy view.
Use zero-copy when possible: Creating NumPy views via
np.asarray(buf)avoids data copying and provides the best performance.Observe BufferChangedEvent for custom wrappers: If you create custom Python wrappers around VTK arrays, observe
BufferChangedEventto detect reallocation.