“It installed fine” is the most misleading sentence in GPU compute setup. On Ubuntu, apt install ocl-icd-opencl-dev returns success, clinfo runs, and the pipeline still quietly executes your compute kernels on the CPU. The package resolved. The dependency tree is clean. And your XR renderer is now paying for a compute offload path that never touched the GPU. That gap — between a package that installs and a runtime that actually dispatches work to silicon — is the whole story of an Ubuntu OpenCL install. The one-line apt command is the easy part. The hard part is knowing which of the three moving pieces (the ICD loader, the vendor runtime, and the kernel-mode driver) you actually got, and whether they agree with each other and with the GPU physically in the machine. What does “ubuntu opencl install” actually set up? OpenCL on Linux is not a single thing you install. It is an installable client driver (ICD) architecture: a thin, vendor-neutral loader that discovers and dispatches to one or more vendor runtimes at run time. When you run clGetPlatformIDs(), you are not calling NVIDIA or AMD code directly — you are calling the loader, which reads /etc/OpenCL/vendors/*.icd, dlopen’s whatever shared objects those files point to, and enumerates the platforms they expose. So an Ubuntu OpenCL install is really two independent installs that people conflate into one: The ICD loader. On Ubuntu this is ocl-icd-libopencl1 (runtime) and ocl-icd-opencl-dev (headers + the libOpenCL.so symlink you link against). This is vendor-agnostic. It ships no compute capability of its own. The vendor runtime. This is the code that actually knows how to compile kernels for a specific device and dispatch them: NVIDIA’s runtime bundled with the driver, AMD’s ROCm OpenCL runtime, or Intel’s Compute Runtime. It registers itself by dropping an .icd file into /etc/OpenCL/vendors/. Install only the first and clinfo will report zero platforms — or, worse, one platform that turns out to be a Mesa/POCL software fallback that runs on the CPU. That is the trap the naive install walks straight into. Why do I need both the ICD loader and the vendor runtime? The separation is deliberate, and it is the same design principle that makes OpenCL portable across vendors in the first place. The loader is a stable ABI; vendor runtimes plug in behind it. One machine can host an NVIDIA runtime and an Intel runtime simultaneously, and the loader will enumerate both as separate platforms. That is genuinely useful — it is what lets a single binary target whatever accelerator is present, a point we develop in what OpenCL actually is as a cross-vendor parallel-compute API. The cost of that flexibility is that you can install half of it and get silence. If /etc/OpenCL/vendors/ is empty, the loader has nothing to load. If it contains a .icd pointing at a .so that does not exist (a dangling entry left by a purged driver), you get an error on enumeration or that vendor is silently skipped. And if the only entry is pocl.icd, every kernel you dispatch runs on the CPU while clinfo cheerfully reports a working platform. A quick way to think about the three layers: Layer Ubuntu package (typical) What breaks if it’s wrong ICD loader ocl-icd-libopencl1, ocl-icd-opencl-dev No platforms enumerate at all; link errors at build time Vendor runtime driver bundle / rocm-opencl-runtime / intel-opencl-icd Platform missing, or a stub runtime that yields no devices Kernel-mode driver nvidia-driver-*, amdgpu / amdgpu-dkms, i915/xe Runtime loads but reports zero compute devices Each row can pass while the row below it fails. That is why “it installed” tells you almost nothing. How do I install OpenCL on Ubuntu for NVIDIA, AMD, and Intel GPUs? The loader install is identical for all three. What differs is the vendor runtime, and each vendor has a driver-version constraint you have to respect against the GPU’s actual capability. Common to all vendors: sudo apt install ocl-icd-libopencl1 ocl-icd-opencl-dev clinfo NVIDIA. The OpenCL runtime ships inside the proprietary driver — there is no separate OpenCL package. Install the driver that matches your GPU’s compute architecture (the version that supports your card, not just the latest), typically via sudo ubuntu-drivers autoinstall or an explicit nvidia-driver-<branch>. The driver drops nvidia.icd into /etc/OpenCL/vendors/. If you have installed the CUDA toolkit, keep the driver and toolkit versions compatible per NVIDIA’s published CUDA/driver compatibility matrix — a driver too old for the toolkit is a classic silent-failure source. AMD. The modern path is the ROCm OpenCL runtime (rocm-opencl-runtime) layered on the in-kernel amdgpu driver, or amdgpu-dkms where the packaged kernel driver is too old for the card. ROCm’s supported-GPU list is narrower than the hardware AMD ships; consumer RDNA parts are not always covered, and installing the runtime on an unsupported card gives you a platform with no usable devices. Check the card against ROCm’s supported-hardware documentation before you commit the stack. Intel. The Compute Runtime (intel-opencl-icd, with libze-intel-gpu1 for the Level Zero path) targets integrated graphics and Arc discrete GPUs on the i915/xe kernel driver. This is generally the least fussy of the three on recent Ubuntu. For the broader Intel discrete-GPU picture on Linux, we cover the Intel Arc driver stack and oneAPI/SYCL support separately. The recurring theme across all three: the package installs against a driver, and the driver has to be new enough for the GPU but compatible with the toolkit. Pin the versions to what the GPU actually exposes rather than to whatever apt resolves as newest. How do I verify OpenCL is using the GPU, not falling back to the CPU? This is the step the one-line install skips, and it is the only step that matters. Verification has two parts: confirm the right platform and device enumerate, and confirm a real kernel dispatches to that device. Diagnostic checklist — run these in order: List ICD entries. ls /etc/OpenCL/vendors/ — you should see the vendor file you expect (nvidia.icd, amdocl64.icd, intel.icd). If it is empty or only shows pocl.icd, the vendor runtime is not registered. Enumerate platforms and devices. clinfo — read the Device Type field. It must say GPU, not CPU. Read Device Name and confirm it is your card, not “pthread-…” (POCL) or a CPU model string. Check the device count is non-zero. clinfo -l gives a compact tree. A platform with 0 devices means the runtime loaded but the kernel-mode driver isn’t exposing the GPU — usually a driver/permissions//dev/dri problem. Confirm dispatch, not just enumeration. Enumeration is necessary but not sufficient. Run a trivial kernel (any OpenCL “hello world” that does a vector add) selecting the GPU device explicitly, and watch nvidia-smi, rocm-smi, or intel_gpu_top for utilisation while it runs. No utilisation on the GPU tool = your work went to the CPU regardless of what clinfo claimed. Assert device type in your own code. In production, never take the default device. Query CL_DEVICE_TYPE and fail loudly if it is not CL_DEVICE_TYPE_GPU. Silent CPU fallback is a runtime decision you should turn into a hard error at startup. Step 4 is the one people skip and regret. Enumeration is a static property; dispatch is what your frame budget pays for. In our experience across GPU-porting engagements, the “we ran clinfo, it looked fine” report and the “the kernel is somehow on the CPU” bug are the same machine on the same day — the difference is whether anyone watched the utilisation counter during an actual dispatch. (Observed pattern across setup-debugging work, not a benchmarked failure rate.) What common failures make clinfo report no platforms or no devices? Four failure modes cover most of what goes wrong, and each has a distinct signature. No platforms at all. The ICD loader has nothing to load. Either the vendor runtime isn’t installed, or /etc/OpenCL/vendors/ is empty. Fix: install the vendor runtime; confirm the .icd file appeared. A platform, but it’s software. clinfo shows a platform whose device is a CPU — usually POCL or a Mesa Rusticl/clover fallback that got pulled in as a dependency. Nothing errored; the loader found a runtime, just not the one you want. Fix: install the real vendor runtime and, if you never want CPU fallback, remove the software ICD. A platform, zero devices. The vendor runtime loaded but the kernel-mode driver isn’t exposing the GPU. Common causes: driver not loaded (lsmod | grep nvidia/amdgpu), the card not on the runtime’s supported list (AMD ROCm especially), or your user lacking access to /dev/dri/renderD* and the render/video group. Fix: confirm the module is loaded, the GPU is supported, and group membership is correct. Dangling ICD entry. A purged or upgraded driver left an .icd pointing at a .so that no longer exists. clinfo errors on enumeration or skips that vendor. Fix: remove the stale .icd file, reinstall the runtime cleanly. If you want a broader treatment of the setup mechanics beyond XR, our practical setup guide for installing OpenCL on Ubuntu and the compute-node-focused how to install OpenCL on Ubuntu for GPU compute nodes walk the same ground for non-real-time workloads. Why does this matter for a real-time XR rendering pipeline? Everything above sounds like ordinary plumbing until you attach a frame budget to it. XR rendering runs against a hard clock. At 72 fps stereo, the compositor deadline is roughly 13.9 ms per frame — a fixed budget, not an average. Miss it and you get dropped frames, judder, and the kind of latency that makes users physically uncomfortable. Many XR pipelines don’t spend that whole budget on shading. They offload discrete compute passes — bake passes, physics, image processing, depth reprojection — to the GPU alongside the render, on the assumption that those passes run on-GPU in the shadow of other work. That assumption is exactly what a broken OpenCL install invalidates. If your offloaded pass silently falls back to the CPU, it doesn’t just run slower; it competes with the main thread for CPU cycles and adds milliseconds to a budget that had none to spare. The renderer that “worked in the demo” starts missing the compositor deadline under real load, and the cause is three layers down in an ICD file nobody checked. This is the same reason it pays to understand how memory movement interacts with the frame clock — a compute offload that lands on the wrong device also lands in the wrong memory, and the copy cost shows up as jitter, a dynamic we unpack in how unified virtual memory affects XR rendering budgets. And when you need the offload path to be portable across whatever GPU is in the headset host, the Linux OpenCL rendering-pipeline mechanics are the layer that has to be right first. A verified runtime is the precondition for any of our GPU engineering work to measure real on-GPU offload cost instead of debugging a phantom. FAQ How does installing OpenCL on Ubuntu work? An Ubuntu OpenCL install is two independent installs: the vendor-neutral ICD loader (ocl-icd-libopencl1 / ocl-icd-opencl-dev) and a vendor runtime that actually dispatches kernels to a specific GPU. The loader reads /etc/OpenCL/vendors/*.icd at run time and dlopen’s whatever runtimes are registered. In practice, a clean apt result means the packages resolved — it does not mean a GPU compute device is available, which is a separate thing you have to verify. What is the difference between the OpenCL ICD loader and the vendor runtime, and why do I need both? The ICD loader is a stable, vendor-agnostic front end with no compute capability of its own; the vendor runtime (NVIDIA driver bundle, AMD ROCm runtime, or Intel Compute Runtime) is the code that compiles kernels and dispatches them to hardware. You need both because the loader is what lets one binary target multiple vendors, but it can only dispatch to runtimes that have registered a .icd file. Install the loader alone and clinfo reports zero platforms — or a CPU-only software fallback. How do I install OpenCL on Ubuntu for NVIDIA, AMD, and Intel GPUs, and how do the package sets differ? The loader install is identical for all three (ocl-icd-libopencl1, ocl-icd-opencl-dev, clinfo). The vendor runtime differs: NVIDIA ships its OpenCL runtime inside the proprietary driver (no separate package); AMD uses rocm-opencl-runtime on the amdgpu driver, with a narrower supported-GPU list; Intel uses intel-opencl-icd on i915/xe. In every case, pin the driver to what the GPU actually supports and keep it compatible with any CUDA/ROCm toolkit you install. How do I verify that OpenCL is actually using the GPU rather than silently falling back to the CPU? Enumeration alone is insufficient — check the .icd entries, confirm clinfo reports a GPU device type with your card’s name (not POCL/pthread), confirm a non-zero device count, and then dispatch a trivial kernel while watching nvidia-smi, rocm-smi, or intel_gpu_top for utilisation. If the GPU tool shows no activity during a dispatch, your work ran on the CPU regardless of what clinfo claimed. In production code, query CL_DEVICE_TYPE and fail loudly rather than accepting the default device. What common install failures cause clinfo to report no platforms or no devices, and how do I fix them? Four signatures cover most cases: no platforms (vendor runtime not installed or /etc/OpenCL/vendors/ empty); a software-only platform (a POCL/Mesa fallback got pulled in — install the real runtime and remove the software ICD); a platform with zero devices (kernel-mode driver not loaded, GPU unsupported, or missing /dev/dri group access); and a dangling .icd entry left by a purged driver (remove the stale file and reinstall cleanly). Each failure lives at a different layer, which is why matching the signature to the fix is faster than reinstalling blindly. Why does a correctly configured OpenCL runtime matter for GPU offload in a real-time XR rendering pipeline? XR rendering runs against a hard frame deadline — roughly 13.9 ms per frame at 72 fps stereo. Pipelines that offload passes like baking, physics, or image processing to the GPU assume those passes run on-GPU in the shadow of shading; a silent CPU fallback breaks that assumption, competing for CPU cycles and pushing the pipeline past the compositor budget. Getting the install and device verification right up front eliminates a “works on my machine” failure class that only surfaces later, under load, when the frame budget is already gone. Get the plumbing wrong and the failure never announces itself at install time — it waits for sustained load, then shows up as a missed compositor deadline whose root cause is an ICD file three layers below the renderer. Verify the dispatch, not just the package.