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 270abaa3ab7..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 @@ -60,6 +61,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 reason := tcc_skip_reason(): + pytest.skip(reason) + + 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 +164,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 +195,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: 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.