Skip to content

Commit c237104

Browse files
authored
feat(compat.opencl): the loader builds on Windows, and the test stops skipping there (#379)
* feat(compat.opencl): the loader builds on Windows, and the test stops skipping there The package had linux and macosx entries and no windows one, so a Windows consumer had no OpenCL loader to link and `tests/examples/opencl` compiled to a printed skip. The gap was stated in the recipe and tracked nowhere. Upstream has supported Windows all along: `loader/windows/` enumerates drivers from `HKLM\SOFTWARE\Khronos\OpenCL\Vendors`, from the display adapters through DXGK, and from installed app packages, and loads each with LoadLibrary. The section here is upstream's WIN32 source list with upstream's two link libraries, `cfgmgr32` and `runtimeobject`. NO RUNTIME ADAPTER, AND THE ASYMMETRY IS THE POINT. `compat.opencl-runtime` exists to undo mcpp's PRIVATE loader on Linux, where a bare-soname dlopen from inside an mcpp binary does not search the host's library path. A Windows artifact runs under the system loader and every registry entry names a DLL by absolute path, so there is nothing to undo -- this platform gets a loader with no adapter beside it. STATIC, as on macOS. A program that wants THE system loader links the vendor's `OpenCL.lib` against `C:\Windows\System32\OpenCL.dll`; a package shipping a second `OpenCL.dll` would compete with that rather than converge on it. Static also keeps `loader/windows/OpenCL.def` out of the build, which only a DLL needs. The member test now declares the dependency unconditionally and its skip branch says what it actually means -- built without the loader -- rather than naming a platform that no longer lacks one. Linux is unchanged and still reports `platform: NVIDIA CUDA / device: NVIDIA GeForce RTX 4080` on a machine with a driver. The Windows half is verified by this repository's `workspace (windows default)` job, which is the only Windows available to the change. * fix(compat.opencl): name the Windows import libraries MSVC would have auto-linked The first Windows section carried upstream's two link libraries and nothing else, because that is all upstream's CMakeLists names. MSVC never has to name the rest: its SDK headers pull the default import libraries in through `#pragma comment(lib, ...)`. mcpp links with lld and does not inherit that. The build compiled cleanly and failed at link with nine undefined symbols -- eight registry and process-token calls and `StringFromGUID2` -- so advapi32 and ole32 are named here. Measured on this repository's Windows job, which is the only Windows this change has. The compile was never the criterion.
1 parent a589ca1 commit c237104

3 files changed

Lines changed: 88 additions & 9 deletions

File tree

‎pkgs/c/compat.opencl.lua‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@
2828
-- hide the GPU. `xim:pocl` therefore declares `OCL_ICD_FILENAMES` into the
2929
-- subos environment and touches no vendors directory.
3030
--
31+
-- WINDOWS ENUMERATES SOMEWHERE ELSE AND NEEDS NO ADAPTER. There the loader
32+
-- reads `HKLM\SOFTWARE\Khronos\OpenCL\Vendors`, the display adapters through
33+
-- DXGK, and installed app packages, and each entry names a DLL by absolute
34+
-- path. An mcpp artifact runs under the SYSTEM loader on Windows, so those
35+
-- paths resolve with no help -- which is why this platform has a loader here
36+
-- and no `compat.opencl-runtime` beside it.
37+
--
3138
-- SHARED, with the canonical soname, for the reason `compat.vulkan` records:
3239
-- everything in a process must converge on one loader, and a library that
3340
-- dlopens `libOpenCL.so.1` by name has to land here.
@@ -59,6 +66,15 @@ package = {
5966
sha256 = "48fd0c5181db7cd046f4f731d5955694892e10998d49d09ee0d997e7e04fd939",
6067
},
6168
},
69+
windows = {
70+
["2026.05.29"] = {
71+
url = {
72+
GLOBAL = "https://github.com/KhronosGroup/OpenCL-ICD-Loader/archive/refs/tags/v2026.05.29.tar.gz",
73+
CN = "https://gitcode.com/mcpp-res/opencl/releases/download/2026.05.29/opencl-2026.05.29.tar.gz",
74+
},
75+
sha256 = "48fd0c5181db7cd046f4f731d5955694892e10998d49d09ee0d997e7e04fd939",
76+
},
77+
},
6278
},
6379

6480
mcpp = {
@@ -153,5 +169,61 @@ package = {
153169
ldflags = { "-ldl" },
154170
runtime = { capabilities = { "opencl.icd.driver" } },
155171
},
172+
173+
windows = {
174+
-- Upstream's WIN32 source list. The loader enumerates drivers from
175+
-- the registry (`HKLM\SOFTWARE\Khronos\OpenCL\Vendors`), from the
176+
-- display adapters through DXGK, and from installed app packages,
177+
-- and loads each with LoadLibrary.
178+
--
179+
-- NO RUNTIME ADAPTER HERE, AND THAT ASYMMETRY IS THE WHOLE REASON
180+
-- LINUX HAS ONE. `compat.opencl-runtime` exists to undo mcpp's
181+
-- PRIVATE loader: a bare-soname dlopen from inside an mcpp binary
182+
-- does not search the host's library path. A Windows artifact runs
183+
-- under the system loader, and the registry names each vendor DLL
184+
-- by absolute path, so there is nothing to undo.
185+
--
186+
-- STATIC, as on macOS, and for a Windows-specific reason on top of
187+
-- the one recorded there. A program that wants THE system loader
188+
-- links the vendor's `OpenCL.lib` against
189+
-- `C:\Windows\System32\OpenCL.dll`; a package shipping a second
190+
-- `OpenCL.dll` would compete with that rather than converge on it.
191+
-- A program that links this package dispatches through this copy,
192+
-- which is the same contract macOS has. It also keeps the module
193+
-- definition file out of the build: upstream exports through
194+
-- `loader/windows/OpenCL.def`, which only a DLL needs.
195+
sources = {
196+
"*/loader/windows/icd_windows.c",
197+
"*/loader/windows/icd_windows_apppackage.c",
198+
"*/loader/windows/icd_windows_dxgk.c",
199+
"*/loader/windows/icd_windows_envvars.c",
200+
"*/loader/windows/icd_windows_hkr.c",
201+
"*/loader/windows/icd_windows_library.c",
202+
},
203+
-- Upstream's `target_link_libraries(OpenCL PRIVATE cfgmgr32.lib
204+
-- runtimeobject.lib)` -- and four more that upstream never has to
205+
-- name. cfgmgr32 is the device enumeration the DXGK path walks;
206+
-- runtimeobject is WinRT, which the app-package scan calls into.
207+
--
208+
-- WHY UPSTREAM'S TWO ARE NOT ENOUGH HERE. MSVC pulls the default
209+
-- Windows import libraries in through `#pragma comment(lib, ...)`
210+
-- in its own SDK headers, so a CMake build never writes advapi32
211+
-- or ole32 down. mcpp links with lld and does not inherit that, and
212+
-- the build compiled cleanly and then failed at link with nine
213+
-- undefined symbols -- eight registry and token calls
214+
-- (`RegOpenKeyExA`, `OpenProcessToken`, `GetSidSubAuthority`, ...)
215+
-- and `StringFromGUID2`.
216+
--
217+
-- Measured on this index's own Windows job, which is the only
218+
-- Windows the change had: the compile is not the criterion, the
219+
-- link is.
220+
ldflags = {
221+
"-lcfgmgr32", -- CM_Get_Device_ID_List, the DXGK adapter walk
222+
"-lruntimeobject", -- WinRT, for the app-package scan
223+
"-ladvapi32", -- registry + process token
224+
"-lole32", -- StringFromGUID2
225+
},
226+
runtime = { capabilities = { "opencl.icd.driver" } },
227+
},
156228
},
157229
}

‎tests/examples/opencl/mcpp.toml‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22
name = "opencl-tests"
33
version = "0.1.0"
44

5-
# The loader and, on Linux, the runtime adapter it depends on. The adapter is
6-
# what lets the loader's dlopen of a host ICD resolve from inside mcpp's own
7-
# C library; on a runner with no driver it is empty and the loader reports
8-
# zero platforms. compat.opencl has no windows entry: Windows drivers are
9-
# enumerated through the registry, which this index does not model yet, so
10-
# the test compiles to a skip there.
11-
[target.'cfg(unix)'.dependencies.compat]
5+
# The loader on every platform it has one, and on Linux the runtime adapter it
6+
# depends on. The adapter is what lets the loader's dlopen of a host ICD resolve
7+
# from inside mcpp's own C library; on a runner with no driver it is empty and
8+
# the loader reports zero platforms.
9+
#
10+
# WINDOWS NEEDS NO ADAPTER AND IS NOT A SKIP ANY MORE. The loader reads the
11+
# registry, DXGK and app packages there, and each entry names a driver DLL by
12+
# absolute path that the SYSTEM loader resolves. `compat.opencl` gained its
13+
# windows section for that reason; the test below is the same source on all
14+
# three platforms.
15+
[dependencies.compat]
1216
opencl = "2026.05.29"
1317

14-
[target.'cfg(unix)'.build]
18+
[build]
1519
cxxflags = ["-DHAVE_OPENCL_LOADER=1"]

‎tests/examples/opencl/tests/platforms.cpp‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414
// for the declarations both provide.
1515
#include <cstdio>
1616
#if !defined(HAVE_OPENCL_LOADER)
17+
// Reached only if a consumer builds this file without the loader on the line.
18+
// It used to be the Windows path, which had no loader to depend on; the
19+
// package has one now and the manifest declares it on every platform.
1720
int main() {
18-
std::printf("compat.opencl: skipped (no windows entry; drivers are enumerated through the registry there)\n");
21+
std::printf("compat.opencl: skipped (built without the loader)\n");
1922
return 0;
2023
}
2124
#else

0 commit comments

Comments
 (0)