├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.zig ├── build.zig.zon ├── examples ├── build.zig ├── build.zig.zon └── saxpy.zig └── src └── opencl.zig /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | - cron: '0 6 * * *' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-24.04 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Fetch dependencies 18 | run: | 19 | export DEBIAN_FRONTEND=noninteractive 20 | sudo apt update 21 | sudo apt install -y clinfo ocl-icd-opencl-dev pocl-opencl-icd 22 | clinfo 23 | 24 | - name: Setup Zig 25 | uses: mlugg/setup-zig@v1 26 | with: 27 | version: master 28 | 29 | - name: Check formatting 30 | run: zig fmt --check . 31 | 32 | - name: Test examples 33 | run: zig build --build-file $(pwd)/examples/build.zig test 34 | 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .zig-cache/ 2 | zig-out 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © Robin Voetter 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opencl-zig 2 | 3 | OpenCL bindings for Zig. 4 | 5 | ## Overview 6 | 7 | This repository attempts to improve the OpenCL programming experience in Zig. This is done by for example translating OpenCL errors into Zig errors, integration with Zig allocators, Zig-friendly boilerplate around OpenCL functions, and adapting the API to common Zig style in general. 8 | 9 | opencl-zig generally targets latest Zig, and is tested daily. 10 | 11 | The bindings are hand-written and added on a when-required basis: Unlike Vulkan, OpenCL's machine readable API definitions (cl.xml) aren't really suitable to automatically generate Zig bindings. For example, information about the possible errors that a function returns is not available. If required API functionality is missing, please make an issue or a pull request. 12 | 13 | ## Examples 14 | 15 | A saxpy example is available in [examples/saxpy.zig](examples/saxpy.zig). It can be executed by running `zig build --build-file $(pwd)/examples/build.zig run-saxpy` in the opencl-zig root directory. 16 | 17 | For uses of this project, see the [Zig SPIR-V test executor](https://github.com/Snektron/zig-spirv-test-executor) and the [Zig OpenCL SPIR-V Demos](https://github.com/Snektron/zig-opencl-spirv-demos/). 18 | 19 | ## Usage 20 | 21 | opencl-zig can be included in a Zig project as a regular Zig dependency. First, add a dependency to the bindings to your `build.zig.zon`: 22 | ```zig 23 | .{ 24 | .dependencies = .{ 25 | .opencl = .{ 26 | .url = "https://github.com/Snektron/opencl-zig/archive/.tar.gz", 27 | .hash = "", 28 | }, 29 | }, 30 | } 31 | ``` 32 | In your `build.zig` file, you can then import opencl-zig as usual. Note that you are required to pass the current build mode and target to the opencl-zig dependency: 33 | ```zig 34 | pub fn build(b: *std.Build) void { 35 | const target = ...; 36 | const optimize = ...; 37 | const opencl = b.dependency("opencl", .{ 38 | .target = target, 39 | .optimize = optimize, 40 | }).module("opencl"); 41 | 42 | const exe = ...; 43 | exe.root_module.addImport("opencl", opencl); 44 | } 45 | ``` 46 | 47 | See [examples/build.zig](examples/build.zig) and [examples/build.zig.zon](examples/build.zig.zon) for a concrete example. 48 | 49 | ### Caveats 50 | 51 | Currently, opencl-zig depends on the `OpenCL` system dependency. On Linux, this is provided by `libOpenCL.so`, which usually either an ICD loader such as [OpenCL-ICD-Loader](https://github.com/KhronosGroup/OpenCL-ICD-Loader) or [ocl-icd](https://github.com/OCL-dev/ocl-icd), or a GPU driver directly. In the future, this project might automatically build an ICD loader automatically, but for now, a system dependency is required. OpenCL headers are not required, these are automatically fetched. 52 | 53 | ## Design 54 | 55 | The bindings are mostly written by translating the [OpenCL API](https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html) to Zig in a straight forward manner, though there are some deviations where it makes sense. In general, the bindings are designed along the following guidelines: 56 | - Functions related to a particular OpenCL object live in a type named after that particular object, but without the OpenCL namespace bits and cased in usual Zig Pascal casing. For example, functions that operate on `cl_platform_id` live in `Platform`, and functions that operate on `cl_event` live in `Event`. 57 | - Zig equivalents of OpenCL objects are declared as `extern struct` with `handle` as sole member. This allows us to type pun the Zig type into an OpenCL type to provide a smoother integration. 58 | - Freestanding functions are stripped of their OpenCL namespace too. Create-style functions such as `clCreateContext` are implemented as `Context.create`, and a freestanding alias `createContext` is provided to mirror the OpenCL API more closely. 59 | - Functions that require allocation are implemented using Zig `Allocator`s so that Users only need to make a single call rather than two. See for example `Device.getName`. 60 | - Where it makes sense, `clGet*Info` calls are lowered into a single `getInfo` function. This doesn't really work for info calls that require dynamic size, those are usually implemented as a separate function. 61 | - Not all errors are directly converted into the equivalent Zig error. Generally, they are translated according to the following rules: 62 | - `CL_SUCCESS` is obviously never translated into an error. 63 | - Errors that are programmer errors, such as invalid parameter values or lifetime issues, are made `unreachable`. All paths are separated so that the programmer can see which error was triggered more easily. 64 | - Errors that occur due to issues that the programmer couldn't avoid by programming better, such as resource exhaustion, (kernel) compile errors, etc, are generally translated into Zig errors of the same name. Some exceptions apply, see below. 65 | - `CL_OUT_OF_HOST_MEMORY` is translated into `error.OutOfMemory` so that it is the same error as returned by allocator implementations. 66 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) void { 4 | const target = b.standardTargetOptions(.{}); 5 | const optimize = b.standardOptimizeOption(.{}); 6 | 7 | const headers = b.dependency("opencl_headers", .{}); 8 | 9 | const opencl = b.addModule("opencl", .{ 10 | .root_source_file = b.path("src/opencl.zig"), 11 | .link_libc = true, 12 | .target = target, 13 | .optimize = optimize, 14 | }); 15 | opencl.addIncludePath(headers.path("")); 16 | // TODO: Instead of relying on a system package, 17 | // we should either package the OpenCL-ICD-Loader 18 | // with Zig or manually load function pointers 19 | // from the OpenCL library. 20 | opencl.linkSystemLibrary("OpenCL", .{}); 21 | } 22 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .fingerprint = 0xf5d708efdddf805b, 3 | .name = .opencl, 4 | .version = "0.1.0", 5 | .dependencies = .{ 6 | .opencl_headers = .{ 7 | .url = "https://github.com/KhronosGroup/OpenCL-Headers/archive/v2024.05.08.tar.gz", 8 | .hash = "1220ad35211a500b402000c3fb50dbf5754a4cd41539cdde4b00195af47dde2cf36f", 9 | }, 10 | }, 11 | 12 | .paths = .{ 13 | "build.zig", 14 | "build.zig.zon", 15 | "src", 16 | "LICENSE", 17 | "README.md", 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /examples/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) void { 4 | const target = b.standardTargetOptions(.{}); 5 | const optimize = b.standardOptimizeOption(.{}); 6 | 7 | const opencl = b.dependency("opencl", .{ 8 | .target = target, 9 | .optimize = optimize, 10 | }).module("opencl"); 11 | 12 | const saxpy_exe = b.addExecutable(.{ 13 | .name = "saxpy", 14 | .root_source_file = b.path("saxpy.zig"), 15 | .target = target, 16 | .link_libc = true, 17 | .optimize = optimize, 18 | }); 19 | saxpy_exe.root_module.addImport("opencl", opencl); 20 | b.installArtifact(saxpy_exe); 21 | 22 | const saxpy_run_cmd = b.addRunArtifact(saxpy_exe); 23 | saxpy_run_cmd.step.dependOn(b.getInstallStep()); 24 | if (b.args) |args| { 25 | saxpy_run_cmd.addArgs(args); 26 | } 27 | 28 | const saxpy_run_step = b.step("run-saxpy", "Run the saxpy example"); 29 | saxpy_run_step.dependOn(&saxpy_run_cmd.step); 30 | 31 | const test_step = b.step("test", "Run all examples and see if they work correctly"); 32 | test_step.dependOn(&saxpy_run_cmd.step); 33 | } 34 | -------------------------------------------------------------------------------- /examples/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .fingerprint = 0x1bac600bf16268a4, 3 | .name = .opencl_zig_examples, 4 | .version = "0.1.0", 5 | .dependencies = .{ 6 | .opencl = .{ 7 | .path = "..", 8 | }, 9 | }, 10 | .paths = .{""}, 11 | } 12 | -------------------------------------------------------------------------------- /examples/saxpy.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Allocator = std.mem.Allocator; 3 | 4 | const cl = @import("opencl"); 5 | 6 | pub const std_options: std.Options = .{ 7 | .log_level = .info, 8 | }; 9 | 10 | fn fail(comptime fmt: []const u8, args: anytype) noreturn { 11 | std.log.err(fmt, args); 12 | std.process.exit(1); 13 | } 14 | 15 | const Options = struct { 16 | platform: ?[]const u8, 17 | device: ?[]const u8, 18 | 19 | fn parse(a: Allocator) !Options { 20 | var args = try std.process.argsWithAllocator(a); 21 | _ = args.next(); // executable name 22 | 23 | var platform: ?[]const u8 = null; 24 | var device: ?[]const u8 = null; 25 | var help: bool = false; 26 | 27 | while (args.next()) |arg| { 28 | if (std.mem.eql(u8, arg, "--platform") or std.mem.eql(u8, arg, "-p")) { 29 | platform = args.next() orelse fail("missing argument to option {s}", .{arg}); 30 | } else if (std.mem.eql(u8, arg, "--device") or std.mem.eql(u8, arg, "-d")) { 31 | device = args.next() orelse fail("missing argument to option {s}", .{arg}); 32 | } else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { 33 | help = true; 34 | } else { 35 | fail("unknown option '{s}'", .{arg}); 36 | } 37 | } 38 | 39 | if (help) { 40 | const out = std.io.getStdOut(); 41 | try out.writer().writeAll( 42 | \\usage: saxpy [options...] 43 | \\ 44 | \\Options: 45 | \\--platform|-p OpenCL platform name to use. By default, uses the 46 | \\ first platform that has any devices available. 47 | \\--device|-d OpenCL device name to use. If --platform is left 48 | \\ unspecified, all devices of all platforms are 49 | \\ matched. By default, uses the first device of the 50 | \\ platform. 51 | ); 52 | std.process.exit(0); 53 | } 54 | 55 | return .{ 56 | .platform = platform, 57 | .device = device, 58 | }; 59 | } 60 | }; 61 | 62 | pub fn main() !void { 63 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); 64 | defer arena.deinit(); 65 | const alloc = arena.allocator(); 66 | 67 | const options = try Options.parse(alloc); 68 | 69 | const platforms = try cl.getPlatforms(alloc); 70 | std.log.info("{} opencl platform(s) available", .{platforms.len}); 71 | if (platforms.len == 0) { 72 | fail("no opencl platforms available", .{}); 73 | } 74 | 75 | const platform, const device = found: for (platforms) |platform| { 76 | const platform_name = try platform.getName(alloc); 77 | if (options.platform) |platform_query| { 78 | if (std.mem.indexOf(u8, platform_name, platform_query) == null) { 79 | continue; 80 | } 81 | } 82 | 83 | const devices = try platform.getDevices(alloc, cl.DeviceType.all); 84 | for (devices) |device| { 85 | const device_name = try device.getName(alloc); 86 | if (options.device) |device_query| { 87 | if (std.mem.indexOf(u8, device_name, device_query) == null) { 88 | continue; 89 | } 90 | } 91 | 92 | std.log.info("selected platform '{s}' and device '{s}'", .{ platform_name, device_name }); 93 | 94 | break :found .{ platform, device }; 95 | } 96 | } else { 97 | fail("failed to select platform and device", .{}); 98 | }; 99 | 100 | const context = try cl.createContext(&.{device}, .{ .platform = platform }); 101 | defer context.release(); 102 | 103 | const queue = try cl.createCommandQueue(context, device, .{ .profiling = true }); 104 | defer queue.release(); 105 | 106 | std.log.info("compiling kernel...", .{}); 107 | 108 | const source = 109 | \\kernel void saxpy(global float* y, global const float* x, const float a) { 110 | \\ const size_t gid = get_global_id(0); 111 | \\ y[gid] += x[gid] * a; 112 | \\} 113 | \\ 114 | ; 115 | 116 | const program = try cl.createProgramWithSource(context, source); 117 | defer program.release(); 118 | 119 | program.build( 120 | &.{device}, 121 | "-cl-std=CL3.0", 122 | ) catch |err| { 123 | if (err == error.BuildProgramFailure) { 124 | const log = try program.getBuildLog(alloc, device); 125 | defer alloc.free(log); 126 | std.log.err("failed to compile kernel:\n{s}", .{log}); 127 | } 128 | 129 | return err; 130 | }; 131 | 132 | const kernel = try cl.createKernel(program, "saxpy"); 133 | defer kernel.release(); 134 | 135 | std.log.info("generating inputs...", .{}); 136 | 137 | const size = 1 * 1024 * 1024; 138 | const y, const x = blk: { 139 | const y = try alloc.alloc(f32, size); 140 | const x = try alloc.alloc(f32, size); 141 | var rng = std.Random.DefaultPrng.init(0); 142 | const random = rng.random(); 143 | for (x) |*value| value.* = random.float(f32); 144 | for (y) |*value| value.* = random.float(f32); 145 | break :blk .{ y, x }; 146 | }; 147 | 148 | const results = try alloc.alloc(f32, size); 149 | 150 | const a: f32 = 10; 151 | 152 | const d_y = try cl.createBufferWithData(f32, context, .{ .read_write = true }, y); 153 | const d_x = try cl.createBufferWithData(f32, context, .{ .read_only = true }, x); 154 | 155 | std.log.info("launching kernel...", .{}); 156 | 157 | try kernel.setArg(@TypeOf(d_y), 0, d_y); 158 | try kernel.setArg(@TypeOf(d_x), 1, d_x); 159 | try kernel.setArg(f32, 2, a); 160 | 161 | const saxpy_complete = try queue.enqueueNDRangeKernel( 162 | kernel, 163 | null, 164 | &.{size}, 165 | &.{256}, 166 | &.{}, 167 | ); 168 | defer saxpy_complete.release(); 169 | 170 | const read_complete = try queue.enqueueReadBuffer( 171 | f32, 172 | d_y, 173 | false, 174 | 0, 175 | results, 176 | &.{saxpy_complete}, 177 | ); 178 | defer read_complete.release(); 179 | 180 | try cl.waitForEvents(&.{read_complete}); 181 | 182 | std.log.info("checking results...", .{}); 183 | 184 | // Compute reference results on host 185 | for (y, x) |*yi, xi| { 186 | yi.* += xi * a; 187 | } 188 | 189 | // Check if the results are close. 190 | // y = y + a * x is 2 operations of 0.5 ulp each, 191 | // multiply by 2 for host and device side error. 192 | const max_error = std.math.floatEps(f32) * 2 * 2; 193 | for (results, y, 0..) |ri, yi, i| { 194 | if (!std.math.approxEqRel(f32, ri, yi, max_error)) { 195 | fail("invalid result at index {}: expected = {d}, actual = {d}", .{ i, yi, ri }); 196 | } 197 | } 198 | 199 | std.log.info("ok", .{}); 200 | } 201 | -------------------------------------------------------------------------------- /src/opencl.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const assert = std.debug.assert; 3 | const Allocator = std.mem.Allocator; 4 | 5 | pub const c = @cImport({ 6 | @cInclude("CL/opencl.h"); 7 | }); 8 | 9 | pub const int = c.cl_int; 10 | pub const uint = c.cl_uint; 11 | pub const long = c.cl_long; 12 | pub const ulong = c.cl_ulong; 13 | 14 | pub const Version = packed struct(c.cl_version) { 15 | patch: u12, 16 | minor: u10, 17 | major: u10, 18 | }; 19 | 20 | pub const NameVersion = extern struct { 21 | comptime { 22 | assert(@sizeOf(NameVersion) == @sizeOf(c.cl_name_version)); 23 | } 24 | 25 | version: Version, 26 | name_raw: [c.CL_NAME_VERSION_MAX_NAME_SIZE]u8, 27 | 28 | pub fn getName(self: *const NameVersion) []const u8 { 29 | const len = std.mem.indexOfScalar(u8, &self.name_raw, 0).?; 30 | return self.name_raw[0..len]; 31 | } 32 | }; 33 | 34 | pub const DeviceType = packed struct(c.cl_device_type) { 35 | default: bool = false, 36 | cpu: bool = false, 37 | gpu: bool = false, 38 | accelerator: bool = false, 39 | custom: bool = false, 40 | _unused: u59 = 0, 41 | 42 | pub const all = DeviceType{ 43 | .cpu = true, 44 | .gpu = true, 45 | .accelerator = true, 46 | }; 47 | }; 48 | 49 | pub fn getPlatforms(a: Allocator) ![]const Platform { 50 | comptime std.debug.assert(@sizeOf(Platform) == @sizeOf(c.cl_platform_id)); 51 | 52 | var num_platforms: uint = undefined; 53 | switch (c.clGetPlatformIDs(0, null, &num_platforms)) { 54 | c.CL_SUCCESS => {}, 55 | c.CL_INVALID_VALUE => unreachable, 56 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 57 | else => @panic("Undocumented error"), 58 | } 59 | 60 | if (num_platforms == 0) { 61 | return &.{}; 62 | } 63 | 64 | const platforms = try a.alloc(Platform, num_platforms); 65 | errdefer a.free(platforms); 66 | 67 | switch (c.clGetPlatformIDs(num_platforms, @ptrCast(platforms.ptr), null)) { 68 | c.CL_SUCCESS => {}, 69 | c.CL_INVALID_VALUE => unreachable, 70 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 71 | else => @panic("Undocumented error"), 72 | } 73 | 74 | return platforms; 75 | } 76 | 77 | pub const Platform = extern struct { 78 | id: c.cl_platform_id, 79 | 80 | pub fn getName(platform: Platform, a: Allocator) ![:0]const u8 { 81 | var name_size: usize = undefined; 82 | switch (c.clGetPlatformInfo(platform.id, c.CL_PLATFORM_NAME, 0, null, &name_size)) { 83 | c.CL_SUCCESS => {}, 84 | c.CL_INVALID_PLATFORM => unreachable, 85 | c.CL_INVALID_VALUE => unreachable, 86 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 87 | else => @panic("Undocumented error"), 88 | } 89 | 90 | const name = try a.alloc(u8, name_size); 91 | errdefer a.free(name); 92 | 93 | switch (c.clGetPlatformInfo(platform.id, c.CL_PLATFORM_NAME, name.len, name.ptr, null)) { 94 | c.CL_SUCCESS => {}, 95 | c.CL_INVALID_VALUE => unreachable, 96 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 97 | else => @panic("Undocumented error"), 98 | } 99 | 100 | return name[0 .. name.len - 1 :0]; 101 | } 102 | 103 | pub fn getDevices(platform: Platform, a: Allocator, device_type: DeviceType) ![]const Device { 104 | comptime std.debug.assert(@sizeOf(Device) == @sizeOf(c.cl_device_id)); 105 | 106 | var num_devices: uint = undefined; 107 | switch (c.clGetDeviceIDs(platform.id, @bitCast(device_type), 0, null, &num_devices)) { 108 | c.CL_SUCCESS => {}, 109 | c.CL_INVALID_PLATFORM => unreachable, 110 | c.CL_INVALID_DEVICE_TYPE => unreachable, 111 | c.CL_INVALID_VALUE => unreachable, 112 | c.CL_DEVICE_NOT_FOUND => return &.{}, 113 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 114 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 115 | else => @panic("Undocumented error"), 116 | } 117 | 118 | if (num_devices == 0) { 119 | return &.{}; 120 | } 121 | 122 | const devices = try a.alloc(Device, num_devices); 123 | errdefer a.free(devices); 124 | 125 | switch (c.clGetDeviceIDs(platform.id, @bitCast(device_type), num_devices, @ptrCast(devices.ptr), null)) { 126 | c.CL_SUCCESS => {}, 127 | c.CL_INVALID_PLATFORM => unreachable, 128 | c.CL_INVALID_DEVICE_TYPE => unreachable, 129 | c.CL_INVALID_VALUE => unreachable, 130 | c.CL_DEVICE_NOT_FOUND => unreachable, 131 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 132 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 133 | else => @panic("Undocumented error"), 134 | } 135 | 136 | return devices; 137 | } 138 | }; 139 | 140 | pub const Device = extern struct { 141 | pub const Info = enum(uint) { 142 | type = c.CL_DEVICE_TYPE, 143 | max_compute_units = c.CL_DEVICE_MAX_COMPUTE_UNITS, 144 | 145 | pub fn Type(comptime info: Info) type { 146 | return switch (info) { 147 | .type => DeviceType, 148 | .max_compute_units => uint, 149 | }; 150 | } 151 | }; 152 | 153 | id: c.cl_device_id, 154 | 155 | pub fn getName(device: Device, a: Allocator) ![:0]const u8 { 156 | var name_size: usize = undefined; 157 | switch (c.clGetDeviceInfo(device.id, c.CL_DEVICE_NAME, 0, null, &name_size)) { 158 | c.CL_SUCCESS => {}, 159 | c.CL_INVALID_DEVICE => unreachable, 160 | c.CL_INVALID_VALUE => unreachable, 161 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 162 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 163 | else => @panic("Undocumented error"), 164 | } 165 | 166 | const name = try a.alloc(u8, name_size); 167 | errdefer a.free(name); 168 | 169 | switch (c.clGetDeviceInfo(device.id, c.CL_DEVICE_NAME, name.len, name.ptr, null)) { 170 | c.CL_SUCCESS => {}, 171 | c.CL_INVALID_DEVICE => unreachable, 172 | c.CL_INVALID_VALUE => unreachable, 173 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 174 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 175 | else => @panic("Undocumented error"), 176 | } 177 | 178 | return name[0 .. name.len - 1 :0]; 179 | } 180 | 181 | pub fn getILsWithVersion(device: Device, a: Allocator) ![]const NameVersion { 182 | var ils_size: usize = undefined; 183 | switch (c.clGetDeviceInfo(device.id, c.CL_DEVICE_ILS_WITH_VERSION, 0, null, &ils_size)) { 184 | c.CL_SUCCESS => {}, 185 | c.CL_INVALID_DEVICE => unreachable, 186 | c.CL_INVALID_VALUE => unreachable, 187 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 188 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 189 | else => @panic("Undocumented error"), 190 | } 191 | 192 | const ils = try a.alloc(NameVersion, ils_size / @sizeOf(NameVersion)); 193 | errdefer a.free(ils); 194 | @memset(@as([*]u8, @ptrCast(ils.ptr))[0..ils_size], 'A'); 195 | 196 | switch (c.clGetDeviceInfo(device.id, c.CL_DEVICE_ILS_WITH_VERSION, ils_size, ils.ptr, null)) { 197 | c.CL_SUCCESS => {}, 198 | c.CL_INVALID_DEVICE => unreachable, 199 | c.CL_INVALID_VALUE => unreachable, 200 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 201 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 202 | else => @panic("Undocumented error"), 203 | } 204 | 205 | return ils; 206 | } 207 | 208 | pub fn getInfo(device: Device, comptime info: Info) !info.Type() { 209 | var data: info.Type() = undefined; 210 | return switch (c.clGetDeviceInfo(device.id, @intFromEnum(info), @sizeOf(info.Type()), &data, null)) { 211 | c.CL_SUCCESS => data, 212 | c.CL_INVALID_DEVICE => unreachable, 213 | c.CL_INVALID_VALUE => unreachable, 214 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 215 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 216 | else => @panic("Undocumented error"), 217 | }; 218 | } 219 | }; 220 | 221 | pub const Context = extern struct { 222 | pub const Properties = struct { 223 | platform: ?Platform = null, 224 | }; 225 | 226 | handle: c.cl_context, 227 | 228 | pub fn create(devices: []const Device, properties: Properties) !Context { 229 | var cl_props = std.BoundedArray(c.cl_context_properties, 3).init(0) catch unreachable; 230 | 231 | if (properties.platform) |platform| { 232 | cl_props.appendAssumeCapacity(c.CL_CONTEXT_PLATFORM); 233 | cl_props.appendAssumeCapacity(@bitCast(@intFromPtr(platform.id))); 234 | } 235 | cl_props.appendAssumeCapacity(0); 236 | 237 | var status: int = undefined; 238 | const context = c.clCreateContext( 239 | &cl_props.buffer, 240 | @intCast(devices.len), 241 | @ptrCast(devices.ptr), 242 | null, 243 | null, 244 | &status, 245 | ); 246 | return switch (status) { 247 | c.CL_SUCCESS => .{ .handle = context }, 248 | c.CL_INVALID_PLATFORM => unreachable, 249 | c.CL_INVALID_PROPERTY => unreachable, 250 | c.CL_INVALID_VALUE => unreachable, 251 | c.CL_INVALID_DEVICE => unreachable, 252 | c.CL_DEVICE_NOT_AVAILABLE => error.DeviceNotAvailable, 253 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 254 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 255 | else => @panic("Undocumented error"), 256 | }; 257 | } 258 | 259 | pub fn release(context: Context) void { 260 | switch (c.clReleaseContext(context.handle)) { 261 | c.CL_SUCCESS => {}, 262 | c.CL_INVALID_CONTEXT => unreachable, 263 | // Ignore any errors 264 | c.CL_OUT_OF_RESOURCES => {}, 265 | c.CL_OUT_OF_HOST_MEMORY => {}, 266 | else => @panic("Undocumented error"), 267 | } 268 | } 269 | 270 | pub fn retain(context: Context) !void { 271 | switch (c.clRetainContext(context.handle)) { 272 | c.CL_SUCCESS => {}, 273 | c.CL_INVALID_CONTEXT => unreachable, 274 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 275 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 276 | else => @panic("Undocumented error"), 277 | } 278 | } 279 | }; 280 | 281 | pub const createContext = Context.create; 282 | 283 | pub const CommandQueueProperties = packed struct(c.cl_command_queue_properties) { 284 | out_of_order_exec_mode: bool = false, 285 | profiling: bool = false, 286 | on_device: bool = false, 287 | on_device_default: bool = false, 288 | _unused: u60 = 0, 289 | }; 290 | 291 | pub const CommandQueue = extern struct { 292 | handle: c.cl_command_queue, 293 | 294 | pub fn create(context: Context, device: Device, properties: CommandQueueProperties) !CommandQueue { 295 | var status: int = undefined; 296 | const queue = c.clCreateCommandQueue(context.handle, device.id, @bitCast(properties), &status); 297 | return switch (status) { 298 | c.CL_SUCCESS => .{ .handle = queue }, 299 | c.CL_INVALID_CONTEXT => unreachable, 300 | c.CL_INVALID_DEVICE => unreachable, 301 | c.CL_INVALID_VALUE => unreachable, 302 | c.CL_INVALID_QUEUE_PROPERTIES => unreachable, 303 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 304 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 305 | else => @panic("Undocumented error"), 306 | }; 307 | } 308 | 309 | pub fn release(queue: CommandQueue) void { 310 | switch (c.clReleaseCommandQueue(queue.handle)) { 311 | c.CL_SUCCESS => {}, 312 | c.CL_INVALID_COMMAND_QUEUE => unreachable, 313 | // Ignore any errors 314 | c.CL_OUT_OF_RESOURCES => {}, 315 | c.CL_OUT_OF_HOST_MEMORY => {}, 316 | else => @panic("Undocumented error"), 317 | } 318 | } 319 | 320 | pub fn retain(queue: CommandQueue) !void { 321 | switch (c.clRetainCommandQueue(queue.handle)) { 322 | c.CL_SUCCESS => {}, 323 | c.CL_INVALID_COMMAND_QUEUE => unreachable, 324 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 325 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 326 | else => @panic("Undocumented error"), 327 | } 328 | } 329 | 330 | pub fn enqueueNDRangeKernel( 331 | queue: CommandQueue, 332 | kernel: Kernel, 333 | maybe_global_work_offset: ?[]const usize, 334 | global_work_size: []const usize, 335 | local_work_size: []const usize, 336 | wait_list: []const Event, 337 | ) !Event { 338 | const work_dim = global_work_size.len; 339 | std.debug.assert(work_dim == local_work_size.len); 340 | if (maybe_global_work_offset) |global_work_offset| { 341 | std.debug.assert(work_dim == global_work_offset.len); 342 | } 343 | 344 | var event: Event = undefined; 345 | return switch (c.clEnqueueNDRangeKernel( 346 | queue.handle, 347 | kernel.handle, 348 | @intCast(work_dim), 349 | if (maybe_global_work_offset) |global_work_offset| global_work_offset.ptr else null, 350 | global_work_size.ptr, 351 | local_work_size.ptr, 352 | @intCast(wait_list.len), 353 | if (wait_list.len == 0) null else @ptrCast(wait_list.ptr), 354 | &event.handle, 355 | )) { 356 | c.CL_SUCCESS => event, 357 | c.CL_INVALID_PROGRAM_EXECUTABLE => unreachable, 358 | c.CL_INVALID_COMMAND_QUEUE => unreachable, 359 | c.CL_INVALID_KERNEL => unreachable, 360 | c.CL_INVALID_CONTEXT => unreachable, 361 | c.CL_INVALID_KERNEL_ARGS => unreachable, 362 | c.CL_INVALID_WORK_DIMENSION => unreachable, 363 | c.CL_INVALID_GLOBAL_WORK_SIZE => unreachable, 364 | c.CL_INVALID_GLOBAL_OFFSET => unreachable, 365 | c.CL_INVALID_WORK_GROUP_SIZE => unreachable, 366 | c.CL_INVALID_WORK_ITEM_SIZE => unreachable, 367 | c.CL_MISALIGNED_SUB_BUFFER_OFFSET => unreachable, 368 | c.CL_INVALID_IMAGE_SIZE => unreachable, 369 | c.CL_IMAGE_FORMAT_NOT_SUPPORTED => unreachable, 370 | c.CL_INVALID_EVENT_WAIT_LIST => unreachable, 371 | c.CL_INVALID_OPERATION => unreachable, 372 | c.CL_MEM_OBJECT_ALLOCATION_FAILURE => error.OutOfDeviceMemory, 373 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 374 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 375 | else => @panic("Undocumented error"), 376 | }; 377 | } 378 | 379 | pub fn enqueueWriteBuffer( 380 | queue: CommandQueue, 381 | comptime T: type, 382 | buffer: Buffer(T), 383 | blocking_write: bool, 384 | offset: usize, 385 | src: []const T, 386 | wait_list: []const Event, 387 | ) !Event { 388 | var event: Event = undefined; 389 | return switch (c.clEnqueueWriteBuffer( 390 | queue.handle, 391 | buffer.handle, 392 | if (blocking_write) c.CL_TRUE else c.CL_FALSE, 393 | offset * @sizeOf(T), 394 | src.len * @sizeOf(T), 395 | src.ptr, 396 | @intCast(wait_list.len), 397 | if (wait_list.len == 0) null else @ptrCast(wait_list.ptr), 398 | &event.handle, 399 | )) { 400 | c.CL_SUCCESS => event, 401 | c.CL_INVALID_COMMAND_QUEUE => unreachable, 402 | c.CL_INVALID_CONTEXT => unreachable, 403 | c.CL_INVALID_MEM_OBJECT => unreachable, 404 | c.CL_INVALID_VALUE => unreachable, 405 | c.CL_INVALID_EVENT_WAIT_LIST => unreachable, 406 | c.CL_MISALIGNED_SUB_BUFFER_OFFSET => unreachable, 407 | c.CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST => unreachable, 408 | c.CL_INVALID_OPERATION => error.InvalidOperation, 409 | c.CL_MEM_OBJECT_ALLOCATION_FAILURE => error.OutOfDeviceMemory, 410 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 411 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 412 | else => @panic("Undocumented error"), 413 | }; 414 | } 415 | 416 | pub fn enqueueReadBuffer( 417 | queue: CommandQueue, 418 | comptime T: type, 419 | buffer: Buffer(T), 420 | blocking_read: bool, 421 | offset: usize, 422 | dst: []T, 423 | wait_list: []const Event, 424 | ) !Event { 425 | var event: Event = undefined; 426 | return switch (c.clEnqueueReadBuffer( 427 | queue.handle, 428 | buffer.handle, 429 | if (blocking_read) c.CL_TRUE else c.CL_FALSE, 430 | offset * @sizeOf(T), 431 | dst.len * @sizeOf(T), 432 | dst.ptr, 433 | @intCast(wait_list.len), 434 | if (wait_list.len == 0) null else @ptrCast(wait_list.ptr), 435 | &event.handle, 436 | )) { 437 | c.CL_SUCCESS => event, 438 | c.CL_INVALID_COMMAND_QUEUE => unreachable, 439 | c.CL_INVALID_CONTEXT => unreachable, 440 | c.CL_INVALID_MEM_OBJECT => unreachable, 441 | c.CL_INVALID_VALUE => unreachable, 442 | c.CL_INVALID_EVENT_WAIT_LIST => unreachable, 443 | c.CL_MISALIGNED_SUB_BUFFER_OFFSET => unreachable, 444 | c.CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST => unreachable, 445 | c.CL_INVALID_OPERATION => error.InvalidOperation, 446 | c.CL_MEM_OBJECT_ALLOCATION_FAILURE => error.OutOfDeviceMemory, 447 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 448 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 449 | else => @panic("Undocumented error"), 450 | }; 451 | } 452 | }; 453 | 454 | pub const createCommandQueue = CommandQueue.create; 455 | 456 | pub const Program = extern struct { 457 | handle: c.cl_program, 458 | 459 | pub fn createWithSource(context: Context, source: [:0]const u8) !Program { 460 | return createWithSources(context, &.{source.ptr}, &.{source.len}); 461 | } 462 | 463 | pub fn createWithSources(context: Context, strings: []const [*:0]const u8, lengths: []const usize) !Program { 464 | std.debug.assert(strings.len == lengths.len); 465 | var status: int = undefined; 466 | const program = c.clCreateProgramWithSource( 467 | context.handle, 468 | @intCast(strings.len), 469 | @constCast(@ptrCast(strings.ptr)), 470 | lengths.ptr, 471 | &status, 472 | ); 473 | return switch (status) { 474 | c.CL_SUCCESS => .{ .handle = program }, 475 | c.CL_INVALID_CONTEXT => unreachable, 476 | c.CL_INVALID_VALUE => unreachable, 477 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 478 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 479 | else => @panic("Undocumented error"), 480 | }; 481 | } 482 | 483 | pub fn createWithIL(context: Context, il: []const u8) !Program { 484 | assert(il.len > 0); 485 | var status: int = undefined; 486 | const program = c.clCreateProgramWithIL( 487 | context.handle, 488 | il.ptr, 489 | il.len, 490 | &status, 491 | ); 492 | return switch (status) { 493 | c.CL_SUCCESS => .{ .handle = program }, 494 | c.CL_INVALID_CONTEXT => unreachable, 495 | c.CL_INVALID_OPERATION => return error.InvalidOperation, 496 | // * CL_INVALID_VALUE if il is NULL or if length is zero. 497 | // * CL_INVALID_VALUE if the length-byte block of memory pointed to 498 | // by il does not contain well-formed intermediate language input 499 | // that can be consumed by the OpenCL runtime. 500 | // 501 | // The former is caught by the assert, so this can only mean invalid IL. 502 | c.CL_INVALID_VALUE => error.InvalidIL, 503 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 504 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 505 | else => @panic("Undocumented error"), 506 | }; 507 | } 508 | 509 | pub fn release(program: Program) void { 510 | switch (c.clReleaseProgram(program.handle)) { 511 | c.CL_SUCCESS => {}, 512 | c.CL_INVALID_PROGRAM => unreachable, 513 | // Ignore any errors 514 | c.CL_OUT_OF_RESOURCES => {}, 515 | c.CL_OUT_OF_HOST_MEMORY => {}, 516 | else => @panic("Undocumented error"), 517 | } 518 | } 519 | 520 | pub fn retain(program: Program) !void { 521 | switch (c.clRetainProgram(program.handle)) { 522 | c.CL_SUCCESS => {}, 523 | c.CL_INVALID_PROGRAM => unreachable, 524 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 525 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 526 | else => @panic("Undocumented error"), 527 | } 528 | } 529 | 530 | pub fn build(program: Program, devices: []const Device, options: [*:0]const u8) !void { 531 | return switch (c.clBuildProgram( 532 | program.handle, 533 | @intCast(devices.len), 534 | @ptrCast(devices.ptr), 535 | options, 536 | null, 537 | null, 538 | )) { 539 | c.CL_SUCCESS => {}, 540 | c.CL_INVALID_PROGRAM => unreachable, 541 | c.CL_INVALID_VALUE => unreachable, 542 | c.CL_INVALID_DEVICE => unreachable, 543 | c.CL_INVALID_BINARY => unreachable, 544 | c.CL_INVALID_BUILD_OPTIONS => unreachable, 545 | c.CL_INVALID_OPERATION => unreachable, 546 | c.CL_COMPILER_NOT_AVAILABLE => error.CompilerNotAvailable, 547 | c.CL_BUILD_PROGRAM_FAILURE => error.BuildProgramFailure, 548 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 549 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 550 | else => @panic("Undocumented error"), 551 | }; 552 | } 553 | 554 | pub fn getBuildLog(program: Program, a: Allocator, device: Device) ![]const u8 { 555 | var log_size: usize = undefined; 556 | switch (c.clGetProgramBuildInfo( 557 | program.handle, 558 | device.id, 559 | c.CL_PROGRAM_BUILD_LOG, 560 | 0, 561 | null, 562 | &log_size, 563 | )) { 564 | c.CL_SUCCESS => {}, 565 | c.CL_INVALID_DEVICE => unreachable, 566 | c.CL_INVALID_VALUE => unreachable, 567 | c.CL_INVALID_PROGRAM => unreachable, 568 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 569 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 570 | else => @panic("Undocumented error"), 571 | } 572 | 573 | const log = try a.alloc(u8, log_size); 574 | errdefer a.free(log); 575 | 576 | return switch (c.clGetProgramBuildInfo( 577 | program.handle, 578 | device.id, 579 | c.CL_PROGRAM_BUILD_LOG, 580 | log_size, 581 | log.ptr, 582 | null, 583 | )) { 584 | c.CL_SUCCESS => log, 585 | c.CL_INVALID_DEVICE => unreachable, 586 | c.CL_INVALID_VALUE => unreachable, 587 | c.CL_INVALID_PROGRAM => unreachable, 588 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 589 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 590 | else => @panic("Undocumented error"), 591 | }; 592 | } 593 | }; 594 | 595 | pub const createProgramWithIL = Program.createWithIL; 596 | pub const createProgramWithSource = Program.createWithSource; 597 | pub const createProgramWithSources = Program.createWithSources; 598 | 599 | pub const Kernel = extern struct { 600 | handle: c.cl_kernel, 601 | 602 | pub fn create(program: Program, entrypoint: [*:0]const u8) !Kernel { 603 | var status: int = undefined; 604 | const kernel = c.clCreateKernel(program.handle, entrypoint, &status); 605 | return switch (status) { 606 | c.CL_SUCCESS => .{ .handle = kernel }, 607 | c.CL_INVALID_PROGRAM => unreachable, 608 | c.CL_INVALID_PROGRAM_EXECUTABLE => unreachable, 609 | c.CL_INVALID_KERNEL_NAME => error.InvalidKernelName, 610 | c.CL_INVALID_KERNEL_DEFINITION => error.InvalidKernelDefinition, 611 | c.CL_INVALID_VALUE => unreachable, 612 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 613 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 614 | else => @panic("Undocumented error"), 615 | }; 616 | } 617 | 618 | pub fn release(kernel: Kernel) void { 619 | switch (c.clReleaseKernel(kernel.handle)) { 620 | c.CL_SUCCESS => {}, 621 | c.CL_INVALID_KERNEL => unreachable, 622 | // Ignore any errors 623 | c.CL_OUT_OF_RESOURCES => {}, 624 | c.CL_OUT_OF_HOST_MEMORY => {}, 625 | else => @panic("Undocumented error"), 626 | } 627 | } 628 | 629 | pub fn retain(kernel: Kernel) !void { 630 | switch (c.clRetainKernel(kernel.handle)) { 631 | c.CL_SUCCESS => {}, 632 | c.CL_INVALID_KERNEL => unreachable, 633 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 634 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 635 | else => @panic("Undocumented error"), 636 | } 637 | } 638 | 639 | pub fn setArg(kernel: Kernel, comptime T: type, index: uint, value: T) !void { 640 | return switch (c.clSetKernelArg( 641 | kernel.handle, 642 | index, 643 | @sizeOf(T), 644 | @ptrCast(&value), 645 | )) { 646 | c.CL_SUCCESS => {}, 647 | // TODO: Should these be errors? 648 | c.CL_INVALID_ARG_INDEX => unreachable, 649 | c.CL_INVALID_ARG_VALUE => unreachable, 650 | c.CL_INVALID_MEM_OBJECT => unreachable, 651 | c.CL_INVALID_SAMPLER => unreachable, 652 | c.CL_INVALID_DEVICE_QUEUE => unreachable, 653 | c.CL_INVALID_ARG_SIZE => unreachable, 654 | c.CL_MAX_SIZE_RESTRICTION_EXCEEDED => unreachable, 655 | c.CL_OUT_OF_RESOURCES => error.OutOfResources, 656 | c.CL_OUT_OF_HOST_MEMORY => error.OutOfMemory, 657 | else => @panic("Undocumented error"), 658 | }; 659 | } 660 | }; 661 | 662 | pub const createKernel = Kernel.create; 663 | 664 | pub const Event = extern struct { 665 | handle: c.cl_event, 666 | 667 | pub fn release(event: Event) void { 668 | switch (c.clReleaseEvent(event.handle)) { 669 | c.CL_SUCCESS => {}, 670 | c.CL_INVALID_EVENT => unreachable, 671 | // Ignore any errors 672 | c.CL_OUT_OF_RESOURCES => {}, 673 | c.CL_OUT_OF_HOST_MEMORY => {}, 674 | else => @panic("Undocumented error"), 675 | } 676 | } 677 | 678 | pub fn retain(event: Event) !void { 679 | switch (c.clRetainEvent(event.handle)) { 680 | c.CL_SUCCESS => {}, 681 | c.CL_INVALID_EVENT => unreachable, 682 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 683 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 684 | else => @panic("Undocumented error"), 685 | } 686 | } 687 | 688 | pub fn commandQueuedTime(event: Event) !u64 { 689 | return event.getProfilingInfo(c.CL_PROFILING_COMMAND_QUEUED); 690 | } 691 | 692 | pub fn commandSubmitTime(event: Event) !u64 { 693 | return event.getProfilingInfo(c.CL_PROFILING_COMMAND_SUBMIT); 694 | } 695 | 696 | pub fn commandStartTime(event: Event) !u64 { 697 | return event.getProfilingInfo(c.CL_PROFILING_COMMAND_START); 698 | } 699 | 700 | pub fn commandEndTime(event: Event) !u64 { 701 | return event.getProfilingInfo(c.CL_PROFILING_COMMAND_END); 702 | } 703 | 704 | pub fn commandCompleteTime(event: Event) !u64 { 705 | return event.getProfilingInfo(c.CL_PROFILING_COMMAND_COMPLETE); 706 | } 707 | 708 | fn getProfilingInfo(event: Event, info: c.cl_profiling_info) !u64 { 709 | var value: ulong = undefined; 710 | return switch (c.clGetEventProfilingInfo(event.handle, info, @sizeOf(ulong), &value, null)) { 711 | c.CL_SUCCESS => value, 712 | c.CL_PROFILING_INFO_NOT_AVAILABLE => unreachable, 713 | c.CL_INVALID_VALUE => unreachable, 714 | c.CL_INVALID_EVENT => unreachable, 715 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 716 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 717 | else => @panic("Undocumented error"), 718 | }; 719 | } 720 | }; 721 | 722 | pub fn waitForEvents(events: []const Event) !void { 723 | return switch (c.clWaitForEvents(@intCast(events.len), @ptrCast(events.ptr))) { 724 | c.CL_SUCCESS => {}, 725 | c.CL_INVALID_VALUE => unreachable, 726 | c.CL_INVALID_CONTEXT => unreachable, 727 | c.CL_INVALID_EVENT => unreachable, 728 | c.CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST => unreachable, 729 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 730 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 731 | else => @panic("Undocumented error"), 732 | }; 733 | } 734 | 735 | pub const MemFlags = packed struct(c.cl_mem_flags) { 736 | read_write: bool = false, 737 | write_only: bool = false, 738 | read_only: bool = false, 739 | use_host_ptr: bool = false, 740 | alloc_host_ptr: bool = false, 741 | copy_host_ptr: bool = false, 742 | _reserved0: u1 = 0, 743 | host_write_only: bool = false, 744 | host_read_only: bool = false, 745 | host_no_access: bool = false, 746 | svm_fine_grain_buffer: bool = false, 747 | svm_atomics: bool = false, 748 | kernel_read_and_write: bool = false, 749 | _unused: u51 = 0, 750 | }; 751 | 752 | pub fn Buffer(comptime T: type) type { 753 | return extern struct { 754 | const Self = @This(); 755 | 756 | handle: c.cl_mem, 757 | 758 | pub fn create(context: Context, flags: MemFlags, size: usize) !Self { 759 | return createInternal(context, flags, size, null); 760 | } 761 | 762 | pub fn createWithData(context: Context, flags: MemFlags, data: []const T) !Self { 763 | var new_flags = flags; 764 | new_flags.copy_host_ptr = true; 765 | return createInternal(context, new_flags, data.len, @ptrCast(data.ptr)); 766 | } 767 | 768 | fn createInternal(context: Context, flags: MemFlags, size: usize, host_ptr: ?*const T) !Self { 769 | var status: int = undefined; 770 | const buffer = c.clCreateBuffer( 771 | context.handle, 772 | @bitCast(flags), 773 | size * @sizeOf(T), 774 | @constCast(host_ptr), 775 | &status, 776 | ); 777 | return switch (status) { 778 | c.CL_SUCCESS => .{ .handle = buffer }, 779 | c.CL_INVALID_CONTEXT => unreachable, 780 | c.CL_INVALID_PROPERTY => unreachable, 781 | c.CL_INVALID_VALUE => unreachable, 782 | c.CL_INVALID_BUFFER_SIZE => unreachable, 783 | c.CL_INVALID_HOST_PTR => unreachable, 784 | c.CL_MEM_OBJECT_ALLOCATION_FAILURE => return error.OutOfDeviceMemory, 785 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 786 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 787 | else => @panic("Undocumented error"), 788 | }; 789 | } 790 | 791 | pub fn release(buffer: Self) void { 792 | switch (c.clReleaseMemObject(buffer.handle)) { 793 | c.CL_SUCCESS => {}, 794 | c.CL_INVALID_MEM_OBJECT => unreachable, 795 | // Ignore any errors 796 | c.CL_OUT_OF_RESOURCES => {}, 797 | c.CL_OUT_OF_HOST_MEMORY => {}, 798 | else => @panic("Undocumented error"), 799 | } 800 | } 801 | 802 | pub fn retain(buffer: Self) !void { 803 | switch (c.clRetainMemObject(buffer.handle)) { 804 | c.CL_SUCCESS => {}, 805 | c.CL_INVALID_MEM_OBJECT => unreachable, 806 | c.CL_OUT_OF_RESOURCES => return error.OutOfResources, 807 | c.CL_OUT_OF_HOST_MEMORY => return error.OutOfMemory, 808 | else => @panic("Undocumented error"), 809 | } 810 | } 811 | }; 812 | } 813 | 814 | pub fn createBuffer(comptime T: type, context: Context, flags: MemFlags, size: usize) !Buffer(T) { 815 | return try Buffer(T).create(context, flags, size); 816 | } 817 | 818 | pub fn createBufferWithData(comptime T: type, context: Context, flags: MemFlags, data: []const T) !Buffer(T) { 819 | return try Buffer(T).createWithData(context, flags, data); 820 | } 821 | --------------------------------------------------------------------------------