From f91eca5154d7447dd08ada756597e219c2447fae Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Fri, 11 Sep 2026 19:26:46 -0300 Subject: [PATCH 1/9] Add KI device selection interface --- src/OpenCLKernels.jl | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index f57c1642..9f16bafe 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -15,6 +15,12 @@ import Adapt export OpenCLBackend struct OpenCLBackend <: KA.GPU + platform::cl.Platform + + function OpenCLBackend(; platform=cl.platform()) + cl.platform!(platform) + new(platform) + end end function KA.allocate(::OpenCLBackend, ::Type{T}, dims::Tuple; unified::Bool = false) where T @@ -48,6 +54,31 @@ Adapt.adapt_storage(::KA.CPU, a::CLArray) = convert(Array, a) # rather than for `CLArray`. Adapt.adapt_storage(::KA.ConstAdaptor, a::CLDeviceArray) = Base.Experimental.Const(a) +## Device Selection + +# devices are numbered consecutively across all platforms, in enumeration order + +function KA.ndevices(b::OpenCLBackend) + length(cl.devices(b.platform)) +end + +function KA.device(b::OpenCLBackend) + current = cl.device() + i = 0 + for d in cl.devices(b.platform) + i += 1 + d == current && return i + end + error("Active OpenCL device $current not found in the current OpenCL platform \"$(b.platform.name)\".") +end + +function KA.device!(b::OpenCLBackend, id::Int) + devs = cl.devices(b.platform) + id > length(devs) && throw(ArgumentError("Device id $id out of bounds.")) + + cl.device!(devs[id]) + return nothing +end ## Memory Operations From e630e4b162592349c9d9acaf6088d4b58369f24a Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:04:59 -0300 Subject: [PATCH 2/9] Fix test --- src/OpenCLKernels.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index 9f16bafe..b480aaf6 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -74,7 +74,7 @@ end function KA.device!(b::OpenCLBackend, id::Int) devs = cl.devices(b.platform) - id > length(devs) && throw(ArgumentError("Device id $id out of bounds.")) + 0 < id < length(devs) || throw(ArgumentError("Device id $id out of bounds.")) cl.device!(devs[id]) return nothing From 544b2d39ee40c9b006b66f38e15b88d18cdeba3e Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:05:08 -0300 Subject: [PATCH 3/9] Style fix --- src/OpenCLKernels.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index b480aaf6..53fac2eb 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -64,9 +64,7 @@ end function KA.device(b::OpenCLBackend) current = cl.device() - i = 0 - for d in cl.devices(b.platform) - i += 1 + for (i, d) in enumerate(cl.devices(b.platform)) d == current && return i end error("Active OpenCL device $current not found in the current OpenCL platform \"$(b.platform.name)\".") From 25d4399df0f25d90cea037a26228ccde6bce7914 Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:06:52 -0300 Subject: [PATCH 4/9] Fix test fix --- src/OpenCLKernels.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index 53fac2eb..123ef2c1 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -72,7 +72,7 @@ end function KA.device!(b::OpenCLBackend, id::Int) devs = cl.devices(b.platform) - 0 < id < length(devs) || throw(ArgumentError("Device id $id out of bounds.")) + 0 < id <= length(devs) || throw(ArgumentError("Device id $id out of bounds.")) cl.device!(devs[id]) return nothing From fc5a516e4db923470505b4dbe2687b522f649717 Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:55:57 -0300 Subject: [PATCH 5/9] Fix comment --- src/OpenCLKernels.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index 123ef2c1..f23850e4 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -56,7 +56,7 @@ Adapt.adapt_storage(::KA.ConstAdaptor, a::CLDeviceArray) = Base.Experimental.Con ## Device Selection -# devices are numbered consecutively across all platforms, in enumeration order +# devices are numbered consecutively within the backend's platform, in enumeration order function KA.ndevices(b::OpenCLBackend) length(cl.devices(b.platform)) From b181074ec21f451706734428d61344f097031827 Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Tue, 15 Sep 2026 13:20:25 -0300 Subject: [PATCH 6/9] Add ndevices function to OpenCL and use it in the KA interface --- lib/cl/platform.jl | 21 ++++++++++++++------- src/OpenCLKernels.jl | 4 ++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/cl/platform.jl b/lib/cl/platform.jl index 7ea5a27c..33bbd751 100644 --- a/lib/cl/platform.jl +++ b/lib/cl/platform.jl @@ -81,16 +81,23 @@ function num_platforms() return Int(nplatforms[]) end -function devices(p::Platform, dtype) - ndevices = Ref{Cuint}() - ret = unchecked_clGetDeviceIDs(p, dtype, 0, C_NULL, ndevices) - if ret == CL_DEVICE_NOT_FOUND || ndevices[] == 0 - return Device[] +function ndevices(p::Platform, dtype=CL_DEVICE_TYPE_ALL) + ndevs = Ref{Cuint}() + ret = unchecked_clGetDeviceIDs(p, dtype, 0, C_NULL, ndevs) + if ret == CL_DEVICE_NOT_FOUND + return Cuint(0) elseif ret != CL_SUCCESS throw(CLError(ret)) end - result = Vector{cl_device_id}(undef, ndevices[]) - clGetDeviceIDs(p, dtype, ndevices[], result, C_NULL) + + return ndevs[] +end + +function devices(p::Platform, dtype) + ndevs = ndevices(p, dtype) + ndevs > 0 || return Device[] + result = Vector{cl_device_id}(undef, ndevs) + clGetDeviceIDs(p, dtype, ndevs, result, C_NULL) devs = Device[Device(id) for id in result] # OpenCL does not guarantee a stable enumeration order, so sort deterministically diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index f23850e4..88d49fde 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -59,7 +59,7 @@ Adapt.adapt_storage(::KA.ConstAdaptor, a::CLDeviceArray) = Base.Experimental.Con # devices are numbered consecutively within the backend's platform, in enumeration order function KA.ndevices(b::OpenCLBackend) - length(cl.devices(b.platform)) + Int(cl.ndevices(b.platform)) end function KA.device(b::OpenCLBackend) @@ -71,8 +71,8 @@ function KA.device(b::OpenCLBackend) end function KA.device!(b::OpenCLBackend, id::Int) + 0 < id <= KA.ndevices(b) || throw(ArgumentError("Device id $id out of bounds.")) devs = cl.devices(b.platform) - 0 < id <= length(devs) || throw(ArgumentError("Device id $id out of bounds.")) cl.device!(devs[id]) return nothing From 65ecbc9e8d58aa427cebe92b0f5a44b983699d6a Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Tue, 15 Sep 2026 13:26:14 -0300 Subject: [PATCH 7/9] Clarify error message --- src/OpenCLKernels.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index 88d49fde..752b1827 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -67,7 +67,7 @@ function KA.device(b::OpenCLBackend) for (i, d) in enumerate(cl.devices(b.platform)) d == current && return i end - error("Active OpenCL device $current not found in the current OpenCL platform \"$(b.platform.name)\".") + error("Active OpenCL device $current not found in the OpenCLBackend's platform \"$(b.platform.name)\".") end function KA.device!(b::OpenCLBackend, id::Int) From 4609f7c3e48730729e4b5737a547f1b8b7509460 Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Tue, 15 Sep 2026 13:38:53 -0300 Subject: [PATCH 8/9] Don't change the platform when constructing backend, but check --- src/OpenCLKernels.jl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index 752b1827..afd48af7 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -14,16 +14,17 @@ import Adapt export OpenCLBackend -struct OpenCLBackend <: KA.GPU - platform::cl.Platform +Base.@kwdef struct OpenCLBackend <: KA.GPU + platform::cl.Platform = cl.platform() +end - function OpenCLBackend(; platform=cl.platform()) - cl.platform!(platform) - new(platform) - end +function check_platform(b::OpenCLBackend) + b.platform === cl.platform() || @warn "OpenCLBackend platform \"$(b.platform.name)\" is not the active platform \"$(cl.platform().name)\"" + return nothing end -function KA.allocate(::OpenCLBackend, ::Type{T}, dims::Tuple; unified::Bool = false) where T +function KA.allocate(b::OpenCLBackend, ::Type{T}, dims::Tuple; unified::Bool = false) where T + check_platform(b) if unified memory_backend = cl.unified_memory_backend() if memory_backend === cl.USMBackend() @@ -130,6 +131,8 @@ function threads_to_workgroupsize(threads, ndrange) end function (obj::KA.Kernel{OpenCLBackend})(args...; ndrange=nothing, workgroupsize=nothing) + check_platform(obj.backend) + ndrange, workgroupsize, iterspace, dynamic = KA.launch_config(obj, ndrange, workgroupsize) From 5350e44ba396496e8d8b264e112ba6643617a7bd Mon Sep 17 00:00:00 2001 From: Christian <28689358+christiangnrd@users.noreply.github.com> Date: Wed, 16 Sep 2026 07:57:54 -0300 Subject: [PATCH 9/9] Inline check, outline warning --- src/OpenCLKernels.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenCLKernels.jl b/src/OpenCLKernels.jl index afd48af7..b1d4eaa7 100644 --- a/src/OpenCLKernels.jl +++ b/src/OpenCLKernels.jl @@ -18,13 +18,13 @@ Base.@kwdef struct OpenCLBackend <: KA.GPU platform::cl.Platform = cl.platform() end -function check_platform(b::OpenCLBackend) - b.platform === cl.platform() || @warn "OpenCLBackend platform \"$(b.platform.name)\" is not the active platform \"$(cl.platform().name)\"" +@noinline function platform_mismatch_warning(expected::cl.Platform, active::cl.Platform) + @warn "OpenCLBackend platform \"$(expected.name)\" is not the active platform \"$(active.name)\"" return nothing end function KA.allocate(b::OpenCLBackend, ::Type{T}, dims::Tuple; unified::Bool = false) where T - check_platform(b) + b.platform === cl.platform() || platform_mismatch_warning(b.platform, cl.platform()) if unified memory_backend = cl.unified_memory_backend() if memory_backend === cl.USMBackend() @@ -131,7 +131,7 @@ function threads_to_workgroupsize(threads, ndrange) end function (obj::KA.Kernel{OpenCLBackend})(args...; ndrange=nothing, workgroupsize=nothing) - check_platform(obj.backend) + obj.backend.platform === cl.platform() || platform_mismatch_warning(obj.backend.platform, cl.platform()) ndrange, workgroupsize, iterspace, dynamic = KA.launch_config(obj, ndrange, workgroupsize)