From f833a8cd924a79855a5ccd51ed7989fba92593da Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Thu, 8 Oct 2026 13:43:25 -0700 Subject: [PATCH 1/2] test(cuda.core): skip CUDA-GL interop tests on TCC devices TCC is a compute-only driver model with no OpenGL, so GL context setup raises a GLException that is not classified as "context unavailable". All 23 test_graphics.py tests then fail instead of skipping on Windows/TCC. Skip up front when Device().properties.tcc_driver is set. Co-Authored-By: Claude Sonnet 5.5 --- cuda_core/tests/test_graphics.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cuda_core/tests/test_graphics.py b/cuda_core/tests/test_graphics.py index 270abaa3ab7..b829c4fd1f7 100644 --- a/cuda_core/tests/test_graphics.py +++ b/cuda_core/tests/test_graphics.py @@ -60,6 +60,12 @@ def _register_gl_image(tex_id, target): raise +def _skip_if_tcc(): + """Skip on TCC: compute-only driver model, no OpenGL, so CUDA-GL interop is unavailable.""" + if Device().properties.tcc_driver: + pytest.skip("Device is in TCC mode; TCC does not support OpenGL, so CUDA-GL interop is unavailable.") + + def _configure_pyglet_headless(): """On headless Linux: enable EGL mode, matched to the current CUDA device, or skip if EGL is absent.""" if sys.platform.startswith("linux") and not (os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")): @@ -157,6 +163,7 @@ def _allocate_gl_texture(win, width, height): @contextlib.contextmanager def _gl_context_and_buffer(nbytes=1024): """Yield ``(gl_buffer_name, nbytes)`` with a current GL context, or skip if GL is unavailable.""" + _skip_if_tcc() _configure_pyglet_headless() try: @@ -187,6 +194,7 @@ def _gl_context_and_buffer(nbytes=1024): @contextlib.contextmanager def _gl_context_and_texture(width=16, height=16): """Yield ``(tex_id, tex_target)`` with a current GL context, or skip if GL is unavailable.""" + _skip_if_tcc() _configure_pyglet_headless() try: From b1ea1b47154e9124b0257ef627e09a6083c46692 Mon Sep 17 00:00:00 2001 From: Ralf Juengling Date: Mon, 12 Oct 2026 08:32:32 -0700 Subject: [PATCH 2/2] test(graphics): share TCC-mode skip helper across graphics test suites Add tcc_skip_reason() to cuda_python_test_helpers.graphics so both cuda_core/tests/test_graphics.py and cuda_bindings/tests/test_graphics_apis.py skip CUDA-GL interop tests on a TCC device using one shared predicate. The helper queries the current device via cuda.bindings.runtime (not cuda.core) so the cuda_bindings suite does not take a cuda.core dependency. Runtime query failures propagate rather than being masked as a skip. test_cuda_register_image_invalid stays enabled: it does not open a GL context and only checks the error path for an invalid handle. Signed-off-by: Ralf Juengling --- cuda_bindings/tests/test_graphics_apis.py | 3 ++ cuda_core/tests/test_graphics.py | 5 ++-- .../cuda_python_test_helpers/graphics.py | 29 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/cuda_bindings/tests/test_graphics_apis.py b/cuda_bindings/tests/test_graphics_apis.py index bd87fc4d2e6..491aa7bd837 100644 --- a/cuda_bindings/tests/test_graphics_apis.py +++ b/cuda_bindings/tests/test_graphics_apis.py @@ -13,6 +13,7 @@ gl_context_not_on_nvidia_gpu_reason, is_gl_context_unavailable, open_gl_window, + tcc_skip_reason, ) from cuda.bindings import runtime as cudart @@ -56,6 +57,8 @@ def _allocate_gl_texture(win): @contextlib.contextmanager def _gl_context(): """Yield ``(tex_id, tex_target)`` with a current GL context, or skip if GL is unavailable.""" + if reason := tcc_skip_reason(): + pytest.skip(reason) _configure_pyglet_headless() try: diff --git a/cuda_core/tests/test_graphics.py b/cuda_core/tests/test_graphics.py index b829c4fd1f7..72e1c8c559c 100644 --- a/cuda_core/tests/test_graphics.py +++ b/cuda_core/tests/test_graphics.py @@ -20,6 +20,7 @@ is_gl_context_unavailable, open_gl_window, select_headless_egl_device_for_cuda, + tcc_skip_reason, ) import cuda_python_test_helpers @@ -62,8 +63,8 @@ def _register_gl_image(tex_id, target): def _skip_if_tcc(): """Skip on TCC: compute-only driver model, no OpenGL, so CUDA-GL interop is unavailable.""" - if Device().properties.tcc_driver: - pytest.skip("Device is in TCC mode; TCC does not support OpenGL, so CUDA-GL interop is unavailable.") + if reason := tcc_skip_reason(): + pytest.skip(reason) def _configure_pyglet_headless(): diff --git a/cuda_python_test_helpers/cuda_python_test_helpers/graphics.py b/cuda_python_test_helpers/cuda_python_test_helpers/graphics.py index fae57257a4d..96561e8b244 100644 --- a/cuda_python_test_helpers/cuda_python_test_helpers/graphics.py +++ b/cuda_python_test_helpers/cuda_python_test_helpers/graphics.py @@ -128,6 +128,35 @@ def open_gl_window(): return None +def tcc_skip_reason() -> str | None: + """Return a skip reason if the current CUDA device uses the TCC driver model, else None. + + TCC (Tesla Compute Cluster) is a Windows compute-only driver model with no + OpenGL support, so CUDA-GL interop is unavailable on a TCC device. The + check queries the currently-selected device via the CUDA runtime API + (``cuda.bindings``) rather than ``cuda.core``, so both the ``cuda_core`` + and ``cuda_bindings`` graphics tests can share it without the latter + taking a ``cuda.core`` dependency. + + Runtime query failures (e.g. no CUDA device) propagate rather than being + masked as a skip, so a real environment problem surfaces instead of being + hidden behind a skipped test. + """ + from cuda.bindings import runtime as cudart + + err, device = cudart.cudaGetDevice() + if err != cudart.cudaError_t.cudaSuccess: + raise RuntimeError(f"cudaGetDevice failed: {cudart.cudaGetErrorString(err)[1].decode()!r}") + err, is_tcc = cudart.cudaDeviceGetAttribute(cudart.cudaDeviceAttr.cudaDevAttrTccDriver, device) + if err != cudart.cudaError_t.cudaSuccess: + raise RuntimeError( + f"cudaDeviceGetAttribute(cudaDevAttrTccDriver) failed: {cudart.cudaGetErrorString(err)[1].decode()!r}" + ) + if is_tcc: + return "Device is in TCC mode; TCC does not support OpenGL, so CUDA-GL interop is unavailable." + return None + + def gl_context_not_on_nvidia_gpu_reason() -> str | None: """Return a skip reason if the current GL context is not on an NVIDIA GPU, else None.