From ad87703ea872a27d7191ec778ddc5828e237a605 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Fri, 5 Jun 2026 17:48:21 +0200 Subject: [PATCH] adding util to runtime api --- ffi/SpirvInterpreter.h | 24 +++++++++++ ffi/runtime.zig | 93 ++++++++++++++++++++++++++++++++++++++++++ src/Runtime.zig | 17 +++++++- src/Value.zig | 33 +++++++++++++++ 4 files changed, 166 insertions(+), 1 deletion(-) diff --git a/ffi/SpirvInterpreter.h b/ffi/SpirvInterpreter.h index 2833fbb..f4b692d 100644 --- a/ffi/SpirvInterpreter.h +++ b/ffi/SpirvInterpreter.h @@ -452,6 +452,20 @@ typedef enum SPV_LOCATION_OUTPUT = 1 } SpvLocationType; +typedef enum +{ + SPV_ENTRY_POINT_COMPLETED = 0, + SPV_ENTRY_POINT_BARRIER = 1 +} SpvEntryPointStatus; + +typedef enum +{ + SPV_PRIMITIVE_BOOL = 0, + SPV_PRIMITIVE_FLOAT = 1, + SPV_PRIMITIVE_SINT = 2, + SPV_PRIMITIVE_UINT = 3 +} SpvPrimitiveType; + typedef struct { float x; @@ -497,19 +511,29 @@ SPV_API void SpvDeinitRuntime(SpvRuntime runtime); SPV_API SpvResult SpvFlushDescriptorSets(SpvRuntime runtime); SPV_API SpvResult SpvAddSpecializationInfo(SpvRuntime runtime, SpvRuntimeSpecializationEntry entry, const SpvByte* data, SpvSize data_size); +SPV_API SpvResult SpvPopulatePushConstants(SpvRuntime runtime, const SpvByte* data, SpvSize data_size); SPV_API SpvResult SpvGetResultByName(SpvRuntime runtime, const char* name, SpvWord* result); SPV_API SpvResult SpvGetResultLocation(SpvRuntime runtime, SpvWord location, SpvLocationType type, SpvWord* result); +SPV_API SpvResult SpvGetResultByLocation(SpvRuntime runtime, SpvWord location, SpvLocationType type, SpvWord* result); +SPV_API SpvResult SpvGetResultByLocationComponent(SpvRuntime runtime, SpvWord location, SpvWord component, SpvLocationType type, SpvWord* result); SPV_API SpvResult SpvGetEntryPointByName(SpvRuntime runtime, const char* name, SpvWord* result); SPV_API SpvResult SpvGetResultMemorySize(SpvRuntime runtime, SpvWord result, SpvSize* size); +SPV_API SpvResult SpvGetInputLocationMemorySize(SpvRuntime runtime, SpvWord location, SpvSize* size); +SPV_API SpvResult SpvGetResultPrimitiveType(SpvRuntime runtime, SpvWord result, SpvPrimitiveType* primitive_type); +SPV_API SpvBool SpvResultIsInteger(SpvRuntime runtime, SpvWord result); SPV_API SpvBool SpvHasResultDecoration(SpvRuntime runtime, SpvWord result, SpvDecoration decoration); SPV_API SpvResult SpvCallEntryPoint(SpvRuntime runtime, SpvWord entry_point_index); +SPV_API SpvResult SpvBeginEntryPoint(SpvRuntime runtime, SpvWord entry_point_index, SpvEntryPointStatus* status); +SPV_API SpvResult SpvContinueEntryPoint(SpvRuntime runtime, SpvEntryPointStatus* status); +SPV_API void SpvResetInvocation(SpvRuntime runtime); SPV_API SpvResult SpvReadOutput(SpvRuntime runtime, SpvByte* output, SpvSize output_size, SpvWord result); SPV_API SpvResult SpvReadBuiltIn(SpvRuntime runtime, SpvByte* output, SpvSize output_size, SpvBuiltIn builtin); SPV_API SpvResult SpvWriteInput(SpvRuntime runtime, const SpvByte* input, SpvSize input_size, SpvWord result); +SPV_API SpvResult SpvWriteInputLocation(SpvRuntime runtime, const SpvByte* input, SpvSize input_size, SpvWord location); SPV_API SpvResult SpvWriteBuiltIn(SpvRuntime runtime, const SpvByte* input, SpvSize input_size, SpvBuiltIn builtin); SPV_API SpvResult SpvWriteDescriptorSet(SpvRuntime runtime, const SpvByte* input, SpvSize input_size, SpvWord set, SpvWord binding, SpvWord descriptor_index); diff --git a/ffi/runtime.zig b/ffi/runtime.zig index a4e31bb..e6f4817 100644 --- a/ffi/runtime.zig +++ b/ffi/runtime.zig @@ -13,6 +13,18 @@ const LocationType = enum(c_int) { output = 1, }; +const EntryPointStatus = enum(c_int) { + completed = 0, + barrier = 1, +}; + +const PrimitiveType = enum(c_int) { + bool = 0, + float = 1, + sint = 2, + uint = 3, +}; + const Vec4f = extern struct { x: f32, y: f32, @@ -82,6 +94,13 @@ fn fromCResult(res: ffi.Result) spv.Runtime.RuntimeError!void { }; } +fn toCEntryPointStatus(status: spv.Runtime.EntryPointStatus) EntryPointStatus { + return switch (status) { + .completed => .completed, + .barrier => .barrier, + }; +} + /// Hacky wrapper const ImageAPIBridge = struct { threadlocal var current_image_api: ?*const ImageAPI = null; @@ -206,6 +225,12 @@ export fn SpvDeinitRuntime(rt: *RuntimeWrapper) callconv(.c) void { allocator.destroy(rt); } +export fn SpvFlushDescriptorSets(rt: *RuntimeWrapper) callconv(.c) ffi.Result { + const allocator = std.heap.c_allocator; + rt.rt.flushDescriptorSets(allocator) catch |err| return toCResult(err); + return .Success; +} + 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( @@ -220,6 +245,11 @@ export fn SpvAddSpecializationInfo(rt: *RuntimeWrapper, entry: CSpecializationEn return .Success; } +export fn SpvPopulatePushConstants(rt: *RuntimeWrapper, data: [*]const u8, data_size: c_ulong) callconv(.c) ffi.Result { + rt.rt.populatePushConstants(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; @@ -233,6 +263,18 @@ export fn SpvGetResultByLocation(rt: *RuntimeWrapper, location: spv.SpvWord, kin return .Success; } +export fn SpvGetResultLocation(rt: *RuntimeWrapper, location: spv.SpvWord, kind: LocationType, result: *spv.SpvWord) callconv(.c) ffi.Result { + return SpvGetResultByLocation(rt, location, kind, result); +} + +export fn SpvGetResultByLocationComponent(rt: *RuntimeWrapper, location: spv.SpvWord, component: spv.SpvWord, kind: LocationType, result: *spv.SpvWord) callconv(.c) ffi.Result { + result.* = rt.rt.getResultByLocationComponent(location, component, 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; @@ -243,6 +285,25 @@ export fn SpvGetResultMemorySize(rt: *RuntimeWrapper, result: spv.SpvWord, size: return .Success; } +export fn SpvGetInputLocationMemorySize(rt: *RuntimeWrapper, location: spv.SpvWord, size: *c_ulong) callconv(.c) ffi.Result { + size.* = rt.rt.getInputLocationMemorySize(location) catch |err| return toCResult(err); + return .Success; +} + +export fn SpvGetResultPrimitiveType(rt: *RuntimeWrapper, result: spv.SpvWord, primitive_type: *PrimitiveType) callconv(.c) ffi.Result { + primitive_type.* = switch (rt.rt.getResultPrimitiveType(result) catch |err| return toCResult(err)) { + .Bool => .bool, + .Float => .float, + .SInt => .sint, + .UInt => .uint, + }; + return .Success; +} + +export fn SpvResultIsInteger(rt: *RuntimeWrapper, result: spv.SpvWord) callconv(.c) c_int { + return if (rt.rt.resultIsInteger(result)) 1 else 0; +} + 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; } @@ -259,6 +320,33 @@ export fn SpvCallEntryPoint(rt: *RuntimeWrapper, entry_point: spv.SpvWord) callc return .Success; } +export fn SpvBeginEntryPoint(rt: *RuntimeWrapper, entry_point: spv.SpvWord, status: *EntryPointStatus) callconv(.c) ffi.Result { + const allocator = std.heap.c_allocator; + + const previous_image_api = ImageAPIBridge.current_image_api; + ImageAPIBridge.current_image_api = &rt.image_api; + defer ImageAPIBridge.current_image_api = previous_image_api; + + status.* = toCEntryPointStatus(rt.rt.beginEntryPoint(allocator, entry_point) catch |err| return toCResult(err)); + return .Success; +} + +export fn SpvContinueEntryPoint(rt: *RuntimeWrapper, status: *EntryPointStatus) callconv(.c) ffi.Result { + const allocator = std.heap.c_allocator; + + const previous_image_api = ImageAPIBridge.current_image_api; + ImageAPIBridge.current_image_api = &rt.image_api; + defer ImageAPIBridge.current_image_api = previous_image_api; + + status.* = toCEntryPointStatus(rt.rt.continueEntryPoint(allocator) catch |err| return toCResult(err)); + return .Success; +} + +export fn SpvResetInvocation(rt: *RuntimeWrapper) callconv(.c) void { + const allocator = std.heap.c_allocator; + rt.rt.resetInvocation(allocator); +} + 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; @@ -274,6 +362,11 @@ export fn SpvWriteInput(rt: *RuntimeWrapper, input: [*]const u8, input_size: c_u return .Success; } +export fn SpvWriteInputLocation(rt: *RuntimeWrapper, input: [*]const u8, input_size: c_ulong, location: spv.SpvWord) callconv(.c) ffi.Result { + rt.rt.writeInputLocation(input[0..input_size], location) 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; diff --git a/src/Runtime.zig b/src/Runtime.zig index 2dbcc80..468ae99 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -12,7 +12,9 @@ const SpvWord = spv.SpvWord; const SpvBool = spv.SpvBool; const Module = @import("Module.zig"); +const PrimitiveType = @import("Value.zig").PrimitiveType; const Result = @import("Result.zig"); +const Value = @import("Value.zig").Value; const WordIterator = @import("WordIterator.zig"); const Self = @This(); @@ -195,6 +197,19 @@ pub fn getResultByLocationComponent(self: *const Self, location: SpvWord, compon return RuntimeError.NotFound; } +pub fn getResultPrimitiveType(self: *const Self, result: SpvWord) RuntimeError!PrimitiveType { + if (result >= self.results.len) + return RuntimeError.OutOfBounds; + return (try self.results[result].getConstValue()).resolvePrimitiveType(); +} + +pub fn resultIsInteger(self: *const Self, result: SpvWord) bool { + return switch (self.getResultPrimitiveType(result) catch return false) { + .SInt, .UInt => true, + else => false, + }; +} + pub fn dumpResultsTable(self: *Self, allocator: std.mem.Allocator, writer: *std.Io.Writer) RuntimeError!void { const dump = pretty.dump(allocator, self.results, .{ .tab_size = 4, @@ -414,7 +429,7 @@ fn resolveInputLocationTarget(self: *const Self, location: SpvWord) RuntimeError return RuntimeError.NotFound; } -fn getInputLocationTargetValue(self: *const Self, target: InputLocationTarget) RuntimeError!*@import("Value.zig").Value { +fn getInputLocationTargetValue(self: *const Self, target: InputLocationTarget) RuntimeError!*Value { const value = switch ((try self.results[target.result].getVariant()).*) { .Variable => |*v| &v.value, .AccessChain => |*a| &a.value, diff --git a/src/Value.zig b/src/Value.zig index dbe1ccf..6a09cfd 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -169,6 +169,39 @@ pub const Value = union(Type) { }; } + pub fn resolvePrimitiveType(self: *const Self) RuntimeError!PrimitiveType { + return switch (self.*) { + .Bool => .Bool, + .Float, + .Vector2f32, + .Vector3f32, + .Vector4f32, + => .Float, + .Int => |int| if (int.is_signed) .SInt else .UInt, + .Vector2i32, + .Vector3i32, + .Vector4i32, + => .SInt, + .Vector2u32, + .Vector3u32, + .Vector4u32, + => .UInt, + .Vector => |lanes| { + if (lanes.len == 0) + return RuntimeError.InvalidValueType; + return lanes[0].resolvePrimitiveType(); + }, + else => RuntimeError.InvalidValueType, + }; + } + + pub fn isInteger(self: *const Self) bool { + return switch (self.resolvePrimitiveType() catch return false) { + .SInt, .UInt => true, + else => false, + }; + } + pub fn init(allocator: std.mem.Allocator, results: []const Result, target_type: SpvWord, is_externally_visible: bool) RuntimeError!Self { const resolved = results[target_type].resolveTypeWordOrNull() orelse target_type; return initUnresolved(allocator, results, resolved, is_externally_visible);