Skip to content

💥 Support reading into CuPy arrays via cog3pio xarray backend#71

Draft
weiji14 wants to merge 15 commits into
mainfrom
xarray_cudacog
Draft

💥 Support reading into CuPy arrays via cog3pio xarray backend#71
weiji14 wants to merge 15 commits into
mainfrom
xarray_cudacog

Conversation

@weiji14

@weiji14 weiji14 commented Feb 13, 2026

Copy link
Copy Markdown
Owner

Proof of concept interface to read TIFF data into an xarray.DataArray via Cog3pioBackendEntrypoint. Handles reshaping of the 1-D array returned from CudaCogReader into a proper 3-D array of shape (Channels, Height, Width).

Preview at https://cog3pio--71.org.readthedocs.build/en/71/api/#cog3pio.xarray_backend.Cog3pioBackendEntrypoint

Example usage:

import cog3pio
import cupy as cp

da: xr.DataArray = xr.open_dataarray(filename_or_obj="TCI.tif", engine="cog3pio", device=(2, 0))
assert isinstance(da, cp.ndarray)

TODO:

Notes:

  • The use_cuda flag doesn't feel very ergonomic or future proof. Am thinking that there could be some way to conform better to the array API standard, by letting users to set a device which is passed to the from_dlpack() method somehow edit: no, device should be set when DLPack object is instantiated. This means users can control where the TIFF data is decoded, be it CPU or a CUDA GPU (with a specific GPU id?)
  • Want to have a way to default to GPU-based decoding somehow, with a fallback to CPU. Also should default to per-thread default stream instead of legacy default stream.

References:

Closes #26

Get list of x and y coordinates for the TIFF image from Python-side.
Rough attempt to include CudaCogReader as the default reader inside Cog3pioBackendEntrypoint, via a `use_cuda` flag that defaults to True. Handle the reshaping of the cupy.ndarray from 1-D to 3-D based on info from .xy_coords method. Temporarily fallback to CPU-based reader in unit test to work on CI.
@weiji14 weiji14 added this to the 0.1.0 milestone Feb 13, 2026
@weiji14 weiji14 self-assigned this Feb 13, 2026
@weiji14 weiji14 added the feature New feature or request label Feb 13, 2026
@codspeed-hq

codspeed-hq Bot commented Feb 14, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 7 untouched benchmarks


Comparing xarray_cudacog (60f5605) with main (bd9b65a)

Open in CodSpeed

Allow setting the device_id to decode TIFFs on a specific CUDA  GPU device (default is device_id=0). Also added a missing `import cog3pio` in test_xarray_backend.py
Trying to conform to DLPack standard a bit better, by having the device be set using a (device_type, device_id) tuple. Note that device_id is being passed into the CudaCogReader struct/class, rather than at xp.from_dlpack whose `device` parameter expects a library-specific device object. Considered supported library-specific device objects (e.g. cupy.cuda.Device, torch.device), but they have different attributes, so using this DLPack device tuple that should be more forward compatible. Updated test_xarray_backend_open_dataarray unit test to ensure both CPU and CUDA decode paths are tested.
weiji14 added a commit that referenced this pull request Feb 17, 2026
dl_device will be handled in #71, copy another time.
weiji14 added a commit that referenced this pull request Feb 17, 2026
* ➕ Add pyo3-stub-gen and pyo3-stub-gen-derive

Stub file (*.pyi) generator for PyO3! Crates at https://crates.io/crates/pyo3-stub-gen and https://crates.io/crates/pyo3-stub-gen-derive

* 🔨 Create python stub file generator script

Xref https://github.com/Jij-Inc/pyo3-stub-gen/tree/0.19.0?tab=readme-ov-file#generate-a-stub-file. Also need to bump MSRV from 1.85.0 to 1.91.0 to fix complain about macro `gen_stub_pyclass` and `gen_stub_pymethods` having some "item is stable in a `const` context since `1.91.0`".

* ⬆️ Bump MSRV from 1.85.0 to 1.91.0

To satisfy `gen_stub_pyclass` and `gen_stub_pymethods` macros.

* 💚 Add missing pyo3 feature flag

To fix `error[E0433]: failed to resolve: could not find `python` in the crate root`.

* 👷 Add mypy stubtest CI check

Validate that stub files match runtime behaviour. Xref https://github.com/Jij-Inc/pyo3-stub-gen/tree/0.19.0?tab=readme-ov-file#advanced-mypystubtest-integration and https://mypy.readthedocs.io/en/stable/stubtest.html#automatic-stub-testing-stubtest

* 🏷️ Fix or ignore some type hint errors

Mostly in the xarray_backend.py file. Also added unit test to check cog3pio.__doc__ attribute, and sneaked in a small addition to .release-plz.toml

* 💚 Type hint returned DLPack tensor as types.PyCapsule

Fix `error: Argument 1 to "from_dlpack" has incompatible type "CogReader"; expected "_SupportsDLPack[None]"  [arg-type]` by properly setting return type from __dlpack__ method as types.CapsuleType. Also need to add stream parameter to satisfy numpy.from_dlpack, xref https://github.com/numpy/numpy/blob/v2.4.2/numpy/__init__.pyi#L1075-L1077

* 🏷️ Generate type stubs for CudaCogReader

Finalize type hint generation for the cog3pio API surface. Needed to reorganize feature gates a bit to make `pyo3` flag mandatory for type stub generation, while `cuda` remains optional. Also fixed an incorrect intersphinx type hint in src/python/adapters.rs.

* 📝 Parse 🏷️ under Documentation section in changelog

* 🍻 Run stubtest with cuda feature enabled

To fix `error: cog3pio.CudaCogReader is not present at runtime`

* 🔇 Silence clippy error about unused variable: `kwargs`

dl_device will be handled in #71, copy another time.
Was trying to be lazy by setting a default `device_id=0` value in the pyo3 signature instead of doing a proper Option<>, but this causes a lot of nasty `rust-lld: error: undefined symbol: Py` linker errors  on pyo3, so removing that for now. Also making sure to set the both cuda AND pyo3 flags for the cudacog stub_info.
Only handle default case of `copy=None` for now (copy=True/False implementation is trickier based on https://data-apis.org/array-api/2025.12/design_topics/copies_views_and_mutation.html#copy-keyword-argument-behavior). This means kwargs is no longer needed in the CudaCogReader.__dlpack__ signature. Updated docstrings for the `copy` and `dl_device` params section, and also the raises error section.
Regenerate pyi stubs for the cuda related stuff, and fix or ignore some static type check errors (especially on the xarray backend entrypoint). Updated some unit tests to have `device_id=0` passed to CudaCogReader (needed for local testing rather than CI).  Also fix some exotic arch (ppc64le/armv7/s390x) tests that seem to be pulling the top-level package docstring from the comment on top of #[pymodule] of the rust file instead of the __init__.py file.
Include some more instructions to deal with installing nvTIFF and patching the header file. Added some new 'CUDA' tabs on the quickstart page to teach reading into cupy arrays via CudaCogReader or xarray, mentioning that `device_id` or `device` needs to be set appropriately.
Take out async-tiff refactor since async-geotiff is out. Want to double-down more on hardware-accelerated decoding instead.
@weiji14
weiji14 marked this pull request as ready for review March 3, 2026 10:38
@weiji14
weiji14 marked this pull request as draft March 5, 2026 02:08
weiji14 added a commit to xarray-contrib/cupy-xarray that referenced this pull request Mar 5, 2026
Read TIFF data into xarray via cog3pio's experimental CudaCogReader struct that uses nvTIFF as its backend. Use cupy.from_dlpack to read the DLPack tensor, and reshape the 1-D array into a 3-D array (CHW form), setting the coordinates as appropriate. Added some API docs and basic unit tests.

Cherry-picked from weiji14/cog3pio#71
@weiji14

weiji14 commented Mar 5, 2026

Copy link
Copy Markdown
Owner Author

Thinking that the Cog3pioBackendEntrypoint code should just live in cupy-xarray, so opened xarray-contrib/cupy-xarray#70. Considering to eventually deprecate xarray as a dependency in this repo.

@weiji14 weiji14 removed this from the 0.1.0 milestone May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EPIC: GPU-decoding from TIFF -> CUDA mem -> dlpack -> CuPy

1 participant