Compare commits

22 Commits

Author SHA1 Message Date
kbz_8 431a634290 fixing CI
Test / build (push) Successful in 24s
Build / build (push) Successful in 31s
2026-06-03 19:27:21 +02:00
kbz_8 df57df44cb fixing descriptor sets managment
Test / build (push) Successful in 58s
Build / build (push) Successful in 29s
2026-06-03 19:14:44 +02:00
kbz_8 9c355fe126 yes
Test / build (push) Successful in 58s
Build / build (push) Successful in 58s
2026-06-01 20:36:24 +02:00
kbz_8 0eefbe63e3 adding opcodes, reworking atomic engine
Build / build (push) Successful in 1m46s
Test / build (push) Successful in 9m39s
2026-06-01 19:32:46 +02:00
kbz_8 e993162d4e adding clamping ocpodes
Build / build (push) Successful in 58s
Test / build (push) Successful in 1m20s
2026-05-29 00:44:49 +02:00
kbz_8 1ffa20d07c adding OpImage
Build / build (push) Successful in 1m1s
Test / build (push) Successful in 1m3s
2026-05-24 22:03:29 +02:00
kbz_8 a8372ce736 adding image fetch
Build / build (push) Successful in 58s
Test / build (push) Successful in 1m7s
2026-05-22 10:42:56 +02:00
kbz_8 bc84a9f553 adding cleaning engines, adding image engine
Test / build (push) Successful in 1m6s
Build / build (push) Successful in 1m14s
2026-05-20 18:04:53 +02:00
kbz_8 45453c1b9e fixing opCompositeConstruct
Test / build (push) Successful in 2m31s
Build / build (push) Successful in 59s
2026-05-18 23:52:05 +02:00
kbz_8 b82d37e7b6 improving value read/write, improving cond engine
Build / build (push) Successful in 1m35s
Test / build (push) Successful in 9m14s
2026-05-18 18:18:39 +02:00
kbz_8 a765246ee9 improving image api
Test / build (push) Successful in 1m19s
Build / build (push) Successful in 1m22s
2026-05-17 01:57:36 +02:00
kbz_8 236c6496ff fixing external structs
Test / build (push) Successful in 1m8s
Build / build (push) Successful in 1m15s
2026-05-15 00:55:34 +02:00
kbz_8 dc80a6a348 updating nzsl
Build / build (push) Successful in 1m17s
Test / build (push) Successful in 7m41s
2026-05-14 14:24:18 +02:00
kbz_8 c0825d5315 fixing example
Build / build (push) Successful in 1m12s
Test / build (push) Successful in 1m10s
2026-05-12 16:45:19 +02:00
kbz_8 3139f3cfdd drafting push constants
Test / build (push) Successful in 1m11s
Build / build (push) Successful in 1m13s
2026-05-12 03:01:04 +02:00
kbz_8 ca33cfe3e9 adding some matrix operations
Build / build (push) Successful in 1m50s
Test / build (push) Successful in 9m32s
2026-05-11 21:36:04 +02:00
kbz_8 769009ad5e adding base matrix management
Build / build (push) Successful in 1m33s
Test / build (push) Successful in 10m31s
2026-05-11 01:48:13 +02:00
kbz_8 9d20363ae8 fixing vector times scalar
Build / build (push) Successful in 1m28s
Test / build (push) Successful in 8m38s
2026-05-10 17:56:10 +02:00
kbz_8 4e852b5c07 removing weird free
Build / build (push) Successful in 2m38s
Test / build (push) Successful in 8m52s
2026-05-08 18:52:26 +02:00
kbz_8 5faf8fd305 adding IAddCarry and ISubBorrow
Build / build (push) Successful in 1m24s
Test / build (push) Successful in 7m52s
2026-04-30 00:20:33 +02:00
kbz_8 ce17209004 fixing runtime array access chain
Build / build (push) Successful in 1m22s
Test / build (push) Successful in 8m28s
2026-04-29 23:52:07 +02:00
kbz_8 11a59d8d7f yes 2026-04-29 23:44:55 +02:00
19 changed files with 2755 additions and 1158 deletions
+18 -13
View File
@@ -6,10 +6,6 @@ on:
pull_request:
branches: [ "master" ]
permissions:
contents: read
deployments: write
jobs:
build:
runs-on: ubuntu-latest
@@ -17,21 +13,30 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: https://codeberg.org/mlugg/setup-zig@v2
- uses: actions/setup-node@v6
with:
node-version: 24
fetch-depth: 0
- uses: https://codeberg.org/mlugg/setup-zig@v2
- name: Building
run: zig build -Dno-example=true
- name: Building FFI
run: zig build ffi-c -Dno-example=true
- name: Generating docs
run: zig build docs -Dno-example=true
- name: Publish to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
- name: Deploying docs
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
uses: milanmk/actions-file-deployer@master
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy zig-out/docs --project-name=spirv-interpreter-docs
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
remote-protocol: sftp
remote-host: ${{ secrets.SFTP_HOST_DOCS }}
remote-user: ${{ secrets.SFTP_USER_DOCS }}
remote-password: ${{ secrets.SFTP_PASSWORD_DOCS }}
remote-port: 6969
local-path: "./zig-out/docs"
remote-path: "/www"
sync: full
+1 -1
View File
@@ -20,4 +20,4 @@ jobs:
- uses: https://codeberg.org/mlugg/setup-zig@v2
- name: Test
run: zig build test -Dno-example=true
run: zig build test -Dno-example=true --release=fast
+4
View File
@@ -84,3 +84,7 @@ int main(void)
return 0;
}
```
## Documentation
A full documentation on how to use this interpreter will come soon (when I'll stop being lazy).
+12 -3
View File
@@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void {
addSandbox(b, target, optimize, use_llvm, spv_mod, &install_spv_lib.step);
addExample(b, target, optimize, use_llvm, spv_mod, &install_spv_lib.step);
addZigTests(b, target, optimize, spv_mod, zmath);
addZigTests(b, target, optimize, use_llvm, spv_mod, zmath);
addCffi(b, target, optimize, use_llvm, spv_mod);
addDocs(b, spv_mod);
}
@@ -45,7 +45,7 @@ fn addExample(
install_spv_lib_step: *std.Build.Step,
) void {
const no_example = b.option(bool, "no-example", "Skip example build") orelse false;
if (!no_example and false) {
if (!no_example) {
const sdl3 = b.lazyDependency("sdl3", .{
.target = target,
.optimize = optimize,
@@ -146,15 +146,22 @@ fn addZigTests(
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
use_llvm: bool,
spv_mod: *std.Build.Module,
zmath: *std.Build.Dependency,
) void {
const no_test = b.option(bool, "no-test", "Skip unit test dependencies fetch") orelse false;
if (no_test) return;
const test_filter = b.option(
[]const u8,
"test-filter",
"Only run tests whose name contains this substring",
);
const nzsl = b.lazyDependency("NZSL", .{
.target = target,
.optimize = optimize,
.optimize = .ReleaseFast,
}) orelse return;
const tests = b.addTest(.{
@@ -172,6 +179,8 @@ fn addZigTests(
.path = b.path("test/test_runner.zig"),
.mode = .simple,
},
.filters = if (test_filter) |filter| &.{filter} else &.{},
.use_llvm = use_llvm,
});
const run_tests = b.addRunArtifact(tests);
+7 -7
View File
@@ -7,15 +7,15 @@
.hash = "zmath-0.11.0-dev-wjwivdMsAwD-xaLj76YHUq3t9JDH-X16xuMTmnDzqbu2",
},
.NZSL = .{ // For unit tests
.url = "git+https://git.kbz8.me/kbz_8/NZigSL#ab95fc3734da46079fda2a4cd0f14143d92bf633",
.hash = "NZSL-1.1.2-N0xSVCR7AACeI_Wa6JPggJzy9_MPCpWC-2OHkMowwX-7",
.url = "git+https://git.kbz8.me/kbz_8/NZigSL#3ab28e423cdab27797f779d352094a69800c0c6b",
.hash = "NZSL-1.1.5-N0xSVHV7AAB168H2nqMDYY0hjUXtXJ9_eQGNHasSr9r8",
.lazy = true,
},
.sdl3 = .{
.url = "git+https://codeberg.org/7Games/zig-sdl3?ref=v0.2.0#40c2e4b579aa556db37a502c936426aa1c8b5c95",
.hash = "sdl3-0.2.0-NmT1Q0mFJwBi9kZmArzh2rfJ_mFshydV0zPGULVlpACc",
.lazy = true,
},
//.sdl3 = .{
// .url = "git+https://codeberg.org/7Games/zig-sdl3?ref=v0.1.6#9c1842246c59f03f87ba59b160ca7e3d5e5ce972",
// .hash = "sdl3-0.1.6-NmT1Q5sQJgCzT6hLj7WOSrwxE0Qsef1wIkDopbOOFru0",
// .lazy = true,
//},
.pretty = .{
.url = "git+https://github.com/Kbz-8/pretty.git#f91d534d033277ca1ae7fcd598a070e8b3ddc532",
.hash = "pretty-0.10.6-Tm65r7FTAQA5BEL8tcIcF-Wp4XRC7J7BhuRI0KUnFj2X",
+50 -45
View File
@@ -4,14 +4,22 @@ const spv = @import("spv");
const shader_source = @embedFile("shader.spv");
const screen_width = 300;
const screen_height = 300;
const screen_width = 400;
const screen_height = 240;
pub fn main() !void {
{
//var gpa: std.heap.DebugAllocator(.{}) = .init;
//defer _ = gpa.deinit();
const allocator = std.heap.smp_allocator;
var threaded: std.Io.Threaded = .init(allocator, .{
.async_limit = @enumFromInt(screen_height),
});
defer threaded.deinit();
const io = threaded.io();
defer sdl3.shutdown();
const init_flags = sdl3.InitFlags{ .video = true, .events = true };
try sdl3.init(init_flags);
@@ -22,26 +30,28 @@ pub fn main() !void {
const surface = try window.getSurface();
const allocator = std.heap.smp_allocator;
var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)), .{});
defer module.deinit(allocator);
var runner_cache: std.ArrayList(Runner) = try .initCapacity(allocator, screen_height);
const fragment_count = screen_height * screen_width;
const batch_size = switch (threaded.async_limit) {
.nothing => 1,
.unlimited => std.Thread.getCpuCount() catch 1, // If we cannot get the CPU count, fallback on single runtime
else => |count| @intFromEnum(count),
};
const runners: []Runner = try allocator.alloc(Runner, batch_size);
defer {
for (runner_cache.items) |*runner| {
allocator.free(runner.heap);
for (runners) |*runner| {
runner.rt.deinit(allocator);
}
runner_cache.deinit(allocator);
allocator.free(runners);
}
for (0..screen_height) |_| {
const heap = try allocator.alloc(u8, module.needed_runtime_bytes);
errdefer allocator.free(heap);
var buffer_allocator: std.heap.FixedBufferAllocator = .init(heap);
var rt = try spv.Runtime.init(buffer_allocator.allocator(), &module);
(try runner_cache.addOne(allocator)).* = .{
for (runners) |*runner| {
var rt = try spv.Runtime.init(allocator, &module, undefined);
runner.* = .{
.allocator = allocator,
.surface = surface,
.rt = rt,
@@ -50,15 +60,13 @@ pub fn main() !void {
.time = try rt.getResultByName("time"),
.pos = try rt.getResultByName("pos"),
.res = try rt.getResultByName("res"),
.heap = heap,
.invocation_count = fragment_count,
.batch_size = batch_size,
};
}
const timer = std.Io.Timestamp.now(io, .real);
var thread_pool: std.Thread.Pool = undefined;
try thread_pool.init(.{ .allocator = allocator });
var timer = try std.time.Timer.start();
var wg: std.Io.Group = .init;
var quit = false;
while (!quit) {
try surface.clear(.{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0 });
@@ -76,21 +84,13 @@ pub fn main() !void {
const pixel_map: [*]u32 = @as([*]u32, @ptrCast(@alignCast((surface.getPixels() orelse return).ptr)));
const delta: f32 = @as(f32, @floatFromInt(timer.read())) / std.time.ns_per_s;
const duration = timer.untilNow(io, .real);
const delta: f32 = @as(f32, @floatFromInt(duration.toNanoseconds())) / std.time.ns_per_s;
var frame_timer = try std.time.Timer.start();
defer {
const ns = frame_timer.lap();
const ms = @as(f32, @floatFromInt(ns)) / std.time.ns_per_s;
std.log.info("Took {d:.3}s - {d:.3}fps to render", .{ ms, 1.0 / ms });
for (0..@min(batch_size, fragment_count)) |batch_id| {
wg.async(io, Runner.runWrapper, .{ &runners[batch_id], batch_id, pixel_map, delta });
}
var wait_group: std.Thread.WaitGroup = .{};
for (0..screen_height) |y| {
const runner = &runner_cache.items[y];
thread_pool.spawnWg(&wait_group, Runner.runWrapper, .{ runner, y, pixel_map, delta });
}
thread_pool.waitAndWork(&wait_group);
try wg.await(io);
}
try window.updateSurface();
@@ -110,24 +110,29 @@ const Runner = struct {
time: spv.SpvWord,
pos: spv.SpvWord,
res: spv.SpvWord,
heap: []u8,
invocation_count: usize,
batch_size: usize,
fn runWrapper(self: *Self, y: usize, pixel_map: [*]u32, timer: f32) void {
@call(.always_inline, Self.run, .{ self, y, pixel_map, timer }) catch |err| {
fn runWrapper(self: *Self, batch_id: usize, pixel_map: [*]u32, timer: f32) void {
@call(.always_inline, Self.run, .{ self, batch_id, pixel_map, timer }) catch |err| {
std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
std.debug.dumpErrorReturnTrace(trace);
}
std.process.abort();
};
}
fn run(self: *Self, y: usize, pixel_map: [*]u32, timer: f32) !void {
fn run(self: *Self, batch_id: usize, pixel_map: [*]u32, timer: f32) !void {
var rt = self.rt; // Copy to avoid pointer access of `self` at runtime. Okay as Runtime contains only pointers and trivially copyable fields
var output: [4]f32 = undefined;
for (0..screen_width) |x| {
var invocation_index: usize = batch_id;
while (invocation_index < self.invocation_count) : (invocation_index += self.batch_size) {
const y = @divTrunc(invocation_index, screen_width);
const x = @mod(invocation_index, screen_width);
try rt.writeInput(std.mem.asBytes(&timer), self.time);
try rt.writeInput(std.mem.asBytes(&[_]f32{ @floatFromInt(screen_width), @floatFromInt(screen_height) }), self.res);
try rt.writeInput(std.mem.asBytes(&[_]f32{ @floatFromInt(x), @floatFromInt(y) }), self.pos);
@@ -135,13 +140,13 @@ const Runner = struct {
try rt.readOutput(std.mem.asBytes(output[0..]), self.color);
const rgba = self.surface.mapRgba(
@intCast(@max(@min(@as(i32, @intFromFloat(output[0] * 255.0)), 255), 0)),
@intCast(@max(@min(@as(i32, @intFromFloat(output[1] * 255.0)), 255), 0)),
@intCast(@max(@min(@as(i32, @intFromFloat(output[2] * 255.0)), 255), 0)),
@intCast(@max(@min(@as(i32, @intFromFloat(output[3] * 255.0)), 255), 0)),
@intCast(std.math.clamp(@as(i32, @intFromFloat(output[0] * 255.0)), 0, 255)),
@intCast(std.math.clamp(@as(i32, @intFromFloat(output[1] * 255.0)), 0, 255)),
@intCast(std.math.clamp(@as(i32, @intFromFloat(output[2] * 255.0)), 0, 255)),
@intCast(std.math.clamp(@as(i32, @intFromFloat(output[3] * 255.0)), 0, 255)),
);
pixel_map[(y * self.surface.getWidth()) + x] = rgba.value;
pixel_map[invocation_index] = rgba.value;
}
}
};
+121 -109
View File
@@ -38,112 +38,7 @@ extern "C"
#endif
#ifndef spirv_H
typedef enum SpvBuiltIn_ SpvBuiltIn;
typedef enum SpvDecoration_ SpvDecoration;
#endif /* spirv_H */
typedef int SpvBool;
typedef unsigned char SpvByte;
typedef unsigned long SpvWord;
typedef unsigned long SpvSize;
typedef enum
{
SPV_RESULT_SUCCESS = 0,
SPV_RESULT_DIVISION_BY_ZERO = -1,
SPV_RESULT_INVALID_ENTRY_POINT = -2,
SPV_RESULT_INVALID_SPIRV = -3,
SPV_RESULT_INVALID_VALUE_TYPE = -4,
SPV_RESULT_KILLED = -5,
SPV_RESULT_NOT_FOUND = -6,
SPV_RESULT_OUT_OF_MEMORY = -7,
SPV_RESULT_OUT_OF_BOUNDS = -8,
SPV_RESULT_TODO = -9,
SPV_RESULT_UNREACHABLE = -10,
SPV_RESULT_UNSUPPORTED_SPIRV = -11,
SPV_RESULT_UNSUPPORTED_EXTENSION = -12,
SPV_RESULT_UNSUPPORTED_ENDIANNESS = -13,
SPV_RESULT_INVALID_MAGIC = -14,
SPV_RESULT_UNKNOWN = -15,
} SpvResult;
typedef struct
{
SpvBool use_simd_vectors_specializations;
} SpvModuleOptions;
typedef struct
{
SpvWord id;
SpvSize offset;
SpvSize size;
} SpvRuntimeSpecializationEntry;
typedef enum
{
SPV_LOCATION_INPUT = 0,
SPV_LOCATION_OUTPUT = 1,
} SpvLocationType;
typedef struct
{
float x;
float y;
float z;
float w;
} SpvVec4f;
typedef struct
{
unsigned int x;
unsigned int y;
unsigned int z;
unsigned int w;
} SpvVec4u;
typedef SpvResult (*SpvReadImageFloat4_PFN)(void* driver_image, int x, int y, int z, SpvVec4f* dst);
typedef SpvResult (*SpvReadImageInt4_PFN)(void* driver_image, int x, int y, int z, SpvVec4u* dst);
typedef SpvResult (*SpvWriteImageFloat4_PFN)(void* driver_image, int x, int y, int z, SpvVec4f src);
typedef SpvResult (*SpvWriteImageInt4_PFN)(void* driver_image, int x, int y, int z, SpvVec4u src);
typedef struct
{
SpvReadImageFloat4_PFN SpvReadImageFloat4;
SpvReadImageInt4_PFN SpvReadImageInt4;
SpvWriteImageFloat4_PFN SpvWriteImageFloat4;
SpvWriteImageInt4_PFN SpvWriteImageInt4;
} SpvImageAPI;
typedef void* SpvModule;
typedef void* SpvRuntime;
SPV_API SpvResult SpvInitModule(SpvModule* module, const SpvWord* source, SpvSize source_len, SpvModuleOptions options);
SPV_API void SpvDeinitModule(SpvModule module);
SPV_API SpvResult SpvInitRuntime(SpvRuntime* runtime, SpvModule module, SpvImageAPI image_api);
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 SpvGetResultByName(SpvRuntime runtime, const char* name, SpvWord* result);
SPV_API SpvResult SpvGetResultLocation(SpvRuntime runtime, SpvWord location, 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 SpvBool SpvHasResultDecoration(SpvRuntime runtime, SpvWord result, SpvDecoration decoration);
SPV_API SpvResult SpvCallEntryPoint(SpvRuntime runtime, SpvWord entry_point_index);
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 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);
#ifndef spirv_H
enum SpvBuiltIn_
typedef enum SpvBuiltIn_
{
SpvBuiltInPosition = 0,
SpvBuiltInPointSize = 1,
@@ -295,8 +190,8 @@ SPV_API SpvResult SpvWriteDescriptorSet(SpvRuntime runtime, const SpvByte* input
SpvBuiltInHitLSSRadiiNV = 5421,
SpvBuiltInClusterIDNV = 5436,
SpvBuiltInCullMaskKHR = 6021,
SpvBuiltInMax = 0x7fffffff,
};
SpvBuiltInMax = 0x7fffffff
} SpvBuiltIn;
typedef enum SpvDecoration_ {
SpvDecorationRelaxedPrecision = 0,
@@ -497,10 +392,127 @@ SPV_API SpvResult SpvWriteDescriptorSet(SpvRuntime runtime, const SpvByte* input
SpvDecorationConditionalINTEL = 6247,
SpvDecorationCacheControlLoadINTEL = 6442,
SpvDecorationCacheControlStoreINTEL = 6443,
SpvDecorationMax = 0x7fffffff,
SpvDecorationMax = 0x7fffffff
} SpvDecoration;
typedef enum SpvDim_ {
_1D = 0,
_2D = 1,
_3D = 2,
Cube = 3,
Rect = 4,
Buffer = 5,
SubpassData = 6,
TileImageDataEXT = 4173,
Max = 0x7fffffff
} SpvDim;
#endif /* spirv_H */
typedef int SpvBool;
typedef unsigned char SpvByte;
typedef unsigned long SpvWord;
typedef unsigned long SpvSize;
typedef enum
{
SPV_RESULT_SUCCESS = 0,
SPV_RESULT_BARRIER = 1,
SPV_RESULT_KILLED = 2,
SPV_RESULT_DIVISION_BY_ZERO = -1,
SPV_RESULT_INVALID_ENTRY_POINT = -2,
SPV_RESULT_INVALID_SPIRV = -3,
SPV_RESULT_INVALID_VALUE_TYPE = -4,
SPV_RESULT_NOT_FOUND = -5,
SPV_RESULT_OUT_OF_MEMORY = -6,
SPV_RESULT_OUT_OF_BOUNDS = -7,
SPV_RESULT_TODO = -8,
SPV_RESULT_UNREACHABLE = -9,
SPV_RESULT_UNSUPPORTED_SPIRV = -10,
SPV_RESULT_UNSUPPORTED_EXTENSION = -11,
SPV_RESULT_UNSUPPORTED_ENDIANNESS = -12,
SPV_RESULT_INVALID_MAGIC = -13,
SPV_RESULT_UNKNOWN = -14
} SpvResult;
typedef struct
{
SpvBool use_simd_vectors_specializations;
} SpvModuleOptions;
typedef struct
{
SpvWord id;
SpvSize offset;
SpvSize size;
} SpvRuntimeSpecializationEntry;
typedef enum
{
SPV_LOCATION_INPUT = 0,
SPV_LOCATION_OUTPUT = 1
} SpvLocationType;
typedef struct
{
float x;
float y;
float z;
float w;
} SpvVec4f;
typedef struct
{
unsigned int x;
unsigned int y;
unsigned int z;
unsigned int w;
} SpvVec4u;
typedef SpvResult (*SpvReadImageFloat4_PFN)(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4f* dst);
typedef SpvResult (*SpvReadImageInt4_PFN)(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4u* dst);
typedef SpvResult (*SpvWriteImageFloat4_PFN)(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4f src);
typedef SpvResult (*SpvWriteImageInt4_PFN)(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4u src);
typedef SpvResult (*SpvSampleImageFloat4_PFN)(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, SpvVec4f* dst);
typedef SpvResult (*SpvQueryImageSize_PFN)(void* driver_image, SpvDim dim, SpvBool arrayed, SpvVec4u* dst);
typedef struct
{
SpvReadImageFloat4_PFN SpvReadImageFloat4;
SpvReadImageInt4_PFN SpvReadImageInt4;
SpvWriteImageFloat4_PFN SpvWriteImageFloat4;
SpvWriteImageInt4_PFN SpvWriteImageInt4;
SpvSampleImageFloat4_PFN SpvSampleImageFloat4;
SpvQueryImageSize_PFN SpvQueryImageSize;
} SpvImageAPI;
typedef void* SpvModule;
typedef void* SpvRuntime;
SPV_API SpvResult SpvInitModule(SpvModule* module, const SpvWord* source, SpvSize source_len, SpvModuleOptions options);
SPV_API void SpvDeinitModule(SpvModule module);
SPV_API SpvResult SpvInitRuntime(SpvRuntime* runtime, SpvModule module, SpvImageAPI image_api);
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 SpvGetResultByName(SpvRuntime runtime, const char* name, SpvWord* result);
SPV_API SpvResult SpvGetResultLocation(SpvRuntime runtime, SpvWord location, 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 SpvBool SpvHasResultDecoration(SpvRuntime runtime, SpvWord result, SpvDecoration decoration);
SPV_API SpvResult SpvCallEntryPoint(SpvRuntime runtime, SpvWord entry_point_index);
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 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);
#ifdef __cplusplus
}
#endif
+12 -11
View File
@@ -6,21 +6,22 @@ pub const SpvCSize = c_ulong;
pub const Result = enum(c_int) {
Success = 0,
Barrier = 1,
Killed = 2,
DivisionByZero = -1,
InvalidEntryPoint = -2,
InvalidSpirV = -3,
InvalidValueType = -4,
Killed = -5,
NotFound = -6,
OutOfMemory = -7,
OutOfBounds = -8,
ToDo = -9,
Unreachable = -10,
UnsupportedSpirV = -11,
UnsupportedExtension = -12,
UnsupportedEndianness = -13,
InvalidMagic = -14,
Unknown = -15,
NotFound = -5,
OutOfMemory = -6,
OutOfBounds = -7,
ToDo = -8,
Unreachable = -9,
UnsupportedSpirV = -10,
UnsupportedExtension = -11,
UnsupportedEndianness = -12,
InvalidMagic = -13,
Unknown = -14,
};
comptime {
+62 -21
View File
@@ -27,67 +27,74 @@ const Vec4u = extern struct {
w: c_uint,
};
const readImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, x: c_int, y: c_int, z: c_int, dst: *Vec4f) callconv(.c) ffi.Result;
const readImageInt4_PFN = *const fn (driver_image: ?*anyopaque, x: c_int, y: c_int, z: c_int, dst: *Vec4u) callconv(.c) ffi.Result;
const writeImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, x: c_int, y: c_int, z: c_int, src: Vec4f) callconv(.c) ffi.Result;
const writeImageInt4_PFN = *const fn (driver_image: ?*anyopaque, x: c_int, y: c_int, z: c_int, src: Vec4u) callconv(.c) ffi.Result;
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.OutOfMemory => ffi.Result.OutOfMemory,
spv.Runtime.RuntimeError.OutOfBounds => ffi.Result.OutOfBounds,
spv.Runtime.RuntimeError.OutOfMemory => ffi.Result.OutOfMemory,
spv.Runtime.RuntimeError.ToDo => ffi.Result.ToDo,
spv.Runtime.RuntimeError.Unreachable => ffi.Result.Unreachable,
spv.Runtime.RuntimeError.UnsupportedSpirV => ffi.Result.UnsupportedSpirV,
spv.Runtime.RuntimeError.UnsupportedExtension => ffi.Result.UnsupportedExtension,
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.OutOfMemory => spv.Runtime.RuntimeError.OutOfMemory,
ffi.Result.OutOfBounds => spv.Runtime.RuntimeError.OutOfBounds,
ffi.Result.OutOfMemory => spv.Runtime.RuntimeError.OutOfMemory,
ffi.Result.ToDo => spv.Runtime.RuntimeError.ToDo,
ffi.Result.Unreachable => spv.Runtime.RuntimeError.Unreachable,
ffi.Result.UnsupportedSpirV => spv.Runtime.RuntimeError.UnsupportedSpirV,
ffi.Result.UnsupportedExtension => spv.Runtime.RuntimeError.UnsupportedExtension,
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; // Hacky
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, x: i32, y: i32, z: i32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(f32) {
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, @intCast(x), @intCast(y), @intCast(z), &dst);
const result = image_api.readImageFloat4(driver_image, dim, @intCast(x), @intCast(y), @intCast(z), &dst);
try fromCResult(result);
@@ -99,11 +106,11 @@ const ImageAPIBridge = struct {
};
}
fn readImageInt4(driver_image: *anyopaque, x: i32, y: i32, z: i32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
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, @intCast(x), @intCast(y), @intCast(z), &dst);
const result = image_api.readImageInt4(driver_image, dim, @intCast(x), @intCast(y), @intCast(z), &dst);
try fromCResult(result);
@@ -115,21 +122,53 @@ const ImageAPIBridge = struct {
};
}
fn writeImageFloat4(driver_image: *anyopaque, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(f32)) spv.Runtime.RuntimeError!void {
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, @intCast(x), @intCast(y), @intCast(z), .{ .x = pixel.x, .y = pixel.y, .z = pixel.z, .w = pixel.w });
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, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(u32)) spv.Runtime.RuntimeError!void {
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, @intCast(x), @intCast(y), @intCast(z), .{ .x = pixel.x, .y = pixel.y, .z = pixel.z, .w = pixel.w });
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 {
@@ -151,6 +190,8 @@ export fn SpvInitRuntime(rt: **RuntimeWrapper, module: *spv.Module, image_api: I
.readImageInt4 = ImageAPIBridge.readImageInt4,
.writeImageFloat4 = ImageAPIBridge.writeImageFloat4,
.writeImageInt4 = ImageAPIBridge.writeImageInt4,
.sampleImageFloat4 = ImageAPIBridge.sampleImageFloat4,
.queryImageSize = ImageAPIBridge.queryImageSize,
},
) catch |err| {
allocator.destroy(rt.*);
+60
View File
@@ -84,6 +84,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Exp)] = MathEngine(.Float, .Exp).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Exp2)] = MathEngine(.Float, .Exp2).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FAbs)] = MathEngine(.Float, .FAbs).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FClamp)] = MathEngine(.Float, .FClamp).opTripleOperators;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FMax)] = MathEngine(.Float, .FMax).opDoubleOperators;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FSign)] = MathEngine(.Float, .FSign).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Floor)] = MathEngine(.Float, .Floor).opSingleOperator;
@@ -93,11 +94,13 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Normalize)] = opNormalize;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Round)] = MathEngine(.Float, .Round).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.SAbs)] = MathEngine(.SInt, .SAbs).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.SClamp)] = MathEngine(.SInt, .SClamp).opTripleOperators;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.SSign)] = MathEngine(.SInt, .SSign).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Sin)] = MathEngine(.Float, .Sin).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Sqrt)] = MathEngine(.Float, .Sqrt).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Tan)] = MathEngine(.Float, .Tan).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Trunc)] = MathEngine(.Float, .Trunc).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.UClamp)] = MathEngine(.UInt, .UClamp).opTripleOperators;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindILsb)] = IntBitEngine(.FindILsb).op;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindSMsb)] = IntBitEngine(.FindSMsb).op;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindUMsb)] = IntBitEngine(.FindUMsb).op;
@@ -242,6 +245,63 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp) type {
else => return RuntimeError.InvalidSpirV,
}
}
fn opTripleOperators(_: std.mem.Allocator, target_type_id: SpvWord, id: SpvWord, _: SpvWord, rt: *Runtime) RuntimeError!void {
const target_type = (try rt.results[target_type_id].getVariant()).Type;
const dst = try rt.results[id].getValue();
const x = try rt.results[try rt.it.next()].getValue();
const min_val = try rt.results[try rt.it.next()].getValue();
const max_val = try rt.results[try rt.it.next()].getValue();
const lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
const operator = struct {
fn operation(comptime TT: type, v: TT, lo: TT, hi: TT) RuntimeError!TT {
return switch (Op) {
.FClamp, .SClamp, .UClamp => @min(@max(v, lo), hi),
else => RuntimeError.InvalidSpirV,
};
}
fn applyScalar(bit_count: SpvWord, d: *Value, v: *const Value, lo: *const Value, hi: *const Value) RuntimeError!void {
switch (bit_count) {
inline 8, 16, 32, 64 => |bits| {
if (bits == 8 and T == .Float) return RuntimeError.InvalidSpirV;
const ScalarT = Value.getPrimitiveFieldType(T, bits);
const d_field = try Value.getPrimitiveField(T, bits, d);
const v_field = try Value.getPrimitiveField(T, bits, @constCast(v));
const lo_field = try Value.getPrimitiveField(T, bits, @constCast(lo));
const hi_field = try Value.getPrimitiveField(T, bits, @constCast(hi));
d_field.* = try operation(ScalarT, v_field.*, lo_field.*, hi_field.*);
},
else => return RuntimeError.InvalidSpirV,
}
}
};
switch (dst.*) {
.Int, .Float => try operator.applyScalar(lane_bits, dst, x, min_val, max_val),
.Vector => |dst_vec| for (dst_vec, x.Vector, min_val.Vector, max_val.Vector) |*d_lane, x_lane, min_lane, max_lane| {
try operator.applyScalar(lane_bits, d_lane, &x_lane, &min_lane, &max_lane);
},
.Vector4f32 => |*d| d.* = try operator.operation(@Vector(4, f32), x.Vector4f32, min_val.Vector4f32, max_val.Vector4f32),
.Vector3f32 => |*d| d.* = try operator.operation(@Vector(3, f32), x.Vector3f32, min_val.Vector3f32, max_val.Vector3f32),
.Vector2f32 => |*d| d.* = try operator.operation(@Vector(2, f32), x.Vector2f32, min_val.Vector2f32, max_val.Vector2f32),
.Vector4i32 => |*d| d.* = try operator.operation(@Vector(4, i32), x.Vector4i32, min_val.Vector4i32, max_val.Vector4i32),
.Vector3i32 => |*d| d.* = try operator.operation(@Vector(3, i32), x.Vector3i32, min_val.Vector3i32, max_val.Vector3i32),
.Vector2i32 => |*d| d.* = try operator.operation(@Vector(2, i32), x.Vector2i32, min_val.Vector2i32, max_val.Vector2i32),
.Vector4u32 => |*d| d.* = try operator.operation(@Vector(4, u32), x.Vector4u32, min_val.Vector4u32, max_val.Vector4u32),
.Vector3u32 => |*d| d.* = try operator.operation(@Vector(3, u32), x.Vector3u32, min_val.Vector3u32, max_val.Vector3u32),
.Vector2u32 => |*d| d.* = try operator.operation(@Vector(2, u32), x.Vector2u32, min_val.Vector2u32, max_val.Vector2u32),
else => return RuntimeError.InvalidSpirV,
}
}
};
}
+57 -27
View File
@@ -9,8 +9,6 @@ const SpvByte = spv.SpvByte;
const SpvWord = spv.SpvWord;
const SpvBool = spv.SpvBool;
const SpvBinding = spv.SpvBinding;
const Result = @import("Result.zig");
const Runtime = @import("Runtime.zig");
const Value = @import("Value.zig").Value;
@@ -19,6 +17,7 @@ const WordIterator = @import("WordIterator.zig");
const Self = @This();
pub const ModuleOptions = struct {
/// Also affects matrices
use_simd_vectors_specializations: bool = true,
};
@@ -29,6 +28,12 @@ const SpvEntryPoint = struct {
globals: []SpvWord,
};
const BindingEntry = struct {
set: SpvWord,
binding: SpvWord,
result: SpvWord,
};
pub const ModuleError = error{
InvalidSpirV,
InvalidMagic,
@@ -72,9 +77,8 @@ geometry_output: SpvWord,
input_locations: [lib.SPIRV_MAX_INPUT_LOCATIONS]SpvWord,
output_locations: [lib.SPIRV_MAX_OUTPUT_LOCATIONS]SpvWord,
bindings: [lib.SPIRV_MAX_SET][lib.SPIRV_MAX_SET_BINDINGS]SpvWord,
bindings: std.ArrayList(BindingEntry),
builtins: std.EnumMap(spv.SpvBuiltIn, SpvWord),
push_constants: []Value,
pub fn init(allocator: std.mem.Allocator, source: []const SpvWord, options: ModuleOptions) ModuleError!Self {
var self: Self = std.mem.zeroInit(Self, .{
@@ -82,12 +86,14 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord, options: Modu
.code = allocator.dupe(SpvWord, source) catch return ModuleError.OutOfMemory,
.extensions = std.ArrayList([]const u8).empty,
.entry_points = std.ArrayList(SpvEntryPoint).empty,
.bindings = std.ArrayList(BindingEntry).empty,
.capabilities = std.EnumSet(spv.SpvCapability).initEmpty(),
.local_size_x = 1,
.local_size_y = 1,
.local_size_z = 1,
});
errdefer allocator.free(self.code);
errdefer self.bindings.deinit(allocator);
op.initRuntimeDispatcher();
@@ -125,7 +131,7 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord, options: Modu
_ = self.it.skip(); // Skip schema
try self.pass(allocator); // Setup pass
try self.applyDecorations();
try self.applyDecorations(allocator);
return self;
}
@@ -180,7 +186,7 @@ fn resolveConstantWord(self: *const Self, id: SpvWord) ?SpvWord {
}
fn findAccessChainToMember(self: *const Self, base_id: SpvWord, member_index: SpvWord) ?SpvWord {
for (self.results, 0..) |result, id| {
for (self.results, 0..) |*result, id| {
const variant = result.variant orelse continue;
switch (variant) {
@@ -197,12 +203,7 @@ fn findAccessChainToMember(self: *const Self, base_id: SpvWord, member_index: Sp
return null;
}
fn applyInterfaceDecoration(
self: *Self,
storage_class: spv.SpvStorageClass,
decoration: Result.Decoration,
id: SpvWord,
) ModuleError!void {
fn applyInterfaceDecoration(self: *Self, storage_class: spv.SpvStorageClass, decoration: Result.Decoration, id: SpvWord) ModuleError!void {
switch (storage_class) {
.Input => switch (decoration.rtype) {
.BuiltIn => self.builtins.put(
@@ -224,12 +225,7 @@ fn applyInterfaceDecoration(
}
}
fn applyStructMemberInterfaceDecorations(
self: *Self,
storage_class: spv.SpvStorageClass,
type_word: SpvWord,
id: SpvWord,
) ModuleError!void {
fn applyStructMemberInterfaceDecorations(self: *Self, storage_class: spv.SpvStorageClass, type_word: SpvWord, id: SpvWord) ModuleError!void {
switch (storage_class) {
.Input, .Output => {},
else => return,
@@ -266,21 +262,24 @@ fn applyStructMemberInterfaceDecorations(
}
}
fn applyDecorations(self: *Self) ModuleError!void {
fn applyDecorations(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
for (self.results, 0..) |result, id| {
if (result.variant == null)
continue;
var set: ?usize = null;
var binding: ?usize = null;
var set: ?SpvWord = null;
var binding: ?SpvWord = null;
for (result.decorations.items) |decoration| {
switch (result.variant.?) {
if (result.variant) |*variant| switch (variant.*) {
.Variable => |v| {
try self.applyInterfaceDecoration(v.storage_class, decoration, @intCast(id));
switch (v.storage_class) {
.StorageBuffer, .Uniform, .UniformConstant => {
.StorageBuffer,
.Uniform,
.UniformConstant,
=> {
switch (decoration.rtype) {
.Binding => binding = decoration.literal_1,
.DescriptorSet => set = decoration.literal_1,
@@ -301,20 +300,50 @@ fn applyDecorations(self: *Self) ModuleError!void {
}
},
else => {},
}
};
}
switch (result.variant.?) {
.Variable => |v| try self.applyStructMemberInterfaceDecorations(v.storage_class, v.type_word, @intCast(id)),
if (result.variant) |*variant| switch (variant.*) {
.Variable => |*v| {
try self.applyStructMemberInterfaceDecorations(v.storage_class, v.type_word, @intCast(id));
switch (v.storage_class) {
.StorageBuffer,
.Uniform,
.PushConstant,
=> if (v.value == .Structure) {
if (self.results[v.type_word].variant) |type_variant| switch (type_variant) {
.Type => |type_data| switch (type_data) {
.Structure => |s| @memcpy(@constCast(v.value.Structure.offsets), s.members_offsets),
else => {},
},
else => {},
};
},
else => {},
}
},
else => {},
};
if (set != null and binding != null) {
self.bindings[set.?][binding.?] = @intCast(id);
self.bindings.append(allocator, .{
.set = set.?,
.binding = binding.?,
.result = @intCast(id),
}) catch return ModuleError.OutOfMemory;
}
}
}
pub fn getBindingResult(self: *const Self, set: SpvWord, binding: SpvWord) ?SpvWord {
for (self.bindings.items) |entry| {
if (entry.set == set and entry.binding == binding) {
return entry.result;
}
}
return null;
}
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(self.code);
for (self.entry_points.items) |entry| {
@@ -327,6 +356,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(ext);
}
self.extensions.deinit(allocator);
self.bindings.deinit(allocator);
for (self.results) |*result| {
result.deinit(allocator);
+6 -2
View File
@@ -139,7 +139,9 @@ pub const TypeData = union(Type) {
access: ?spv.SpvAccessQualifier,
},
Sampler: struct {},
SampledImage: struct {},
SampledImage: struct {
image_type: SpvWord,
},
Pointer: struct {
storage_class: spv.SpvStorageClass,
target: SpvWord,
@@ -390,6 +392,7 @@ pub fn resolveLaneBitWidth(target_type: TypeData, rt: *const Runtime) RuntimeErr
.Float => |f| f.bit_length,
.Int => |i| i.bit_length,
.Vector => |v| continue :sw (try rt.results[v.components_type_word].getVariant()).Type,
.Matrix => |m| continue :sw (try rt.results[m.column_type_word].getVariant()).Type,
.Vector4f32,
.Vector3f32,
.Vector2f32,
@@ -408,6 +411,7 @@ pub fn resolveLaneCount(target_type: TypeData) RuntimeError!SpvWord {
return switch (target_type) {
.Bool, .Float, .Int => 1,
.Vector => |v| v.member_count,
.Matrix => |m| m.member_count,
.Vector4f32, .Vector4i32, .Vector4u32 => 4,
.Vector3f32, .Vector3i32, .Vector3u32 => 3,
.Vector2f32, .Vector2i32, .Vector2u32 => 2,
@@ -419,6 +423,7 @@ pub fn resolveSign(target_type: TypeData, rt: *const Runtime) RuntimeError!enum
return sw: switch (target_type) {
.Int => |i| if (i.is_signed) .signed else .unsigned,
.Vector => |v| continue :sw (try rt.results[v.components_type_word].getVariant()).Type,
.Matrix => |m| continue :sw (try rt.results[m.column_type_word].getVariant()).Type,
.Vector4i32 => .signed,
.Vector3i32 => .signed,
.Vector2i32 => .signed,
@@ -457,7 +462,6 @@ pub fn getMemberCounts(self: *const Self) usize {
.Vector2f32, .Vector2i32, .Vector2u32 => return 2,
.Matrix => |m| return m.member_count,
.Array => |a| return a.member_count,
.SampledImage => return 2,
.Structure => |s| return s.members_type_word.len,
.Function => |f| return f.params.len,
else => {},
+57 -17
View File
@@ -18,6 +18,7 @@ const WordIterator = @import("WordIterator.zig");
const Self = @This();
pub const RuntimeError = error{
Barrier,
DivisionByZero,
InvalidEntryPoint,
InvalidSpirV,
@@ -33,6 +34,11 @@ pub const RuntimeError = error{
Unknown,
};
pub const EntryPointStatus = enum {
completed,
barrier,
};
pub const SpecializationEntry = struct {
id: SpvWord,
offset: usize,
@@ -55,10 +61,12 @@ pub fn Vec4(comptime T: type) type {
}
pub const ImageAPI = struct {
readImageFloat4: *const fn (driver_image: *anyopaque, x: i32, y: i32, z: i32) RuntimeError!Vec4(f32),
readImageInt4: *const fn (driver_image: *anyopaque, x: i32, y: i32, z: i32) RuntimeError!Vec4(u32),
writeImageFloat4: *const fn (driver_image: *anyopaque, x: i32, y: i32, z: i32, pixel: Vec4(f32)) RuntimeError!void,
writeImageInt4: *const fn (driver_image: *anyopaque, x: i32, y: i32, z: i32, pixel: Vec4(u32)) RuntimeError!void,
readImageFloat4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!Vec4(f32),
readImageInt4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!Vec4(u32),
writeImageFloat4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: Vec4(f32)) RuntimeError!void,
writeImageInt4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: Vec4(u32)) RuntimeError!void,
sampleImageFloat4: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32) RuntimeError!Vec4(f32),
queryImageSize: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, arrayed: bool) RuntimeError!Vec4(u32),
};
mod: *Module,
@@ -117,6 +125,17 @@ pub fn addSpecializationInfo(self: *Self, allocator: std.mem.Allocator, entry: S
self.specialization_constants.put(allocator, entry.id, slice) catch return RuntimeError.OutOfMemory;
}
pub fn copySpecializationConstantsFrom(self: *Self, allocator: std.mem.Allocator, other: *const Self) RuntimeError!void {
var it = other.specialization_constants.iterator();
while (it.next()) |entry| {
const slice = allocator.dupe(u8, entry.value_ptr.*) catch return RuntimeError.OutOfMemory;
self.specialization_constants.put(allocator, entry.key_ptr.*, slice) catch {
allocator.free(slice);
return RuntimeError.OutOfMemory;
};
}
}
pub fn getEntryPointByName(self: *const Self, name: []const u8) RuntimeError!SpvWord {
for (self.mod.entry_points.items, 0..) |entry_point, i| {
if (blk: {
@@ -174,8 +193,11 @@ pub fn dumpResultsTable(self: *Self, allocator: std.mem.Allocator, writer: *std.
/// Calls an entry point, `entry_point_index` being the index of the entry point ordered by declaration in the bytecode
pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_index: SpvWord) RuntimeError!void {
self.reset();
_ = try self.beginEntryPoint(allocator, entry_point_index);
}
pub fn beginEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_index: SpvWord) RuntimeError!EntryPointStatus {
self.reset();
if (entry_point_index > self.mod.entry_points.items.len)
return RuntimeError.InvalidEntryPoint;
@@ -210,13 +232,23 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind
}
// Execution pass
try self.pass(allocator, null);
return self.continueEntryPoint(allocator);
}
pub fn continueEntryPoint(self: *Self, allocator: std.mem.Allocator) RuntimeError!EntryPointStatus {
self.pass(allocator, null) catch |err| switch (err) {
RuntimeError.Barrier => return .barrier,
else => return err,
};
return .completed;
}
fn pass(self: *Self, allocator: std.mem.Allocator, op_set: ?std.EnumSet(spv.SpvOp)) RuntimeError!void {
self.it.did_jump = false; // To reset function jump
while (self.it.nextOrNull()) |opcode_data| {
const word_count = ((opcode_data & (~spv.SpvOpCodeMask)) >> spv.SpvWordCountShift) - 1;
const word_count_with_header = (opcode_data & (~spv.SpvOpCodeMask)) >> spv.SpvWordCountShift;
if (word_count_with_header == 0) return RuntimeError.InvalidSpirV;
const word_count = word_count_with_header - 1;
const opcode = (opcode_data & spv.SpvOpCodeMask);
if (op_set) |set| {
@@ -240,24 +272,32 @@ fn pass(self: *Self, allocator: std.mem.Allocator, op_set: ?std.EnumSet(spv.SpvO
}
}
pub fn populatePushConstants(self: *Self, blob: []const u8) RuntimeError!void {
for (self.results) |*result| {
if (result.variant == null or std.meta.activeTag(result.variant.?) != .Variable)
continue;
const variable = &result.variant.?.Variable;
if (variable.storage_class != .PushConstant)
continue;
_ = try variable.value.write(blob);
}
}
pub fn writeDescriptorSet(self: *const Self, input: []const u8, set: SpvWord, binding: SpvWord, descriptor_index: SpvWord) RuntimeError!void {
if (set < lib.SPIRV_MAX_SET and binding < lib.SPIRV_MAX_SET_BINDINGS) {
const value = &self.results[self.mod.bindings[set][binding]].variant.?.Variable.value;
const result = self.mod.getBindingResult(set, binding) orelse return RuntimeError.NotFound;
const value = &(self.results[result].variant orelse return).Variable.value;
switch (value.*) {
.Array => |arr| {
if (descriptor_index >= arr.values.len)
return RuntimeError.NotFound;
_ = try arr.values[descriptor_index].writeConst(input);
_ = try arr.values[descriptor_index].write(input);
},
else => {
if (descriptor_index != 0)
return RuntimeError.NotFound;
_ = try value.writeConst(input);
_ = try value.write(input);
},
}
} else {
return RuntimeError.NotFound;
}
}
fn readResultValue(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void {
@@ -280,15 +320,15 @@ fn readResultValue(self: *const Self, output: []u8, result: SpvWord) RuntimeErro
fn writeResultValue(self: *const Self, input: []const u8, result: SpvWord) RuntimeError!void {
if (self.results[result].variant) |*variant| {
switch (variant.*) {
.Variable => |*v| _ = try v.value.writeConst(input),
.Variable => |*v| _ = try v.value.write(input),
.AccessChain => |*a| switch (a.value) {
.Pointer => |ptr| switch (ptr.ptr) {
.common => |value_ptr| _ = try value_ptr.writeConst(input),
.common => |value_ptr| _ = try value_ptr.write(input),
.f32_ptr => |value_ptr| std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(f32)]),
.i32_ptr => |value_ptr| std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(i32)]),
.u32_ptr => |value_ptr| std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(u32)]),
},
else => _ = try a.value.writeConst(input),
else => _ = try a.value.write(input),
},
else => return RuntimeError.InvalidSpirV,
}
+277 -211
View File
@@ -1,5 +1,6 @@
const std = @import("std");
const lib = @import("lib.zig");
const spv = @import("spv.zig");
const Result = @import("Result.zig");
const Runtime = @import("Runtime.zig");
@@ -83,14 +84,14 @@ pub const Value = union(Type) {
errdefer allocator.destroy(value);
value.* = try Value.init(allocator, results, self.type_word, false);
_ = try value.writeConst(self.data[self.getOffsetOfIndex(index)..]);
_ = try value.write(self.data[self.getOffsetOfIndex(index)..]);
return value;
}
pub inline fn createLocalValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!Value {
var value = try Value.init(allocator, results, self.type_word, false);
_ = try value.writeConst(self.data[self.getOffsetOfIndex(index)..]);
_ = try value.write(self.data[self.getOffsetOfIndex(index)..]);
return value;
}
@@ -103,6 +104,7 @@ pub const Value = union(Type) {
}
},
Structure: struct {
external_data: ?[]u8,
offsets: []const ?SpvWord,
values: []Self,
},
@@ -111,8 +113,14 @@ pub const Value = union(Type) {
type_word: SpvWord,
driver_image: *anyopaque,
},
Sampler: struct {},
SampledImage: struct {},
Sampler: struct {
driver_sampler: *anyopaque,
},
SampledImage: struct {
type_word: SpvWord,
driver_image: *anyopaque,
driver_sampler: *anyopaque,
},
Pointer: struct {
ptr: union(enum) {
common: *Self,
@@ -120,24 +128,43 @@ pub const Value = union(Type) {
i32_ptr: *i32, //< For vector specializations
u32_ptr: *u32,
},
is_owner_of_uniform_slice: bool = false,
image_texel: ?struct {
driver_image: *anyopaque,
dim: spv.SpvDim,
x: i32,
y: i32,
z: i32,
} = null,
/// Exact byte window in externally visible descriptor storage that
/// corresponds to this pointer. For a pointer to struct member N this
/// starts at the member offset, not at the containing struct.
uniform_slice_window: ?[]u8 = null,
/// Heap-owned value that backs a pointer into a materialized runtime
/// array element. This may differ from ptr.common when the pointer is
/// to a child/member of that materialized value.
uniform_backing_value: ?*Self = null,
},
pub inline fn getCompositeDataOrNull(self: *const Self) ?[]Self {
return switch (self.*) {
.Structure => |*s| s.values,
.Array => |*a| a.values,
.Vector, .Matrix => |v| v,
.Vector => |v| v,
else => null,
};
}
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;
const member_count = results[resolved].getMemberCounts();
return initUnresolved(allocator, results, resolved, is_externally_visible);
}
return switch (results[resolved].variant.?) {
pub fn initUnresolved(allocator: std.mem.Allocator, results: []const Result, target_type: SpvWord, is_externally_visible: bool) RuntimeError!Self {
const member_count = results[target_type].getMemberCounts();
return switch (results[target_type].variant.?) {
.Type => |t| switch (t) {
.Void => .{ .Void = .{} },
.Bool => .{ .Bool = false },
@@ -159,13 +186,13 @@ pub const Value = union(Type) {
}
break :blk self;
},
.Vector4f32 => .{ .Vector4f32 = Vec4f32{ 0.0, 0.0, 0.0, 0.0 } },
.Vector4f32 => .{ .Vector4f32 = Vec4f32{ 0.0, 0.0, 0.0, 1.0 } },
.Vector3f32 => .{ .Vector3f32 = Vec3f32{ 0.0, 0.0, 0.0 } },
.Vector2f32 => .{ .Vector2f32 = Vec2f32{ 0.0, 0.0 } },
.Vector4i32 => .{ .Vector4i32 = Vec4i32{ 0, 0, 0, 0 } },
.Vector4i32 => .{ .Vector4i32 = Vec4i32{ 0, 0, 0, 1 } },
.Vector3i32 => .{ .Vector3i32 = Vec3i32{ 0, 0, 0 } },
.Vector2i32 => .{ .Vector2i32 = Vec2i32{ 0, 0 } },
.Vector4u32 => .{ .Vector4u32 = Vec4u32{ 0, 0, 0, 0 } },
.Vector4u32 => .{ .Vector4u32 = Vec4u32{ 0, 0, 0, 1 } },
.Vector3u32 => .{ .Vector3u32 = Vec3u32{ 0, 0, 0 } },
.Vector2u32 => .{ .Vector2u32 = Vec2u32{ 0, 0 } },
.Matrix => |m| blk: {
@@ -205,6 +232,7 @@ pub const Value = union(Type) {
.Structure => |s| blk: {
const self: Self = .{
.Structure = .{
.external_data = null,
.offsets = allocator.dupe(?SpvWord, s.members_offsets) catch return RuntimeError.OutOfMemory,
.values = allocator.alloc(Self, member_count) catch return RuntimeError.OutOfMemory,
},
@@ -225,12 +253,27 @@ pub const Value = union(Type) {
},
.Image => .{
.Image = .{
.type_word = resolved,
.type_word = target_type,
.driver_image = undefined,
},
},
.Sampler => RuntimeError.ToDo,
.SampledImage => RuntimeError.ToDo,
.Sampler => .{
.Sampler = .{
.driver_sampler = undefined,
},
},
.SampledImage => .{
.SampledImage = .{
.type_word = target_type,
.driver_image = undefined,
.driver_sampler = undefined,
},
},
.Pointer => .{
.Pointer = .{
.ptr = undefined,
},
},
else => RuntimeError.InvalidSpirV,
},
else => RuntimeError.InvalidSpirV,
@@ -273,6 +316,7 @@ pub const Value = union(Type) {
errdefer allocator.free(values);
for (values, s.values) |*new_value, value| new_value.* = try value.dupe(allocator);
break :blk .{
.external_data = s.external_data,
.offsets = allocator.dupe(?SpvWord, s.offsets) catch return RuntimeError.OutOfMemory,
.values = values,
};
@@ -283,6 +327,23 @@ pub const Value = union(Type) {
}
pub fn read(self: *const Self, output: []u8) RuntimeError!usize {
const vecRoutine = struct {
inline fn routine(comptime T: type, vec: T, out: []u8) RuntimeError!usize {
const size = @typeInfo(T).vector.len * 4;
if (size >= out.len) {
const range = @typeInfo(T).vector.len;
inline for (0..range) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= out.len or end > out.len) return i * 4;
@memcpy(out[start..end], std.mem.asBytes(&vec[i]));
}
}
std.mem.bytesAsValue(T, out[0..]).* = vec;
return size;
}
}.routine;
switch (self.*) {
.Bool => |b| {
output[0] = if (b == true) 1 else 0;
@@ -291,76 +352,35 @@ pub const Value = union(Type) {
.Int => |i| {
switch (i.bit_count) {
8 => output[0] = @bitCast(i.value.uint8),
16 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint16)),
32 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint32)),
64 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint64)),
16 => @memcpy(output[0..2], std.mem.asBytes(&i.value.uint16)),
32 => @memcpy(output[0..4], std.mem.asBytes(&i.value.uint32)),
64 => @memcpy(output[0..8], std.mem.asBytes(&i.value.uint64)),
else => return RuntimeError.InvalidValueType,
}
return @divExact(i.bit_count, 8);
},
.Float => |f| {
switch (f.bit_count) {
16 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float16)),
32 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float32)),
64 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float64)),
16 => @memcpy(output[0..2], std.mem.asBytes(&f.value.float16)),
32 => @memcpy(output[0..4], std.mem.asBytes(&f.value.float32)),
64 => @memcpy(output[0..8], std.mem.asBytes(&f.value.float64)),
else => return RuntimeError.InvalidValueType,
}
return @divExact(f.bit_count, 8);
},
.Vector4f32 => |vec| {
inline for (0..4) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 4 * 4;
},
.Vector3f32 => |vec| {
inline for (0..3) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 3 * 4;
},
.Vector2f32 => |vec| {
inline for (0..2) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 2 * 4;
},
.Vector4i32 => |vec| {
inline for (0..4) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 4 * 4;
},
.Vector3i32 => |vec| {
inline for (0..3) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 3 * 4;
},
.Vector2i32 => |vec| {
inline for (0..2) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 2 * 4;
},
.Vector4u32 => |vec| {
inline for (0..4) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 4 * 4;
},
.Vector3u32 => |vec| {
inline for (0..3) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 3 * 4;
},
.Vector2u32 => |vec| {
inline for (0..2) |i| {
std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i]));
}
return 2 * 4;
},
.Vector4f32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector3f32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector2f32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector4i32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector3i32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector2i32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector4u32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector3u32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector2u32 => |vec| return vecRoutine(@TypeOf(vec), vec, output),
.Vector, .Matrix => |values| {
var offset: usize = 0;
for (values) |v| {
@@ -377,29 +397,38 @@ pub const Value = union(Type) {
return offset;
},
.Structure => |s| {
var offset: usize = 0;
var end_offset: usize = 0;
for (s.values, 0..) |v, i| {
const read_size = try v.read(output[offset..]);
if (i + 1 < s.offsets.len) {
if (s.offsets[i + 1]) |o| {
offset = o;
continue;
const member_offset: usize = @intCast(s.offsets[i] orelse end_offset);
const read_size = try v.read(output[member_offset..]);
end_offset = @max(end_offset, member_offset + read_size);
}
}
offset += read_size;
}
return offset;
return end_offset;
},
.RuntimeArray => {},
else => return RuntimeError.InvalidValueType,
}
return 0;
}
pub fn writeConst(self: *Self, input: []const u8) RuntimeError!usize {
return self.write(@constCast(input));
pub fn write(self: *Self, input: []const u8) RuntimeError!usize {
const vecRoutine = struct {
inline fn routine(comptime T: type, vec: *T, in: []const u8) RuntimeError!usize {
const size = @typeInfo(T).vector.len * 4;
if (size >= in.len) {
const range = @typeInfo(T).vector.len;
inline for (0..range) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= in.len or end > in.len) return i * 4;
@memcpy(std.mem.asBytes(&vec[i]), in[start..end]);
}
}
vec.* = std.mem.bytesToValue(T, in[0..]);
return size;
}
}.routine;
pub fn write(self: *Self, input: []u8) RuntimeError!usize {
switch (self.*) {
.Bool => |*b| {
b.* = if (input[0] != 0) true else false;
@@ -408,103 +437,35 @@ pub const Value = union(Type) {
.Int => |*i| {
switch (i.bit_count) {
8 => i.value.uint8 = @bitCast(input[0]),
16 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint16), input[0..2]),
32 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint32), input[0..4]),
64 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint64), input[0..8]),
16 => @memcpy(std.mem.asBytes(&i.value.uint16), input[0..2]),
32 => @memcpy(std.mem.asBytes(&i.value.uint32), input[0..4]),
64 => @memcpy(std.mem.asBytes(&i.value.uint64), input[0..8]),
else => return RuntimeError.InvalidValueType,
}
return @divExact(i.bit_count, 8);
},
.Float => |*f| {
switch (f.bit_count) {
16 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float16), input[0..2]),
32 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float32), input[0..4]),
64 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float64), input[0..8]),
16 => @memcpy(std.mem.asBytes(&f.value.float16), input[0..2]),
32 => @memcpy(std.mem.asBytes(&f.value.float32), input[0..4]),
64 => @memcpy(std.mem.asBytes(&f.value.float64), input[0..8]),
else => return RuntimeError.InvalidValueType,
}
return @divExact(f.bit_count, 8);
},
.Vector4f32 => |*vec| {
inline for (0..4) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 4 * 4;
},
.Vector3f32 => |*vec| {
inline for (0..3) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 3 * 4;
},
.Vector2f32 => |*vec| {
inline for (0..2) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 2 * 4;
},
.Vector4i32 => |*vec| {
inline for (0..4) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 4 * 4;
},
.Vector3i32 => |*vec| {
inline for (0..3) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 3 * 4;
},
.Vector2i32 => |*vec| {
inline for (0..2) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 2 * 4;
},
.Vector4u32 => |*vec| {
inline for (0..4) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 4 * 4;
},
.Vector3u32 => |*vec| {
inline for (0..3) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 3 * 4;
},
.Vector2u32 => |*vec| {
inline for (0..2) |i| {
const start = i * 4;
const end = (i + 1) * 4;
if (start >= input.len or end > input.len) return RuntimeError.OutOfBounds;
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
}
return 2 * 4;
},
.Vector4f32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector3f32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector2f32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector4i32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector3i32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector2i32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector4u32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector3u32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector2u32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input),
.Vector, .Matrix => |*values| {
var offset: usize = 0;
for (values.*) |*v| {
@@ -520,22 +481,23 @@ pub const Value = union(Type) {
}
return offset;
},
.Structure => |s| {
var offset: usize = 0;
.Structure => |*s| {
var end_offset: usize = 0;
for (s.values, 0..) |*v, i| {
const write_size = try v.write(input[offset..]);
if (i + 1 < s.offsets.len) {
if (s.offsets[i + 1]) |o| {
offset = o;
continue;
const member_offset: usize = @intCast(s.offsets[i] orelse end_offset);
const write_size = try v.write(input[member_offset..]);
end_offset = @max(end_offset, member_offset + write_size);
}
}
offset += write_size;
}
return offset;
s.external_data = @constCast(input[0..end_offset]);
return end_offset;
},
.RuntimeArray => |*arr| arr.data = input[0..],
.RuntimeArray => |*arr| arr.data = @constCast(input[0..]),
.Image => |*img| img.driver_image = @ptrFromInt(std.mem.bytesToValue(usize, input[0..])),
.Sampler => |*sampler| sampler.driver_sampler = @ptrFromInt(std.mem.bytesToValue(usize, input[0..])),
.SampledImage => |*img| {
img.driver_image = @ptrFromInt(std.mem.bytesToValue(usize, input[0..]));
img.driver_sampler = @ptrFromInt(std.mem.bytesToValue(usize, input[@sizeOf(usize)..]));
},
else => return RuntimeError.InvalidValueType,
}
return 0;
@@ -560,13 +522,8 @@ pub const Value = union(Type) {
.Structure => |s| blk: {
var size: usize = 0;
for (s.values, 0..) |v, i| {
if (i + 1 < s.offsets.len) {
if (s.offsets[i + 1]) |o| {
size = o;
continue;
}
}
size += try v.getPlainMemorySize();
const member_offset: usize = @intCast(s.offsets[i] orelse size);
size = @max(size, member_offset + try v.getPlainMemorySize());
}
break :blk size;
},
@@ -617,14 +574,35 @@ pub const Value = union(Type) {
switch (p.ptr) {
.common => |ptr| {
_ = try ptr.read(window);
ptr.deinit(allocator);
if (p.is_owner_of_uniform_slice)
allocator.destroy(ptr);
},
else => {},
.f32_ptr => |ptr| {
if (window.len < @sizeOf(f32)) return RuntimeError.OutOfBounds;
@memcpy(window[0..@sizeOf(f32)], std.mem.asBytes(ptr));
},
.i32_ptr => |ptr| {
if (window.len < @sizeOf(i32)) return RuntimeError.OutOfBounds;
@memcpy(window[0..@sizeOf(i32)], std.mem.asBytes(ptr));
},
.u32_ptr => |ptr| {
if (window.len < @sizeOf(u32)) return RuntimeError.OutOfBounds;
@memcpy(window[0..@sizeOf(u32)], std.mem.asBytes(ptr));
},
}
if (p.uniform_backing_value) |backing| {
backing.deinit(allocator);
allocator.destroy(backing);
p.uniform_backing_value = null;
}
p.uniform_slice_window = null;
}
if (p.uniform_backing_value) |backing| {
backing.deinit(allocator);
allocator.destroy(backing);
p.uniform_backing_value = null;
}
},
else => {},
}
@@ -658,28 +636,64 @@ pub const Value = union(Type) {
.Vector => |lanes| (try getPrimitiveField(T, bits, &lanes[lane_index])).*,
.Vector2i32 => |*vec| switch (lane_index) {
inline 0...1 => |i| if (bits == 32) @as(TT, @bitCast(vec[i])) else return RuntimeError.InvalidSpirV,
inline 0...1 => |i| blk: {
if (bits == 32) {
break :blk @as(TT, @bitCast(vec[i]));
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
.Vector3i32 => |*vec| switch (lane_index) {
inline 0...2 => |i| if (bits == 32) @as(TT, @bitCast(vec[i])) else return RuntimeError.InvalidSpirV,
inline 0...2 => |i| blk: {
if (bits == 32) {
break :blk @as(TT, @bitCast(vec[i]));
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
.Vector4i32 => |*vec| switch (lane_index) {
inline 0...3 => |i| if (bits == 32) @as(TT, @bitCast(vec[i])) else return RuntimeError.InvalidSpirV,
inline 0...3 => |i| blk: {
if (bits == 32) {
break :blk @as(TT, @bitCast(vec[i]));
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
.Vector2u32 => |*vec| switch (lane_index) {
inline 0...1 => |i| if (bits == 32) @as(TT, @bitCast(vec[i])) else return RuntimeError.InvalidSpirV,
inline 0...1 => |i| blk: {
if (bits == 32) {
break :blk @as(TT, @bitCast(vec[i]));
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
.Vector3u32 => |*vec| switch (lane_index) {
inline 0...2 => |i| if (bits == 32) @as(TT, @bitCast(vec[i])) else return RuntimeError.InvalidSpirV,
inline 0...2 => |i| blk: {
if (bits == 32) {
break :blk @as(TT, @bitCast(vec[i]));
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
.Vector4u32 => |*vec| switch (lane_index) {
inline 0...3 => |i| if (bits == 32) @as(TT, @bitCast(vec[i])) else return RuntimeError.InvalidSpirV,
inline 0...3 => |i| blk: {
if (bits == 32) {
break :blk @as(TT, @bitCast(vec[i]));
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
@@ -694,28 +708,52 @@ pub const Value = union(Type) {
.Vector => |lanes| try setScalarLaneValue(T, bits, &lanes[lane_index], value),
.Vector2i32 => |*vec| switch (lane_index) {
inline 0...1 => |i| vec[i] = if (bits == 32) @bitCast(value) else return RuntimeError.InvalidSpirV,
inline 0...1 => |i| if (bits == 32) {
vec[i] = @bitCast(value);
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
.Vector3i32 => |*vec| switch (lane_index) {
inline 0...2 => |i| vec[i] = if (bits == 32) @bitCast(value) else return RuntimeError.InvalidSpirV,
inline 0...2 => |i| if (bits == 32) {
vec[i] = @bitCast(value);
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
.Vector4i32 => |*vec| switch (lane_index) {
inline 0...3 => |i| vec[i] = if (bits == 32) @bitCast(value) else return RuntimeError.InvalidSpirV,
inline 0...3 => |i| if (bits == 32) {
vec[i] = @bitCast(value);
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
.Vector2u32 => |*vec| switch (lane_index) {
inline 0...1 => |i| vec[i] = if (bits == 32) @bitCast(value) else return RuntimeError.InvalidSpirV,
inline 0...1 => |i| if (bits == 32) {
vec[i] = @bitCast(value);
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
.Vector3u32 => |*vec| switch (lane_index) {
inline 0...2 => |i| vec[i] = if (bits == 32) @bitCast(value) else return RuntimeError.InvalidSpirV,
inline 0...2 => |i| if (bits == 32) {
vec[i] = @bitCast(value);
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
.Vector4u32 => |*vec| switch (lane_index) {
inline 0...3 => |i| vec[i] = if (bits == 32) @bitCast(value) else return RuntimeError.InvalidSpirV,
inline 0...3 => |i| if (bits == 32) {
vec[i] = @bitCast(value);
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
@@ -752,6 +790,10 @@ pub const Value = union(Type) {
}
}
pub fn getPrimitiveFieldConst(comptime T: PrimitiveType, comptime BitCount: SpvWord, v: *const Value) RuntimeError!*const getPrimitiveFieldType(T, BitCount) {
return getPrimitiveField(T, BitCount, @constCast(v));
}
pub fn getPrimitiveField(comptime T: PrimitiveType, comptime BitCount: SpvWord, v: *Value) RuntimeError!*getPrimitiveFieldType(T, BitCount) {
if (std.meta.activeTag(v.*) == .Pointer) {
return switch (v.Pointer.ptr) {
@@ -790,8 +832,8 @@ pub const Value = union(Type) {
pub fn resolveLaneBitWidth(self: *const Self) RuntimeError!SpvWord {
return switch (self.*) {
.Bool => 8,
.Float => |f| f.bit_length,
.Int => |i| i.bit_length,
.Float => |f| @intCast(f.bit_count),
.Int => |i| @intCast(i.bit_count),
.Vector => |v| v[0].resolveLaneBitWidth(),
.Vector4f32,
.Vector3f32,
@@ -831,4 +873,28 @@ pub const Value = union(Type) {
else => .unsigned,
};
}
pub inline fn getVectorSpecialization(self: *const Self, comptime N: usize, comptime T: type) @Vector(N, T) {
return switch (T) {
f32 => switch (N) {
inline 4 => self.Vector4f32,
inline 3 => self.Vector3f32,
inline 2 => self.Vector2f32,
else => unreachable,
},
i32 => switch (N) {
inline 4 => self.Vector4i32,
inline 3 => self.Vector3i32,
inline 2 => self.Vector2i32,
else => unreachable,
},
u32 => switch (N) {
inline 4 => self.Vector4u32,
inline 3 => self.Vector3u32,
inline 2 => self.Vector2u32,
else => unreachable,
},
else => unreachable,
};
}
};
+2
View File
@@ -41,6 +41,8 @@ pub const SpvByte = spv.SpvByte;
pub const SpvWord = spv.SpvWord;
pub const SpvBool = spv.SpvBool;
pub const SpvDim = spv.SpvDim;
pub const Vec4f32 = @Vector(4, f32);
pub const Vec3f32 = @Vector(3, f32);
pub const Vec2f32 = @Vector(2, f32);
+1736 -641
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -263,9 +263,9 @@ pub const SpvStorageClass = enum(u32) {
};
pub const SpvDim = enum(u32) {
_1D = 0,
_2D = 1,
_3D = 2,
@"1D" = 0,
@"2D" = 1,
@"3D" = 2,
Cube = 3,
Rect = 4,
Buffer = 5,
+201
View File
@@ -1,5 +1,6 @@
const std = @import("std");
const root = @import("root.zig");
const zm = @import("zmath");
const compileNzsl = root.compileNzsl;
const case = root.case;
@@ -154,3 +155,203 @@ test "Maths vectors" {
}
}
}
// Tests all mathematical operation on vec2/3/4 with scalars with all NZSL supported primitive types
test "Maths vectors with scalars" {
const allocator = std.testing.allocator;
const types = [_]type{ f32, f64, i32, u32 };
var operations = std.EnumMap(Operations, u8).init(.{
.Mul = '*',
.Div = '/',
.Mod = '%',
});
var it = operations.iterator();
while (it.next()) |op| {
inline for (2..5) |L| {
inline for (types) |T| {
const base_color: case.Vec(L, T) = .{ .val = case.random(@Vector(L, T)) };
const ratio = case.random(T);
const splat_ratio = @as(@Vector(L, T), @splat(ratio));
const expected = switch (op.key) {
.Mul => if (@typeInfo(T) == .int) @mulWithOverflow(base_color.val, splat_ratio)[0] else base_color.val * splat_ratio,
.Div => if (@typeInfo(T) == .int) @divTrunc(base_color.val, splat_ratio) else base_color.val / splat_ratio,
.Mod => @mod(base_color.val, splat_ratio),
else => unreachable,
};
const shader = try std.fmt.allocPrint(
allocator,
\\ [nzsl_version("1.1")]
\\ [feature(float64)]
\\ module;
\\
\\ struct FragOut
\\ {{
\\ [location(0)] color: vec{d}[{s}]
\\ }}
\\
\\ [entry(frag)]
\\ fn main() -> FragOut
\\ {{
\\ let output: FragOut;
\\ output.color = vec{d}[{s}]({f}) {c} {d};
\\ return output;
\\ }}
,
.{
L,
@typeName(T),
L,
@typeName(T),
base_color,
op.value.*,
ratio,
},
);
defer allocator.free(shader);
const code = try compileNzsl(allocator, shader);
defer allocator.free(code);
try case.expect(.{
.source = code,
.expected_outputs = &.{
std.mem.asBytes(&@as([L]T, expected)),
},
});
}
}
}
}
// Tests all mathematical operation on mat3/4 with all NZSL supported primitive types
test "Maths matrices" {
const allocator = std.testing.allocator;
const types = [_]type{ f32, f64 };
var operations = std.EnumMap(Operations, u8).init(.{
.Add = '+',
.Sub = '-',
.Mul = '*',
});
var it = operations.iterator();
while (it.next()) |op| {
inline for (3..5) |L| {
inline for (types) |T| {
const base: case.Mat(L, T) = .{ .val = case.random([L][L]T) };
const ratio: case.Mat(L, T) = .{ .val = case.random([L][L]T) };
var expected: case.Mat(L, T) = undefined;
for (expected.val[0..], base.val[0..], ratio.val[0..]) |*ec, bc, rc| {
for (ec[0..], bc[0..], rc[0..]) |*e, b, r| {
e.* = switch (op.key) {
.Add => b + r,
.Sub => b - r,
.Mul => b * r,
else => unreachable,
};
}
}
const shader = try std.fmt.allocPrint(
allocator,
\\ [nzsl_version("1.1")]
\\ [feature(float64)]
\\ module;
\\
\\ struct FragOut
\\ {{
\\ [location(0)] value: mat{d}[{s}]
\\ }}
\\
\\ [entry(frag)]
\\ fn main() -> FragOut
\\ {{
\\ let output: FragOut;
\\ output.value = mat{d}[{s}]({f}) {c} mat{d}[{s}]({f});
\\ return output;
\\ }}
,
.{
L,
@typeName(T),
L,
@typeName(T),
base,
op.value.*,
L,
@typeName(T),
ratio,
},
);
defer allocator.free(shader);
const code = try compileNzsl(allocator, shader);
defer allocator.free(code);
try case.expect(.{
.source = code,
.expected_outputs = &.{
std.mem.asBytes(&expected),
},
});
}
}
}
}
// Tests all mathematical operation on mat3/4 with all NZSL supported vectors
test "Maths matrices with vectors" {
const allocator = std.testing.allocator;
const types = [_]type{ f32, f64 };
inline for (3..5) |L| {
inline for (types) |T| {
const base: case.Mat(L, T) = .{ .val = case.random([L][L]T) };
const ratio: case.Vec(L, T) = .{ .val = case.random(@Vector(L, T)) };
var expected: @Vector(L, T) = undefined;
expected[0] = (base.val[0][0] * ratio.val[0]) + (base.val[0][1] * ratio.val[1]) + (base.val[0][2] * ratio.val[2]) + if (L == 4) (base.val[0][3] * ratio.val[3]) else 0.0;
expected[1] = (base.val[1][0] * ratio.val[0]) + (base.val[1][1] * ratio.val[1]) + (base.val[1][2] * ratio.val[2]) + if (L == 4) (base.val[1][3] * ratio.val[3]) else 0.0;
expected[2] = (base.val[2][0] * ratio.val[0]) + (base.val[2][1] * ratio.val[1]) + (base.val[2][2] * ratio.val[2]) + if (L == 4) (base.val[2][3] * ratio.val[3]) else 0.0;
if (L == 4)
expected[3] = (base.val[3][0] * ratio.val[0]) + (base.val[3][1] * ratio.val[1]) + (base.val[3][2] * ratio.val[2]) + (base.val[3][3] * ratio.val[3]);
const shader = try std.fmt.allocPrint(
allocator,
\\ [nzsl_version("1.1")]
\\ [feature(float64)]
\\ module;
\\
\\ struct FragOut
\\ {{
\\ [location(0)] value: vec{d}[{s}]
\\ }}
\\
\\ [entry(frag)]
\\ fn main() -> FragOut
\\ {{
\\ let output: FragOut;
\\ output.value = mat{d}[{s}]({f}) * vec{d}[{s}]({f});
\\ return output;
\\ }}
,
.{
L,
@typeName(T),
L,
@typeName(T),
base,
L,
@typeName(T),
ratio,
},
);
defer allocator.free(shader);
const code = try compileNzsl(allocator, shader);
defer allocator.free(code);
try case.expect(.{
.source = code,
.expected_outputs = &.{
std.mem.asBytes(&@as([L]T, expected)),
},
});
}
}
}
+25 -3
View File
@@ -34,10 +34,10 @@ pub const case = struct {
// To test with all important module options
const module_options = [_]spv.Module.ModuleOptions{
.{
.use_simd_vectors_specializations = true,
.use_simd_vectors_specializations = false,
},
.{
.use_simd_vectors_specializations = false,
.use_simd_vectors_specializations = true,
},
};
@@ -78,7 +78,7 @@ pub const case = struct {
}
pub fn random(comptime T: type) T {
var prng: std.Random.DefaultPrng = .init(@intCast(std.Io.Timestamp.now(std.testing.io, .real).toMicroseconds()));
var prng: std.Random.DefaultPrng = .init(@intCast(std.Io.Timestamp.now(std.testing.io, .real).toNanoseconds()));
const rand = prng.random();
return switch (@typeInfo(T)) {
@@ -91,6 +91,13 @@ pub const case = struct {
}
break :blk vec;
},
.array => |a| blk: {
var arr: [a.len]a.child = undefined;
inline for (0..a.len) |i| {
arr[i] = random(a.child);
}
break :blk arr;
},
inline else => unreachable,
};
}
@@ -107,6 +114,21 @@ pub const case = struct {
}
};
}
pub fn Mat(comptime len: usize, comptime T: type) type {
return struct {
const Self = @This();
val: [len][len]T,
pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
inline for (0..len) |i| {
inline for (0..len) |j| {
try w.print("{d}", .{self.val[i][j]});
if (i < len - 1 or j < len - 1) try w.writeAll(", ");
}
}
}
};
}
};
test {