286 lines
11 KiB
Zig
286 lines
11 KiB
Zig
const std = @import("std");
|
|
const ffi = @import("ffi.zig");
|
|
const spv = ffi.spv;
|
|
|
|
const CSpecializationEntry = extern struct {
|
|
id: spv.SpvWord,
|
|
offset: c_ulong,
|
|
size: c_ulong,
|
|
};
|
|
|
|
const LocationType = enum(c_int) {
|
|
input = 0,
|
|
output = 1,
|
|
};
|
|
|
|
const Vec4f = extern struct {
|
|
x: f32,
|
|
y: f32,
|
|
z: f32,
|
|
w: f32,
|
|
};
|
|
|
|
const Vec4u = extern struct {
|
|
x: c_uint,
|
|
y: c_uint,
|
|
z: c_uint,
|
|
w: c_uint,
|
|
};
|
|
|
|
const readImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, x: c_int, y: c_int, z: c_int, dst: *Vec4f) callconv(.c) ffi.Result;
|
|
const readImageInt4_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, x: c_int, y: c_int, z: c_int, dst: *Vec4u) callconv(.c) ffi.Result;
|
|
const writeImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, x: c_int, y: c_int, z: c_int, src: Vec4f) callconv(.c) ffi.Result;
|
|
const writeImageInt4_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, x: c_int, y: c_int, z: c_int, src: Vec4u) callconv(.c) ffi.Result;
|
|
const sampleImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, driver_sampler: ?*anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, dst: *Vec4f) callconv(.c) ffi.Result;
|
|
const queryImageSize_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, arrayed: ffi.SpvCBool, dst: *Vec4u) callconv(.c) ffi.Result;
|
|
|
|
const ImageAPI = extern struct {
|
|
readImageFloat4: readImageFloat4_PFN,
|
|
readImageInt4: readImageInt4_PFN,
|
|
writeImageFloat4: writeImageFloat4_PFN,
|
|
writeImageInt4: writeImageInt4_PFN,
|
|
sampleImageFloat4: sampleImageFloat4_PFN,
|
|
queryImageSize: queryImageSize_PFN,
|
|
};
|
|
|
|
fn toCResult(err: spv.Runtime.RuntimeError) ffi.Result {
|
|
return switch (err) {
|
|
spv.Runtime.RuntimeError.Barrier => ffi.Result.Barrier,
|
|
spv.Runtime.RuntimeError.DivisionByZero => ffi.Result.DivisionByZero,
|
|
spv.Runtime.RuntimeError.InvalidEntryPoint => ffi.Result.InvalidEntryPoint,
|
|
spv.Runtime.RuntimeError.InvalidSpirV => ffi.Result.InvalidSpirV,
|
|
spv.Runtime.RuntimeError.InvalidValueType => ffi.Result.InvalidValueType,
|
|
spv.Runtime.RuntimeError.Killed => ffi.Result.Killed,
|
|
spv.Runtime.RuntimeError.NotFound => ffi.Result.NotFound,
|
|
spv.Runtime.RuntimeError.OutOfBounds => ffi.Result.OutOfBounds,
|
|
spv.Runtime.RuntimeError.OutOfMemory => ffi.Result.OutOfMemory,
|
|
spv.Runtime.RuntimeError.ToDo => ffi.Result.ToDo,
|
|
spv.Runtime.RuntimeError.Unknown => ffi.Result.Unknown,
|
|
spv.Runtime.RuntimeError.Unreachable => ffi.Result.Unreachable,
|
|
spv.Runtime.RuntimeError.UnsupportedExtension => ffi.Result.UnsupportedExtension,
|
|
spv.Runtime.RuntimeError.UnsupportedSpirV => ffi.Result.UnsupportedSpirV,
|
|
};
|
|
}
|
|
|
|
fn fromCResult(res: ffi.Result) spv.Runtime.RuntimeError!void {
|
|
return switch (res) {
|
|
ffi.Result.Barrier => spv.Runtime.RuntimeError.Barrier,
|
|
ffi.Result.DivisionByZero => spv.Runtime.RuntimeError.DivisionByZero,
|
|
ffi.Result.InvalidEntryPoint => spv.Runtime.RuntimeError.InvalidEntryPoint,
|
|
ffi.Result.InvalidSpirV => spv.Runtime.RuntimeError.InvalidSpirV,
|
|
ffi.Result.InvalidValueType => spv.Runtime.RuntimeError.InvalidValueType,
|
|
ffi.Result.Killed => spv.Runtime.RuntimeError.Killed,
|
|
ffi.Result.NotFound => spv.Runtime.RuntimeError.NotFound,
|
|
ffi.Result.OutOfBounds => spv.Runtime.RuntimeError.OutOfBounds,
|
|
ffi.Result.OutOfMemory => spv.Runtime.RuntimeError.OutOfMemory,
|
|
ffi.Result.ToDo => spv.Runtime.RuntimeError.ToDo,
|
|
ffi.Result.Unknown => spv.Runtime.RuntimeError.Unknown,
|
|
ffi.Result.Unreachable => spv.Runtime.RuntimeError.Unreachable,
|
|
ffi.Result.UnsupportedExtension => spv.Runtime.RuntimeError.UnsupportedExtension,
|
|
ffi.Result.UnsupportedSpirV => spv.Runtime.RuntimeError.UnsupportedSpirV,
|
|
else => {},
|
|
};
|
|
}
|
|
|
|
/// Hacky wrapper
|
|
const ImageAPIBridge = struct {
|
|
threadlocal var current_image_api: ?*const ImageAPI = null;
|
|
|
|
fn getImageAPI() spv.Runtime.RuntimeError!*const ImageAPI {
|
|
return current_image_api orelse spv.Runtime.RuntimeError.Unknown;
|
|
}
|
|
|
|
fn readImageFloat4(driver_image: *anyopaque, dim: spv.spv.SpvDim, x: i32, y: i32, z: i32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(f32) {
|
|
const image_api = try getImageAPI();
|
|
|
|
var dst: Vec4f = undefined;
|
|
const result = image_api.readImageFloat4(driver_image, dim, @intCast(x), @intCast(y), @intCast(z), &dst);
|
|
|
|
try fromCResult(result);
|
|
|
|
return .{
|
|
.x = dst.x,
|
|
.y = dst.y,
|
|
.z = dst.z,
|
|
.w = dst.w,
|
|
};
|
|
}
|
|
|
|
fn readImageInt4(driver_image: *anyopaque, dim: spv.spv.SpvDim, x: i32, y: i32, z: i32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
|
|
const image_api = try getImageAPI();
|
|
|
|
var dst: Vec4u = undefined;
|
|
const result = image_api.readImageInt4(driver_image, dim, @intCast(x), @intCast(y), @intCast(z), &dst);
|
|
|
|
try fromCResult(result);
|
|
|
|
return .{
|
|
.x = dst.x,
|
|
.y = dst.y,
|
|
.z = dst.z,
|
|
.w = dst.w,
|
|
};
|
|
}
|
|
|
|
fn writeImageFloat4(driver_image: *anyopaque, dim: spv.spv.SpvDim, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(f32)) spv.Runtime.RuntimeError!void {
|
|
const image_api = try getImageAPI();
|
|
|
|
const result = image_api.writeImageFloat4(driver_image, dim, @intCast(x), @intCast(y), @intCast(z), .{ .x = pixel.x, .y = pixel.y, .z = pixel.z, .w = pixel.w });
|
|
|
|
try fromCResult(result);
|
|
}
|
|
|
|
fn writeImageInt4(driver_image: *anyopaque, dim: spv.spv.SpvDim, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(u32)) spv.Runtime.RuntimeError!void {
|
|
const image_api = try getImageAPI();
|
|
|
|
const result = image_api.writeImageInt4(driver_image, dim, @intCast(x), @intCast(y), @intCast(z), .{ .x = pixel.x, .y = pixel.y, .z = pixel.z, .w = pixel.w });
|
|
|
|
try fromCResult(result);
|
|
}
|
|
|
|
fn sampleImageFloat4(driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(f32) {
|
|
const image_api = try getImageAPI();
|
|
|
|
var dst: Vec4f = undefined;
|
|
const result = image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, &dst);
|
|
|
|
try fromCResult(result);
|
|
|
|
return .{
|
|
.x = dst.x,
|
|
.y = dst.y,
|
|
.z = dst.z,
|
|
.w = dst.w,
|
|
};
|
|
}
|
|
|
|
fn queryImageSize(driver_image: *anyopaque, dim: spv.spv.SpvDim, arrayed: bool) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
|
|
const image_api = try getImageAPI();
|
|
|
|
var dst: Vec4u = undefined;
|
|
const result = image_api.queryImageSize(driver_image, dim, if (arrayed) 1 else 0, &dst);
|
|
|
|
try fromCResult(result);
|
|
|
|
return .{
|
|
.x = dst.x,
|
|
.y = dst.y,
|
|
.z = dst.z,
|
|
.w = dst.w,
|
|
};
|
|
}
|
|
};
|
|
|
|
const RuntimeWrapper = struct {
|
|
rt: spv.Runtime,
|
|
image_api: ImageAPI,
|
|
};
|
|
|
|
export fn SpvInitRuntime(rt: **RuntimeWrapper, module: *spv.Module, image_api: ImageAPI) callconv(.c) ffi.Result {
|
|
const allocator = std.heap.c_allocator;
|
|
|
|
rt.* = allocator.create(RuntimeWrapper) catch return .OutOfMemory;
|
|
rt.*.image_api = image_api;
|
|
|
|
rt.*.rt = spv.Runtime.init(
|
|
allocator,
|
|
module,
|
|
.{
|
|
.readImageFloat4 = ImageAPIBridge.readImageFloat4,
|
|
.readImageInt4 = ImageAPIBridge.readImageInt4,
|
|
.writeImageFloat4 = ImageAPIBridge.writeImageFloat4,
|
|
.writeImageInt4 = ImageAPIBridge.writeImageInt4,
|
|
.sampleImageFloat4 = ImageAPIBridge.sampleImageFloat4,
|
|
.queryImageSize = ImageAPIBridge.queryImageSize,
|
|
},
|
|
) catch |err| {
|
|
allocator.destroy(rt.*);
|
|
return toCResult(err);
|
|
};
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvDeinitRuntime(rt: *RuntimeWrapper) callconv(.c) void {
|
|
const allocator = std.heap.c_allocator;
|
|
rt.rt.deinit(allocator);
|
|
allocator.destroy(rt);
|
|
}
|
|
|
|
export fn SpvAddSpecializationInfo(rt: *RuntimeWrapper, entry: CSpecializationEntry, data: [*]const u8, data_size: c_ulong) callconv(.c) ffi.Result {
|
|
const allocator = std.heap.c_allocator;
|
|
rt.rt.addSpecializationInfo(
|
|
allocator,
|
|
.{
|
|
.id = entry.id,
|
|
.offset = @intCast(entry.offset),
|
|
.size = @intCast(entry.size),
|
|
},
|
|
data[0..data_size],
|
|
) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvGetEntryPointByName(rt: *RuntimeWrapper, name: [*:0]const u8, result: *spv.SpvWord) callconv(.c) ffi.Result {
|
|
result.* = rt.rt.getEntryPointByName(std.mem.span(name)) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvGetResultByLocation(rt: *RuntimeWrapper, location: spv.SpvWord, kind: LocationType, result: *spv.SpvWord) callconv(.c) ffi.Result {
|
|
result.* = rt.rt.getResultByLocation(location, switch (kind) {
|
|
.input => .input,
|
|
.output => .output,
|
|
}) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvGetResultByName(rt: *RuntimeWrapper, name: [*:0]const u8, result: *spv.SpvWord) callconv(.c) ffi.Result {
|
|
result.* = rt.rt.getResultByName(std.mem.span(name)) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvGetResultMemorySize(rt: *RuntimeWrapper, result: spv.SpvWord, size: *c_ulong) callconv(.c) ffi.Result {
|
|
size.* = rt.rt.getResultMemorySize(result) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvHasResultDecoration(rt: *RuntimeWrapper, result: spv.SpvWord, decoration: spv.spv.SpvDecoration) callconv(.c) c_int {
|
|
return if (rt.rt.hasResultDecoration(result, decoration)) 1 else 0;
|
|
}
|
|
|
|
export fn SpvCallEntryPoint(rt: *RuntimeWrapper, entry_point: spv.SpvWord) callconv(.c) ffi.Result {
|
|
const allocator = std.heap.c_allocator;
|
|
|
|
// Ultra hacky
|
|
const previous_image_api = ImageAPIBridge.current_image_api;
|
|
ImageAPIBridge.current_image_api = &rt.image_api;
|
|
defer ImageAPIBridge.current_image_api = previous_image_api;
|
|
|
|
rt.rt.callEntryPoint(allocator, entry_point) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvReadOutput(rt: *RuntimeWrapper, output: [*]u8, output_size: c_ulong, result: spv.SpvWord) callconv(.c) ffi.Result {
|
|
rt.rt.readOutput(output[0..output_size], result) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvReadBuiltIn(rt: *RuntimeWrapper, output: [*]u8, output_size: c_ulong, builtin: spv.spv.SpvBuiltIn) callconv(.c) ffi.Result {
|
|
rt.rt.readBuiltIn(output[0..output_size], builtin) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvWriteInput(rt: *RuntimeWrapper, input: [*]const u8, input_size: c_ulong, result: spv.SpvWord) callconv(.c) ffi.Result {
|
|
rt.rt.writeInput(input[0..input_size], result) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvWriteBuiltIn(rt: *RuntimeWrapper, input: [*]const u8, input_size: c_ulong, builtin: spv.spv.SpvBuiltIn) callconv(.c) ffi.Result {
|
|
rt.rt.writeBuiltIn(input[0..input_size], builtin) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|
|
|
|
export fn SpvWriteDescriptorSet(rt: *RuntimeWrapper, input: [*]const u8, input_size: c_ulong, set: spv.SpvWord, binding: spv.SpvWord, descriptor_index: spv.SpvWord) callconv(.c) ffi.Result {
|
|
rt.rt.writeDescriptorSet(input[0..input_size], set, binding, descriptor_index) catch |err| return toCResult(err);
|
|
return .Success;
|
|
}
|