Skip to content

Patch release 2.18.0.2 - #341

Open
johningve wants to merge 5 commits into
masterfrom
MISC-2026-09-22-patch-release-2.18.0.2
Open

johningve wants to merge 5 commits into
masterfrom
MISC-2026-09-22-patch-release-2.18.0.2

Conversation

@johningve

Copy link
Copy Markdown
Contributor

No description provided.

torbsorb and others added 5 commits September 22, 2026 08:26
Expose each datamodel node's description through the pybind binding
(DataModelWrapper.h, alongside name/path/node_type) and emit it as a
class, enum-class and property docstring in the generated Python
wrappers, so the Python API reference gains the field-level
documentation the C++ and C# references already have.

The datamodel wrappers are regenerated in-tree against SDK 2.18 and
verified: _zivid exposes the descriptions, the wrappers compile, and
Sphinx autodoc renders them (Settings, Acquisition, Aperture, ...).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Building zivid-python in a pixi / conda-forge environment failed with
the compiler reporting missing Zivid headers. The cause is an interplay
between CMake and the compiler.

CMake maintains a list of "implicit include directories" gathered by
probing the compiler -- directories the compiler always searches. For a
normal GCC installation this includes /usr/include. CMake omits these
from the compiler invocation, since they are redundant and since naming
them explicitly would disturb the order in which the compiler searches
its own directories.

When a compiler has different implicit include directories, say
<prefix>/sysroot/usr/include, CMake treats them as a sysroot alias of
/usr/include and still omits /usr/include from the invocation. But such
a compiler does not implicitly read /usr/include, so the Zivid headers
installed there are never found. This has been CMake's behavior since
3.14 and is unchanged as of 4.4, so it is not a regression to wait out
or pin around.

This detects the situation and re-adds /usr/include with -idirafter.
That flag appends the directory after the compiler's own directories, so
the sysroot's libc and libstdc++ headers keep priority; plain -I would
reintroduce the very header shadowing CMake was guarding against.

Note that an environment which replaces the system compiler toolchain
arguably should not discover a Zivid CMake package installed outside the
environment in the first place. Either CMAKE_FIND_ROOT_PATH together
with CMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY, or
CMAKE_IGNORE_PREFIX_PATH="/usr;/", makes the system-installed package
correctly invisible from inside such an environment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
device_colors() accepted only the four 8-bit color formats, while the
equivalent accessors on Frame2D.image_device_array and organized
PointCloud.device_image already accepted RGBAF. The Zivid SDK has always
supported it; only the binding and the allow-list entry were missing.

This matters to consumers feeding unorganized point clouds into GPU
frameworks that want linear float32 colors from 0 to 1, which otherwise
had to convert with astype(float32) / 255.0 on every capture.

RGBAF is 4-channel, so callers that want a packed 3-channel float32
buffer still have to drop the alpha channel themselves.

test_unorganized_device_colors_rejects_rgbaf asserted the old
restriction, so it is replaced by a positive test. RGB takes over as the
rejected-format case, since the accessor still has to reject something.
A third test compares RGBAF against RGBA / 255.0 on the GPU to confirm
the two formats agree; it needs CUDA and torch, so it skips elsewhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DeviceArray implemented __cuda_array_interface__ but not DLPack, so a
consumer that wants a capsule directly had to build a DLManagedTensor by
hand with ctypes, the way the CaptureAndConvertToDlpackTensorOnCuda
sample does. That was never a deliberate choice; nobody had implemented
it.

__dlpack__ and __dlpack_device__ are added in the pybind layer, where
the capsule holds a copy of the reference-counted DeviceArray and drops
it from the capsule deleter once the consumer is done. Both methods
reach every device-array type through the two existing
addCommon*DeviceArrayMethods helpers.

The consumer's stream argument is accepted and ignored: the buffer was
already ordered against the stream or queue passed at acquisition.

The device ordinal comes from cuPointerGetAttribute, resolved from the
CUDA driver already loaded into the process, because neither
DeviceArray nor ComputeDevice exposes it. The resolved pointer is typed
__stdcall on Windows to match the CUDA driver API's CUDAAPI convention,
since calling it as cdecl would corrupt the stack on 32-bit Windows.

One deviation from offensive programming to note for review: the capsule
destructor returns without freeing when the capsule is no longer named
"dltensor". That is not a fallback but the DLPack hand-off protocol -- a
consumer that takes ownership renames the capsule to "used_dltensor",
and the producer must then not free the tensor. Freeing unconditionally
would double-free every imported tensor.

test_device_array_dlpack_protocol_without_a_consumer covers the protocol
without PyTorch, so the ordinal lookup, capsule creation and the capsule
deleter are exercised in CI, where the PyTorch CUDA tests are skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@johningve
johningve requested a review from a team as a code owner September 22, 2026 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants