Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 046b1c8f9e | |||
|
cc041c9677
|
|||
|
9cdb683f3f
|
|||
|
5b7380eea0
|
|||
|
f617fe417c
|
|||
|
147126c06f
|
|||
| ab34c3b49a | |||
|
ef69470183
|
|||
|
664ea9b92b
|
|||
|
c61514feea
|
|||
|
4bd688cf07
|
|||
|
9f586ae9c0
|
|||
| b051c71f02 | |||
| 85e0fe4362 | |||
|
a83a761afa
|
|||
|
15eb36ea4d
|
|||
|
1c26e16eb7
|
|||
| 8072d671af | |||
|
6c8b364c7d
|
|||
|
fbaf85a849
|
|||
|
3c7054fae0
|
|||
|
a503f54e43
|
|||
|
d1bf1c23f2
|
|||
|
2d0d3b40fd
|
|||
|
cb0cdaab36
|
|||
|
569d7fda01
|
|||
|
c18deb432f
|
|||
|
fe47277468
|
|||
|
e8a08d7885
|
|||
|
9e72a6d2bc
|
|||
|
7074345540
|
|||
|
72faa35357
|
|||
|
7dd86b021d
|
|||
|
5d704dcc5f
|
|||
|
1a48af468d
|
|||
|
ad013d23fc
|
|||
|
16eb184808
|
|||
|
7bf671d974
|
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mlugg/setup-zig@v2
|
||||
- uses: https://codeberg.org/mlugg/setup-zig@v2
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mlugg/setup-zig@v2
|
||||
- uses: https://codeberg.org/mlugg/setup-zig@v2
|
||||
|
||||
- name: Test
|
||||
run: zig build test -Dno-example=true
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.zig-cache/
|
||||
zig-out/
|
||||
zig-pkg/
|
||||
.gdb_history
|
||||
*.o
|
||||
vgcore*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 kbz_8
|
||||
Copyright (c) 2026 kbz_8
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPIR-V Interpreter <a href="https://git.kbz8.me/kbz_8/SPIRV-Interpreter/actions?workflows=build.yml"><img src="https://git.kbz8.me/kbz_8/SPIRV-Interpreter/actions/workflows/build.yml/badge.svg"></a> <a href="https://git.kbz8.me/kbz_8/SPIRV-Interpreter/actions?workflows=test.yml"><img src="https://git.kbz8.me/kbz_8/SPIRV-Interpreter/actions/workflows/test.yml/badge.svg"></a>
|
||||
|
||||
A small footprint SPIR-V interpreter to execute SPIR-V shaders on the CPU. It is designed to be used with multiple runtimes concurrently.
|
||||
A small footprint SPIR-V interpreter to execute SPIR-V shaders on the CPU. It is designed to be used with multiple runtimes concurrently and can be SIMD accelerated.
|
||||
|
||||
```zig
|
||||
const std = @import("std");
|
||||
@@ -23,9 +23,64 @@ pub fn main() !void {
|
||||
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
var output: [4]f32 = undefined;
|
||||
try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color"));
|
||||
try rt.readOutput(std.mem.asBytes(output[0..output.len]), try rt.getResultByName("color"));
|
||||
std.log.info("Output: Vec4{any}", .{output});
|
||||
}
|
||||
std.log.info("Successfully executed", .{});
|
||||
}
|
||||
```
|
||||
|
||||
## C bindings
|
||||
|
||||
### Build
|
||||
To build the FFI bindings just
|
||||
```
|
||||
zig build ffi-c --release=[fast, small, safe]
|
||||
```
|
||||
|
||||
You can also build a shared lib using
|
||||
```
|
||||
zig build ffi-c --release=[fast, small, safe] -Dffi-build-static=false
|
||||
```
|
||||
|
||||
You'll find the library in `./zig-out/lib/` and the header file in `./zig-out/include/` or in `./ffi/`.
|
||||
|
||||
### Example
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <SpirvInterpreter.h>
|
||||
|
||||
static const unsigned char shader_source[] = {
|
||||
/* Shader bytecode */
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
SpvModule module;
|
||||
SpvModuleOptions options;
|
||||
options.use_simd_vectors_specializations = 1;
|
||||
|
||||
if(SpvInitModule(&module, (SpvWord*)shader_source, sizeof(shader_source) / 4, options) != SPV_RESULT_SUCCESS)
|
||||
return -1;
|
||||
|
||||
SpvRuntime runtime;
|
||||
if(SpvInitRuntime(&runtime, module) != SPV_RESULT_SUCCESS)
|
||||
return -1;
|
||||
|
||||
SpvWord main_entry_index;
|
||||
SpvGetEntryPointByName(runtime, "main", &main_entry_index);
|
||||
SpvCallEntryPoint(runtime, main_entry_index);
|
||||
|
||||
float output[4];
|
||||
SpvWord output_result;
|
||||
SpvGetResultByName(runtime, "color", &output_result);
|
||||
SpvReadOutput(runtime, (SpvByte*)output, sizeof(output), output_result);
|
||||
|
||||
printf("Output: Vec4[%f, %f, %f, %f]\n", output[0], output[1], output[2], output[3]);
|
||||
|
||||
SpvDeinitRuntime(runtime);
|
||||
SpvDeinitModule(module);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -3,129 +3,256 @@ const std = @import("std");
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const use_llvm = b.option(bool, "use-llvm", "Use LLVM backend") orelse (b.release_mode != .off);
|
||||
|
||||
const use_llvm = b.option(bool, "use-llvm", "use llvm") orelse (b.release_mode != .off);
|
||||
|
||||
const mod = b.addModule("spv", .{
|
||||
const spv_mod = b.addModule("spv", .{
|
||||
.root_source_file = b.path("src/lib.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const zmath = b.dependency("zmath", .{});
|
||||
mod.addImport("zmath", zmath.module("root"));
|
||||
spv_mod.addImport("zmath", zmath.module("root"));
|
||||
|
||||
const pretty = b.dependency("pretty", .{ .target = target, .optimize = optimize });
|
||||
mod.addImport("pretty", pretty.module("pretty"));
|
||||
const pretty = b.dependency("pretty", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
spv_mod.addImport("pretty", pretty.module("pretty"));
|
||||
|
||||
const lib = b.addLibrary(.{
|
||||
const spv_lib = b.addLibrary(.{
|
||||
.name = "spirv_interpreter",
|
||||
.root_module = mod,
|
||||
.root_module = spv_mod,
|
||||
.linkage = .dynamic,
|
||||
.use_llvm = use_llvm,
|
||||
});
|
||||
const lib_install = b.addInstallArtifact(lib, .{});
|
||||
|
||||
// Zig example setup
|
||||
const install_spv_lib = b.addInstallArtifact(spv_lib, .{});
|
||||
|
||||
const no_example = b.option(bool, "no-example", "skips example dependencies fetch") orelse false;
|
||||
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);
|
||||
addCffi(b, target, optimize, use_llvm, spv_mod);
|
||||
addDocs(b, spv_mod);
|
||||
}
|
||||
|
||||
if (!no_example) {
|
||||
const sdl3 = b.lazyDependency("sdl3", .{ .target = target, .optimize = optimize }) orelse return;
|
||||
const example_exe = b.addExecutable(.{
|
||||
fn addExample(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
use_llvm: bool,
|
||||
spv_mod: *std.Build.Module,
|
||||
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) {
|
||||
const sdl3 = b.lazyDependency("sdl3", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}) orelse return;
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "spirv_interpreter_example",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("example/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "spv", .module = mod },
|
||||
.{ .name = "spv", .module = spv_mod },
|
||||
.{ .name = "sdl3", .module = sdl3.module("sdl3") },
|
||||
//.{ .name = "pretty", .module = pretty.module("pretty") },
|
||||
},
|
||||
}),
|
||||
.use_llvm = use_llvm,
|
||||
});
|
||||
|
||||
const example_install = b.addInstallArtifact(example_exe, .{});
|
||||
example_install.step.dependOn(&lib_install.step);
|
||||
const install_exe = b.addInstallArtifact(exe, .{});
|
||||
install_exe.step.dependOn(install_spv_lib_step);
|
||||
|
||||
const run_example = b.addRunArtifact(example_exe);
|
||||
run_example.step.dependOn(&example_install.step);
|
||||
const run_exe = b.addRunArtifact(exe);
|
||||
run_exe.step.dependOn(&install_exe.step);
|
||||
|
||||
const run_example_step = b.step("example", "Run the example");
|
||||
run_example_step.dependOn(&run_example.step);
|
||||
const run_step = b.step("example", "Run the example");
|
||||
run_step.dependOn(&run_exe.step);
|
||||
|
||||
const compile_shader_cmd = b.addSystemCommand(&[_][]const u8{ "nzslc", "example/shader.nzsl", "--compile=spv,spv-dis", "-o", "example" });
|
||||
const compile_shader_step = b.step("example-shader", "Compiles example's shader (needs nzslc installed)");
|
||||
compile_shader_step.dependOn(&compile_shader_cmd.step);
|
||||
addShaderCompileStep(
|
||||
b,
|
||||
"example-shader",
|
||||
"Compile example shader using nzslc",
|
||||
"example/shader.nzsl",
|
||||
"example",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Zig sandbox setup
|
||||
|
||||
const sandbox_exe = b.addExecutable(.{
|
||||
fn addSandbox(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
use_llvm: bool,
|
||||
spv_mod: *std.Build.Module,
|
||||
install_spv_lib_step: *std.Build.Step,
|
||||
) void {
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "spirv_interpreter_sandbox",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("sandbox/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "spv", .module = mod },
|
||||
//.{ .name = "pretty", .module = pretty.module("pretty") },
|
||||
.{ .name = "spv", .module = spv_mod },
|
||||
},
|
||||
}),
|
||||
.use_llvm = use_llvm,
|
||||
});
|
||||
|
||||
const sandbox_install = b.addInstallArtifact(sandbox_exe, .{});
|
||||
sandbox_install.step.dependOn(&lib_install.step);
|
||||
const install_exe = b.addInstallArtifact(exe, .{});
|
||||
install_exe.step.dependOn(install_spv_lib_step);
|
||||
|
||||
const run_sandbox = b.addRunArtifact(sandbox_exe);
|
||||
run_sandbox.step.dependOn(&sandbox_install.step);
|
||||
const run_exe = b.addRunArtifact(exe);
|
||||
run_exe.step.dependOn(&install_exe.step);
|
||||
|
||||
const run_sandbox_step = b.step("sandbox", "Run the sandbox");
|
||||
run_sandbox_step.dependOn(&run_sandbox.step);
|
||||
const run_step = b.step("sandbox", "Run the sandbox");
|
||||
run_step.dependOn(&run_exe.step);
|
||||
|
||||
const compile_shader_cmd = b.addSystemCommand(&[_][]const u8{ "nzslc", "sandbox/shader.nzsl", "--compile=spv,spv-dis", "-o", "sandbox" });
|
||||
const compile_shader_step = b.step("sandbox-shader", "Compiles sandbox's shader (needs nzslc installed)");
|
||||
compile_shader_step.dependOn(&compile_shader_cmd.step);
|
||||
addShaderCompileStep(
|
||||
b,
|
||||
"sandbox-shader",
|
||||
"Compile sandbox shader using nzslc",
|
||||
"sandbox/shader.nzsl",
|
||||
"sandbox",
|
||||
);
|
||||
}
|
||||
|
||||
// Zig unit tests setup
|
||||
|
||||
const no_test = b.option(bool, "no-test", "skips unit test dependencies fetch") orelse false;
|
||||
|
||||
if (!no_test) {
|
||||
const nzsl = b.lazyDependency("NZSL", .{ .target = target, .optimize = optimize }) orelse return;
|
||||
const lib_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("test/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "spv", .module = mod },
|
||||
.{ .name = "nzsl", .module = nzsl.module("nzigsl") },
|
||||
.{ .name = "zmath", .module = zmath.module("root") },
|
||||
},
|
||||
}),
|
||||
.test_runner = .{ .path = b.path("test/test_runner.zig"), .mode = .simple },
|
||||
});
|
||||
const run_tests = b.addRunArtifact(lib_tests);
|
||||
const test_step = b.step("test", "Run Zig unit tests");
|
||||
test_step.dependOn(&run_tests.step);
|
||||
}
|
||||
|
||||
// Docs generation
|
||||
|
||||
const autodoc_test = b.addObject(.{
|
||||
.name = "lib",
|
||||
.root_module = mod,
|
||||
fn addShaderCompileStep(
|
||||
b: *std.Build,
|
||||
step_name: []const u8,
|
||||
description: []const u8,
|
||||
shader_path: []const u8,
|
||||
output_dir: []const u8,
|
||||
) void {
|
||||
const cmd = b.addSystemCommand(&.{
|
||||
"nzslc",
|
||||
shader_path,
|
||||
"--compile=spv,spv-dis",
|
||||
"-o",
|
||||
output_dir,
|
||||
});
|
||||
|
||||
const step = b.step(step_name, description);
|
||||
step.dependOn(&cmd.step);
|
||||
}
|
||||
|
||||
fn addZigTests(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
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 nzsl = b.lazyDependency("NZSL", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}) orelse return;
|
||||
|
||||
const tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("test/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "spv", .module = spv_mod },
|
||||
.{ .name = "nzsl", .module = nzsl.module("nzigsl") },
|
||||
.{ .name = "zmath", .module = zmath.module("root") },
|
||||
},
|
||||
}),
|
||||
.test_runner = .{
|
||||
.path = b.path("test/test_runner.zig"),
|
||||
.mode = .simple,
|
||||
},
|
||||
});
|
||||
|
||||
const run_tests = b.addRunArtifact(tests);
|
||||
|
||||
const test_step = b.step("test", "Run Zig unit tests");
|
||||
test_step.dependOn(&run_tests.step);
|
||||
}
|
||||
|
||||
fn addCffi(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
use_llvm: bool,
|
||||
spv_mod: *std.Build.Module,
|
||||
) void {
|
||||
const static_c_ffi = b.option(bool, "ffi-build-static", "Build C FFI statically") orelse true;
|
||||
|
||||
const c_ffi_mod = b.addModule("c_ffi_spv", .{
|
||||
.root_source_file = b.path("ffi/ffi.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
.imports = &.{
|
||||
.{ .name = "spv", .module = spv_mod },
|
||||
},
|
||||
});
|
||||
|
||||
const install_header = b.addInstallHeaderFile(
|
||||
b.path("ffi/SpirvInterpreter.h"),
|
||||
"SpirvInterpreter.h",
|
||||
);
|
||||
|
||||
const c_ffi_lib = b.addLibrary(.{
|
||||
.name = "spirv_interpreter_c_ffi",
|
||||
.root_module = c_ffi_mod,
|
||||
.linkage = if (static_c_ffi) .static else .dynamic,
|
||||
.use_llvm = use_llvm,
|
||||
});
|
||||
|
||||
const install_lib = b.addInstallArtifact(c_ffi_lib, .{});
|
||||
|
||||
const ffi_step = b.step("ffi-c", "Build C FFI");
|
||||
ffi_step.dependOn(&install_lib.step);
|
||||
ffi_step.dependOn(&install_header.step);
|
||||
|
||||
const c_test = b.addExecutable(.{
|
||||
.name = "c_test",
|
||||
.root_module = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
}),
|
||||
.use_llvm = use_llvm,
|
||||
});
|
||||
|
||||
c_test.root_module.addCSourceFile(.{ .file = b.path("test_c/main.c") });
|
||||
c_test.root_module.linkLibrary(c_ffi_lib);
|
||||
c_test.root_module.addSystemIncludePath(b.path("ffi"));
|
||||
|
||||
const install_c_test = b.addInstallArtifact(c_test, .{});
|
||||
install_c_test.step.dependOn(&install_lib.step);
|
||||
|
||||
const run_c_test = b.addRunArtifact(c_test);
|
||||
run_c_test.step.dependOn(&install_c_test.step);
|
||||
|
||||
const test_c_step = b.step("test-c", "Run C test");
|
||||
test_c_step.dependOn(&run_c_test.step);
|
||||
}
|
||||
|
||||
fn addDocs(b: *std.Build, spv_mod: *std.Build.Module) void {
|
||||
const autodoc_obj = b.addObject(.{
|
||||
.name = "lib",
|
||||
.root_module = spv_mod,
|
||||
});
|
||||
|
||||
const install_docs = b.addInstallDirectory(.{
|
||||
.source_dir = autodoc_test.getEmittedDocs(),
|
||||
.source_dir = autodoc_obj.getEmittedDocs(),
|
||||
.install_dir = .prefix,
|
||||
.install_subdir = "docs",
|
||||
});
|
||||
|
||||
const docs_step = b.step("docs", "Build and install the documentation");
|
||||
const docs_step = b.step("docs", "Build and install documentation");
|
||||
docs_step.dependOn(&install_docs.step);
|
||||
}
|
||||
|
||||
+11
-11
@@ -6,22 +6,22 @@
|
||||
.url = "git+https://github.com/zig-gamedev/zmath.git#3a5955b2b72cd081563fbb084eff05bffd1e3fbb",
|
||||
.hash = "zmath-0.11.0-dev-wjwivdMsAwD-xaLj76YHUq3t9JDH-X16xuMTmnDzqbu2",
|
||||
},
|
||||
.pretty = .{ // For debugging purposes
|
||||
.url = "git+https://github.com/Kbz-8/pretty#117674465efd4d07d5ae9d9d8ca59c2c323a65ba",
|
||||
.hash = "pretty-0.10.6-Tm65r99UAQDEJMgZysD10qE8dinBHr064fPM6YkxVPfB",
|
||||
},
|
||||
.NZSL = .{ // For unit tests
|
||||
.url = "git+https://git.kbz8.me/kbz_8/NZigSL#5377dbdf9935b0de767f76ea4650e7aba4516b32",
|
||||
.hash = "NZSL-1.1.2-N0xSVMt6AAC1ncQHA_RafnclWolDA477iTnFmZgdvxd-",
|
||||
.url = "git+https://git.kbz8.me/kbz_8/NZigSL#ab95fc3734da46079fda2a4cd0f14143d92bf633",
|
||||
.hash = "NZSL-1.1.2-N0xSVCR7AACeI_Wa6JPggJzy9_MPCpWC-2OHkMowwX-7",
|
||||
.lazy = true,
|
||||
},
|
||||
.sdl3 = .{
|
||||
.url = "git+https://codeberg.org/7Games/zig-sdl3?ref=v0.1.6#9c1842246c59f03f87ba59b160ca7e3d5e5ce972",
|
||||
.hash = "sdl3-0.1.6-NmT1Q5sQJgCzT6hLj7WOSrwxE0Qsef1wIkDopbOOFru0",
|
||||
.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",
|
||||
},
|
||||
},
|
||||
.minimum_zig_version = "0.15.2",
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
|
||||
+12
-6
@@ -30,13 +30,17 @@ pub fn main() !void {
|
||||
var runner_cache: std.ArrayList(Runner) = try .initCapacity(allocator, screen_height);
|
||||
defer {
|
||||
for (runner_cache.items) |*runner| {
|
||||
runner.rt.deinit(allocator);
|
||||
allocator.free(runner.heap);
|
||||
}
|
||||
runner_cache.deinit(allocator);
|
||||
}
|
||||
|
||||
for (0..screen_height) |_| {
|
||||
var rt = try spv.Runtime.init(allocator, &module);
|
||||
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)).* = .{
|
||||
.allocator = allocator,
|
||||
.surface = surface,
|
||||
@@ -46,6 +50,7 @@ pub fn main() !void {
|
||||
.time = try rt.getResultByName("time"),
|
||||
.pos = try rt.getResultByName("pos"),
|
||||
.res = try rt.getResultByName("res"),
|
||||
.heap = heap,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -105,6 +110,7 @@ const Runner = struct {
|
||||
time: spv.SpvWord,
|
||||
pos: spv.SpvWord,
|
||||
res: spv.SpvWord,
|
||||
heap: []u8,
|
||||
|
||||
fn runWrapper(self: *Self, y: usize, pixel_map: [*]u32, timer: f32) void {
|
||||
@call(.always_inline, Self.run, .{ self, y, pixel_map, timer }) catch |err| {
|
||||
@@ -122,11 +128,11 @@ const Runner = struct {
|
||||
var output: [4]f32 = undefined;
|
||||
|
||||
for (0..screen_width) |x| {
|
||||
try rt.writeInput(&.{timer}, self.time);
|
||||
try rt.writeInput(&.{ @floatFromInt(screen_width), @floatFromInt(screen_height) }, self.res);
|
||||
try rt.writeInput(&.{ @floatFromInt(x), @floatFromInt(y) }, self.pos);
|
||||
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);
|
||||
try rt.callEntryPoint(self.allocator, self.entry);
|
||||
try rt.readOutput(output[0..], self.color);
|
||||
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)),
|
||||
|
||||
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
Copyright (C) 2026 kbz_8 (contact@kbz8.me)
|
||||
This file is part of the "SPIR-V Interpreter - FFI C Bindings" project
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 kbz_8
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_INTERPRETER_H
|
||||
#define SPIRV_INTERPRETER_H
|
||||
|
||||
#ifndef SPV_API
|
||||
#define SPV_API extern
|
||||
#endif /* SPV_API */
|
||||
|
||||
#ifdef __cplusplus
|
||||
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_
|
||||
{
|
||||
SpvBuiltInPosition = 0,
|
||||
SpvBuiltInPointSize = 1,
|
||||
SpvBuiltInClipDistance = 3,
|
||||
SpvBuiltInCullDistance = 4,
|
||||
SpvBuiltInVertexId = 5,
|
||||
SpvBuiltInInstanceId = 6,
|
||||
SpvBuiltInPrimitiveId = 7,
|
||||
SpvBuiltInInvocationId = 8,
|
||||
SpvBuiltInLayer = 9,
|
||||
SpvBuiltInViewportIndex = 10,
|
||||
SpvBuiltInTessLevelOuter = 11,
|
||||
SpvBuiltInTessLevelInner = 12,
|
||||
SpvBuiltInTessCoord = 13,
|
||||
SpvBuiltInPatchVertices = 14,
|
||||
SpvBuiltInFragCoord = 15,
|
||||
SpvBuiltInPointCoord = 16,
|
||||
SpvBuiltInFrontFacing = 17,
|
||||
SpvBuiltInSampleId = 18,
|
||||
SpvBuiltInSamplePosition = 19,
|
||||
SpvBuiltInSampleMask = 20,
|
||||
SpvBuiltInFragDepth = 22,
|
||||
SpvBuiltInHelperInvocation = 23,
|
||||
SpvBuiltInNumWorkgroups = 24,
|
||||
SpvBuiltInWorkgroupSize = 25,
|
||||
SpvBuiltInWorkgroupId = 26,
|
||||
SpvBuiltInLocalInvocationId = 27,
|
||||
SpvBuiltInGlobalInvocationId = 28,
|
||||
SpvBuiltInLocalInvocationIndex = 29,
|
||||
SpvBuiltInWorkDim = 30,
|
||||
SpvBuiltInGlobalSize = 31,
|
||||
SpvBuiltInEnqueuedWorkgroupSize = 32,
|
||||
SpvBuiltInGlobalOffset = 33,
|
||||
SpvBuiltInGlobalLinearId = 34,
|
||||
SpvBuiltInSubgroupSize = 36,
|
||||
SpvBuiltInSubgroupMaxSize = 37,
|
||||
SpvBuiltInNumSubgroups = 38,
|
||||
SpvBuiltInNumEnqueuedSubgroups = 39,
|
||||
SpvBuiltInSubgroupId = 40,
|
||||
SpvBuiltInSubgroupLocalInvocationId = 41,
|
||||
SpvBuiltInVertexIndex = 42,
|
||||
SpvBuiltInInstanceIndex = 43,
|
||||
SpvBuiltInCoreIDARM = 4160,
|
||||
SpvBuiltInCoreCountARM = 4161,
|
||||
SpvBuiltInCoreMaxIDARM = 4162,
|
||||
SpvBuiltInWarpIDARM = 4163,
|
||||
SpvBuiltInWarpMaxIDARM = 4164,
|
||||
SpvBuiltInSubgroupEqMask = 4416,
|
||||
SpvBuiltInSubgroupEqMaskKHR = 4416,
|
||||
SpvBuiltInSubgroupGeMask = 4417,
|
||||
SpvBuiltInSubgroupGeMaskKHR = 4417,
|
||||
SpvBuiltInSubgroupGtMask = 4418,
|
||||
SpvBuiltInSubgroupGtMaskKHR = 4418,
|
||||
SpvBuiltInSubgroupLeMask = 4419,
|
||||
SpvBuiltInSubgroupLeMaskKHR = 4419,
|
||||
SpvBuiltInSubgroupLtMask = 4420,
|
||||
SpvBuiltInSubgroupLtMaskKHR = 4420,
|
||||
SpvBuiltInBaseVertex = 4424,
|
||||
SpvBuiltInBaseInstance = 4425,
|
||||
SpvBuiltInDrawIndex = 4426,
|
||||
SpvBuiltInPrimitiveShadingRateKHR = 4432,
|
||||
SpvBuiltInDeviceIndex = 4438,
|
||||
SpvBuiltInViewIndex = 4440,
|
||||
SpvBuiltInShadingRateKHR = 4444,
|
||||
SpvBuiltInTileOffsetQCOM = 4492,
|
||||
SpvBuiltInTileDimensionQCOM = 4493,
|
||||
SpvBuiltInTileApronSizeQCOM = 4494,
|
||||
SpvBuiltInBaryCoordNoPerspAMD = 4992,
|
||||
SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993,
|
||||
SpvBuiltInBaryCoordNoPerspSampleAMD = 4994,
|
||||
SpvBuiltInBaryCoordSmoothAMD = 4995,
|
||||
SpvBuiltInBaryCoordSmoothCentroidAMD = 4996,
|
||||
SpvBuiltInBaryCoordSmoothSampleAMD = 4997,
|
||||
SpvBuiltInBaryCoordPullModelAMD = 4998,
|
||||
SpvBuiltInFragStencilRefEXT = 5014,
|
||||
SpvBuiltInRemainingRecursionLevelsAMDX = 5021,
|
||||
SpvBuiltInShaderIndexAMDX = 5073,
|
||||
SpvBuiltInSamplerHeapEXT = 5122,
|
||||
SpvBuiltInResourceHeapEXT = 5123,
|
||||
SpvBuiltInViewportMaskNV = 5253,
|
||||
SpvBuiltInSecondaryPositionNV = 5257,
|
||||
SpvBuiltInSecondaryViewportMaskNV = 5258,
|
||||
SpvBuiltInPositionPerViewNV = 5261,
|
||||
SpvBuiltInViewportMaskPerViewNV = 5262,
|
||||
SpvBuiltInFullyCoveredEXT = 5264,
|
||||
SpvBuiltInTaskCountNV = 5274,
|
||||
SpvBuiltInPrimitiveCountNV = 5275,
|
||||
SpvBuiltInPrimitiveIndicesNV = 5276,
|
||||
SpvBuiltInClipDistancePerViewNV = 5277,
|
||||
SpvBuiltInCullDistancePerViewNV = 5278,
|
||||
SpvBuiltInLayerPerViewNV = 5279,
|
||||
SpvBuiltInMeshViewCountNV = 5280,
|
||||
SpvBuiltInMeshViewIndicesNV = 5281,
|
||||
SpvBuiltInBaryCoordKHR = 5286,
|
||||
SpvBuiltInBaryCoordNV = 5286,
|
||||
SpvBuiltInBaryCoordNoPerspKHR = 5287,
|
||||
SpvBuiltInBaryCoordNoPerspNV = 5287,
|
||||
SpvBuiltInFragSizeEXT = 5292,
|
||||
SpvBuiltInFragmentSizeNV = 5292,
|
||||
SpvBuiltInFragInvocationCountEXT = 5293,
|
||||
SpvBuiltInInvocationsPerPixelNV = 5293,
|
||||
SpvBuiltInPrimitivePointIndicesEXT = 5294,
|
||||
SpvBuiltInPrimitiveLineIndicesEXT = 5295,
|
||||
SpvBuiltInPrimitiveTriangleIndicesEXT = 5296,
|
||||
SpvBuiltInCullPrimitiveEXT = 5299,
|
||||
SpvBuiltInLaunchIdKHR = 5319,
|
||||
SpvBuiltInLaunchIdNV = 5319,
|
||||
SpvBuiltInLaunchSizeKHR = 5320,
|
||||
SpvBuiltInLaunchSizeNV = 5320,
|
||||
SpvBuiltInWorldRayOriginKHR = 5321,
|
||||
SpvBuiltInWorldRayOriginNV = 5321,
|
||||
SpvBuiltInWorldRayDirectionKHR = 5322,
|
||||
SpvBuiltInWorldRayDirectionNV = 5322,
|
||||
SpvBuiltInObjectRayOriginKHR = 5323,
|
||||
SpvBuiltInObjectRayOriginNV = 5323,
|
||||
SpvBuiltInObjectRayDirectionKHR = 5324,
|
||||
SpvBuiltInObjectRayDirectionNV = 5324,
|
||||
SpvBuiltInRayTminKHR = 5325,
|
||||
SpvBuiltInRayTminNV = 5325,
|
||||
SpvBuiltInRayTmaxKHR = 5326,
|
||||
SpvBuiltInRayTmaxNV = 5326,
|
||||
SpvBuiltInInstanceCustomIndexKHR = 5327,
|
||||
SpvBuiltInInstanceCustomIndexNV = 5327,
|
||||
SpvBuiltInObjectToWorldKHR = 5330,
|
||||
SpvBuiltInObjectToWorldNV = 5330,
|
||||
SpvBuiltInWorldToObjectKHR = 5331,
|
||||
SpvBuiltInWorldToObjectNV = 5331,
|
||||
SpvBuiltInHitTNV = 5332,
|
||||
SpvBuiltInHitKindKHR = 5333,
|
||||
SpvBuiltInHitKindNV = 5333,
|
||||
SpvBuiltInCurrentRayTimeNV = 5334,
|
||||
SpvBuiltInHitTriangleVertexPositionsKHR = 5335,
|
||||
SpvBuiltInHitMicroTriangleVertexPositionsNV = 5337,
|
||||
SpvBuiltInHitMicroTriangleVertexBarycentricsNV = 5344,
|
||||
SpvBuiltInIncomingRayFlagsKHR = 5351,
|
||||
SpvBuiltInIncomingRayFlagsNV = 5351,
|
||||
SpvBuiltInRayGeometryIndexKHR = 5352,
|
||||
SpvBuiltInHitIsSphereNV = 5359,
|
||||
SpvBuiltInHitIsLSSNV = 5360,
|
||||
SpvBuiltInHitSpherePositionNV = 5361,
|
||||
SpvBuiltInWarpsPerSMNV = 5374,
|
||||
SpvBuiltInSMCountNV = 5375,
|
||||
SpvBuiltInWarpIDNV = 5376,
|
||||
SpvBuiltInSMIDNV = 5377,
|
||||
SpvBuiltInHitLSSPositionsNV = 5396,
|
||||
SpvBuiltInHitKindFrontFacingMicroTriangleNV = 5405,
|
||||
SpvBuiltInHitKindBackFacingMicroTriangleNV = 5406,
|
||||
SpvBuiltInHitSphereRadiusNV = 5420,
|
||||
SpvBuiltInHitLSSRadiiNV = 5421,
|
||||
SpvBuiltInClusterIDNV = 5436,
|
||||
SpvBuiltInCullMaskKHR = 6021,
|
||||
SpvBuiltInMax = 0x7fffffff,
|
||||
};
|
||||
|
||||
typedef enum SpvDecoration_ {
|
||||
SpvDecorationRelaxedPrecision = 0,
|
||||
SpvDecorationSpecId = 1,
|
||||
SpvDecorationBlock = 2,
|
||||
SpvDecorationBufferBlock = 3,
|
||||
SpvDecorationRowMajor = 4,
|
||||
SpvDecorationColMajor = 5,
|
||||
SpvDecorationArrayStride = 6,
|
||||
SpvDecorationMatrixStride = 7,
|
||||
SpvDecorationGLSLShared = 8,
|
||||
SpvDecorationGLSLPacked = 9,
|
||||
SpvDecorationCPacked = 10,
|
||||
SpvDecorationBuiltIn = 11,
|
||||
SpvDecorationNoPerspective = 13,
|
||||
SpvDecorationFlat = 14,
|
||||
SpvDecorationPatch = 15,
|
||||
SpvDecorationCentroid = 16,
|
||||
SpvDecorationSample = 17,
|
||||
SpvDecorationInvariant = 18,
|
||||
SpvDecorationRestrict = 19,
|
||||
SpvDecorationAliased = 20,
|
||||
SpvDecorationVolatile = 21,
|
||||
SpvDecorationConstant = 22,
|
||||
SpvDecorationCoherent = 23,
|
||||
SpvDecorationNonWritable = 24,
|
||||
SpvDecorationNonReadable = 25,
|
||||
SpvDecorationUniform = 26,
|
||||
SpvDecorationUniformId = 27,
|
||||
SpvDecorationSaturatedConversion = 28,
|
||||
SpvDecorationStream = 29,
|
||||
SpvDecorationLocation = 30,
|
||||
SpvDecorationComponent = 31,
|
||||
SpvDecorationIndex = 32,
|
||||
SpvDecorationBinding = 33,
|
||||
SpvDecorationDescriptorSet = 34,
|
||||
SpvDecorationOffset = 35,
|
||||
SpvDecorationXfbBuffer = 36,
|
||||
SpvDecorationXfbStride = 37,
|
||||
SpvDecorationFuncParamAttr = 38,
|
||||
SpvDecorationFPRoundingMode = 39,
|
||||
SpvDecorationFPFastMathMode = 40,
|
||||
SpvDecorationLinkageAttributes = 41,
|
||||
SpvDecorationNoContraction = 42,
|
||||
SpvDecorationInputAttachmentIndex = 43,
|
||||
SpvDecorationAlignment = 44,
|
||||
SpvDecorationMaxByteOffset = 45,
|
||||
SpvDecorationAlignmentId = 46,
|
||||
SpvDecorationMaxByteOffsetId = 47,
|
||||
SpvDecorationSaturatedToLargestFloat8NormalConversionEXT = 4216,
|
||||
SpvDecorationNoSignedWrap = 4469,
|
||||
SpvDecorationNoUnsignedWrap = 4470,
|
||||
SpvDecorationWeightTextureQCOM = 4487,
|
||||
SpvDecorationBlockMatchTextureQCOM = 4488,
|
||||
SpvDecorationBlockMatchSamplerQCOM = 4499,
|
||||
SpvDecorationExplicitInterpAMD = 4999,
|
||||
SpvDecorationNodeSharesPayloadLimitsWithAMDX = 5019,
|
||||
SpvDecorationNodeMaxPayloadsAMDX = 5020,
|
||||
SpvDecorationTrackFinishWritingAMDX = 5078,
|
||||
SpvDecorationPayloadNodeNameAMDX = 5091,
|
||||
SpvDecorationPayloadNodeBaseIndexAMDX = 5098,
|
||||
SpvDecorationPayloadNodeSparseArrayAMDX = 5099,
|
||||
SpvDecorationPayloadNodeArraySizeAMDX = 5100,
|
||||
SpvDecorationPayloadDispatchIndirectAMDX = 5105,
|
||||
SpvDecorationArrayStrideIdEXT = 5124,
|
||||
SpvDecorationOffsetIdEXT = 5125,
|
||||
SpvDecorationUTFEncodedKHR = 5145,
|
||||
SpvDecorationOverrideCoverageNV = 5248,
|
||||
SpvDecorationPassthroughNV = 5250,
|
||||
SpvDecorationViewportRelativeNV = 5252,
|
||||
SpvDecorationSecondaryViewportRelativeNV = 5256,
|
||||
SpvDecorationPerPrimitiveEXT = 5271,
|
||||
SpvDecorationPerPrimitiveNV = 5271,
|
||||
SpvDecorationPerViewNV = 5272,
|
||||
SpvDecorationPerTaskNV = 5273,
|
||||
SpvDecorationPerVertexKHR = 5285,
|
||||
SpvDecorationPerVertexNV = 5285,
|
||||
SpvDecorationNonUniform = 5300,
|
||||
SpvDecorationNonUniformEXT = 5300,
|
||||
SpvDecorationRestrictPointer = 5355,
|
||||
SpvDecorationRestrictPointerEXT = 5355,
|
||||
SpvDecorationAliasedPointer = 5356,
|
||||
SpvDecorationAliasedPointerEXT = 5356,
|
||||
SpvDecorationMemberOffsetNV = 5358,
|
||||
SpvDecorationHitObjectShaderRecordBufferNV = 5386,
|
||||
SpvDecorationHitObjectShaderRecordBufferEXT = 5389,
|
||||
SpvDecorationBankNV = 5397,
|
||||
SpvDecorationBindlessSamplerNV = 5398,
|
||||
SpvDecorationBindlessImageNV = 5399,
|
||||
SpvDecorationBoundSamplerNV = 5400,
|
||||
SpvDecorationBoundImageNV = 5401,
|
||||
SpvDecorationSIMTCallINTEL = 5599,
|
||||
SpvDecorationReferencedIndirectlyINTEL = 5602,
|
||||
SpvDecorationClobberINTEL = 5607,
|
||||
SpvDecorationSideEffectsINTEL = 5608,
|
||||
SpvDecorationVectorComputeVariableINTEL = 5624,
|
||||
SpvDecorationFuncParamIOKindINTEL = 5625,
|
||||
SpvDecorationVectorComputeFunctionINTEL = 5626,
|
||||
SpvDecorationStackCallINTEL = 5627,
|
||||
SpvDecorationGlobalVariableOffsetINTEL = 5628,
|
||||
SpvDecorationCounterBuffer = 5634,
|
||||
SpvDecorationHlslCounterBufferGOOGLE = 5634,
|
||||
SpvDecorationHlslSemanticGOOGLE = 5635,
|
||||
SpvDecorationUserSemantic = 5635,
|
||||
SpvDecorationUserTypeGOOGLE = 5636,
|
||||
SpvDecorationFunctionRoundingModeINTEL = 5822,
|
||||
SpvDecorationFunctionDenormModeINTEL = 5823,
|
||||
SpvDecorationRegisterALTERA = 5825,
|
||||
SpvDecorationRegisterINTEL = 5825,
|
||||
SpvDecorationMemoryALTERA = 5826,
|
||||
SpvDecorationMemoryINTEL = 5826,
|
||||
SpvDecorationNumbanksALTERA = 5827,
|
||||
SpvDecorationNumbanksINTEL = 5827,
|
||||
SpvDecorationBankwidthALTERA = 5828,
|
||||
SpvDecorationBankwidthINTEL = 5828,
|
||||
SpvDecorationMaxPrivateCopiesALTERA = 5829,
|
||||
SpvDecorationMaxPrivateCopiesINTEL = 5829,
|
||||
SpvDecorationSinglepumpALTERA = 5830,
|
||||
SpvDecorationSinglepumpINTEL = 5830,
|
||||
SpvDecorationDoublepumpALTERA = 5831,
|
||||
SpvDecorationDoublepumpINTEL = 5831,
|
||||
SpvDecorationMaxReplicatesALTERA = 5832,
|
||||
SpvDecorationMaxReplicatesINTEL = 5832,
|
||||
SpvDecorationSimpleDualPortALTERA = 5833,
|
||||
SpvDecorationSimpleDualPortINTEL = 5833,
|
||||
SpvDecorationMergeALTERA = 5834,
|
||||
SpvDecorationMergeINTEL = 5834,
|
||||
SpvDecorationBankBitsALTERA = 5835,
|
||||
SpvDecorationBankBitsINTEL = 5835,
|
||||
SpvDecorationForcePow2DepthALTERA = 5836,
|
||||
SpvDecorationForcePow2DepthINTEL = 5836,
|
||||
SpvDecorationStridesizeALTERA = 5883,
|
||||
SpvDecorationStridesizeINTEL = 5883,
|
||||
SpvDecorationWordsizeALTERA = 5884,
|
||||
SpvDecorationWordsizeINTEL = 5884,
|
||||
SpvDecorationTrueDualPortALTERA = 5885,
|
||||
SpvDecorationTrueDualPortINTEL = 5885,
|
||||
SpvDecorationBurstCoalesceALTERA = 5899,
|
||||
SpvDecorationBurstCoalesceINTEL = 5899,
|
||||
SpvDecorationCacheSizeALTERA = 5900,
|
||||
SpvDecorationCacheSizeINTEL = 5900,
|
||||
SpvDecorationDontStaticallyCoalesceALTERA = 5901,
|
||||
SpvDecorationDontStaticallyCoalesceINTEL = 5901,
|
||||
SpvDecorationPrefetchALTERA = 5902,
|
||||
SpvDecorationPrefetchINTEL = 5902,
|
||||
SpvDecorationStallEnableALTERA = 5905,
|
||||
SpvDecorationStallEnableINTEL = 5905,
|
||||
SpvDecorationFuseLoopsInFunctionALTERA = 5907,
|
||||
SpvDecorationFuseLoopsInFunctionINTEL = 5907,
|
||||
SpvDecorationMathOpDSPModeALTERA = 5909,
|
||||
SpvDecorationMathOpDSPModeINTEL = 5909,
|
||||
SpvDecorationAliasScopeINTEL = 5914,
|
||||
SpvDecorationNoAliasINTEL = 5915,
|
||||
SpvDecorationInitiationIntervalALTERA = 5917,
|
||||
SpvDecorationInitiationIntervalINTEL = 5917,
|
||||
SpvDecorationMaxConcurrencyALTERA = 5918,
|
||||
SpvDecorationMaxConcurrencyINTEL = 5918,
|
||||
SpvDecorationPipelineEnableALTERA = 5919,
|
||||
SpvDecorationPipelineEnableINTEL = 5919,
|
||||
SpvDecorationBufferLocationALTERA = 5921,
|
||||
SpvDecorationBufferLocationINTEL = 5921,
|
||||
SpvDecorationIOPipeStorageALTERA = 5944,
|
||||
SpvDecorationIOPipeStorageINTEL = 5944,
|
||||
SpvDecorationFunctionFloatingPointModeINTEL = 6080,
|
||||
SpvDecorationSingleElementVectorINTEL = 6085,
|
||||
SpvDecorationVectorComputeCallableFunctionINTEL = 6087,
|
||||
SpvDecorationMediaBlockIOINTEL = 6140,
|
||||
SpvDecorationStallFreeALTERA = 6151,
|
||||
SpvDecorationStallFreeINTEL = 6151,
|
||||
SpvDecorationFPMaxErrorDecorationINTEL = 6170,
|
||||
SpvDecorationLatencyControlLabelALTERA = 6172,
|
||||
SpvDecorationLatencyControlLabelINTEL = 6172,
|
||||
SpvDecorationLatencyControlConstraintALTERA = 6173,
|
||||
SpvDecorationLatencyControlConstraintINTEL = 6173,
|
||||
SpvDecorationConduitKernelArgumentALTERA = 6175,
|
||||
SpvDecorationConduitKernelArgumentINTEL = 6175,
|
||||
SpvDecorationRegisterMapKernelArgumentALTERA = 6176,
|
||||
SpvDecorationRegisterMapKernelArgumentINTEL = 6176,
|
||||
SpvDecorationMMHostInterfaceAddressWidthALTERA = 6177,
|
||||
SpvDecorationMMHostInterfaceAddressWidthINTEL = 6177,
|
||||
SpvDecorationMMHostInterfaceDataWidthALTERA = 6178,
|
||||
SpvDecorationMMHostInterfaceDataWidthINTEL = 6178,
|
||||
SpvDecorationMMHostInterfaceLatencyALTERA = 6179,
|
||||
SpvDecorationMMHostInterfaceLatencyINTEL = 6179,
|
||||
SpvDecorationMMHostInterfaceReadWriteModeALTERA = 6180,
|
||||
SpvDecorationMMHostInterfaceReadWriteModeINTEL = 6180,
|
||||
SpvDecorationMMHostInterfaceMaxBurstALTERA = 6181,
|
||||
SpvDecorationMMHostInterfaceMaxBurstINTEL = 6181,
|
||||
SpvDecorationMMHostInterfaceWaitRequestALTERA = 6182,
|
||||
SpvDecorationMMHostInterfaceWaitRequestINTEL = 6182,
|
||||
SpvDecorationStableKernelArgumentALTERA = 6183,
|
||||
SpvDecorationStableKernelArgumentINTEL = 6183,
|
||||
SpvDecorationHostAccessINTEL = 6188,
|
||||
SpvDecorationInitModeALTERA = 6190,
|
||||
SpvDecorationInitModeINTEL = 6190,
|
||||
SpvDecorationImplementInRegisterMapALTERA = 6191,
|
||||
SpvDecorationImplementInRegisterMapINTEL = 6191,
|
||||
SpvDecorationConditionalINTEL = 6247,
|
||||
SpvDecorationCacheControlLoadINTEL = 6442,
|
||||
SpvDecorationCacheControlStoreINTEL = 6443,
|
||||
SpvDecorationMax = 0x7fffffff,
|
||||
} SpvDecoration;
|
||||
#endif /* spirv_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SPIRV_INTERPRETER_H */
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
pub const spv = @import("spv");
|
||||
|
||||
pub const SpvCBool = c_int;
|
||||
pub const SpvCWord = c_ulong;
|
||||
pub const SpvCSize = c_ulong;
|
||||
|
||||
pub const Result = enum(c_int) {
|
||||
Success = 0,
|
||||
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,
|
||||
};
|
||||
|
||||
comptime {
|
||||
_ = @import("module.zig");
|
||||
_ = @import("runtime.zig");
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
const std = @import("std");
|
||||
const ffi = @import("ffi.zig");
|
||||
const spv = ffi.spv;
|
||||
|
||||
const Options = extern struct {
|
||||
use_simd_vectors_specializations: ffi.SpvCBool,
|
||||
};
|
||||
|
||||
fn toCResult(err: spv.Module.ModuleError) ffi.Result {
|
||||
return switch (err) {
|
||||
spv.Module.ModuleError.InvalidSpirV => ffi.Result.InvalidSpirV,
|
||||
spv.Module.ModuleError.InvalidMagic => ffi.Result.InvalidMagic,
|
||||
spv.Module.ModuleError.UnsupportedEndianness => ffi.Result.UnsupportedEndianness,
|
||||
spv.Module.ModuleError.UnsupportedExtension => ffi.Result.UnsupportedExtension,
|
||||
spv.Module.ModuleError.OutOfMemory => ffi.Result.OutOfMemory,
|
||||
};
|
||||
}
|
||||
|
||||
export fn SpvInitModule(module: **spv.Module, source: [*]const ffi.SpvCWord, source_len: ffi.SpvCSize, options: Options) callconv(.c) ffi.Result {
|
||||
const allocator = std.heap.c_allocator;
|
||||
|
||||
module.* = allocator.create(spv.Module) catch return .OutOfMemory;
|
||||
|
||||
const cast_source: []const u32 = @as([*]const u32, @ptrCast(source[0..source_len]))[0..source_len];
|
||||
module.*.* = spv.Module.init(
|
||||
allocator,
|
||||
cast_source[0..source_len],
|
||||
.{
|
||||
.use_simd_vectors_specializations = if (options.use_simd_vectors_specializations == 0) false else true,
|
||||
},
|
||||
) catch |err| {
|
||||
allocator.destroy(module.*);
|
||||
return toCResult(err);
|
||||
};
|
||||
return .Success;
|
||||
}
|
||||
|
||||
export fn SpvDeinitModule(module: *spv.Module) callconv(.c) void {
|
||||
const allocator = std.heap.c_allocator;
|
||||
module.deinit(allocator);
|
||||
allocator.destroy(module);
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
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, 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 ImageAPI = extern struct {
|
||||
readImageFloat4: readImageFloat4_PFN,
|
||||
readImageInt4: readImageInt4_PFN,
|
||||
writeImageFloat4: writeImageFloat4_PFN,
|
||||
writeImageInt4: writeImageInt4_PFN,
|
||||
};
|
||||
|
||||
fn toCResult(err: spv.Runtime.RuntimeError) ffi.Result {
|
||||
return switch (err) {
|
||||
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.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,
|
||||
};
|
||||
}
|
||||
|
||||
fn fromCResult(res: ffi.Result) spv.Runtime.RuntimeError!void {
|
||||
return switch (res) {
|
||||
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.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,
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
const ImageAPIBridge = struct {
|
||||
threadlocal var current_image_api: ?*const ImageAPI = null; // Hacky
|
||||
|
||||
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) {
|
||||
const image_api = try getImageAPI();
|
||||
|
||||
var dst: Vec4f = undefined;
|
||||
const result = image_api.readImageFloat4(driver_image, @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, 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);
|
||||
|
||||
try fromCResult(result);
|
||||
|
||||
return .{
|
||||
.x = dst.x,
|
||||
.y = dst.y,
|
||||
.z = dst.z,
|
||||
.w = dst.w,
|
||||
};
|
||||
}
|
||||
|
||||
fn writeImageFloat4(driver_image: *anyopaque, 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 });
|
||||
|
||||
try fromCResult(result);
|
||||
}
|
||||
|
||||
fn writeImageInt4(driver_image: *anyopaque, 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 });
|
||||
|
||||
try fromCResult(result);
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
) 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;
|
||||
}
|
||||
+3
-2
@@ -26,6 +26,8 @@ pub fn main() !void {
|
||||
|
||||
var ssbo: SSBO = .{};
|
||||
|
||||
try rt.writeDescriptorSet(std.mem.asBytes(&ssbo), 0, 0, 0);
|
||||
|
||||
for (0..16) |i| {
|
||||
for (0..16) |x| {
|
||||
const global_invocation_indices = [3]i32{
|
||||
@@ -35,12 +37,11 @@ pub fn main() !void {
|
||||
};
|
||||
|
||||
try rt.writeBuiltIn(std.mem.asBytes(&global_invocation_indices), .GlobalInvocationId);
|
||||
try rt.writeDescriptorSet(allocator, std.mem.asBytes(&ssbo), 0, 0);
|
||||
rt.callEntryPoint(allocator, entry) catch |err| switch (err) {
|
||||
spv.Runtime.RuntimeError.OutOfBounds => continue,
|
||||
else => return err,
|
||||
};
|
||||
try rt.readDescriptorSet(std.mem.asBytes(&ssbo), 0, 0);
|
||||
try rt.flushDescriptorSets(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+161
-44
@@ -8,18 +8,18 @@ const Module = @import("../Module.zig");
|
||||
const Runtime = @import("../Runtime.zig");
|
||||
const Result = @import("../Result.zig");
|
||||
const WordIterator = @import("../WordIterator.zig");
|
||||
const value_ns = @import("../Value.zig");
|
||||
|
||||
const RuntimeError = Runtime.RuntimeError;
|
||||
const ValueType = opc.ValueType;
|
||||
|
||||
const getValuePrimitiveField = opc.getValuePrimitiveField;
|
||||
const getValuePrimitiveFieldType = opc.getValuePrimitiveFieldType;
|
||||
|
||||
const SpvVoid = spv.SpvVoid;
|
||||
const SpvByte = spv.SpvByte;
|
||||
const SpvWord = spv.SpvWord;
|
||||
const SpvBool = spv.SpvBool;
|
||||
|
||||
const Value = value_ns.Value;
|
||||
const PrimitiveType = value_ns.PrimitiveType;
|
||||
|
||||
const MathOp = enum {
|
||||
Acos,
|
||||
Acosh,
|
||||
@@ -66,6 +66,12 @@ const MathOp = enum {
|
||||
UMin,
|
||||
};
|
||||
|
||||
const IntBitOp = enum {
|
||||
FindILsb,
|
||||
FindSMsb,
|
||||
FindUMsb,
|
||||
};
|
||||
|
||||
pub const OpCodeExtFunc = opc.OpCodeExtFunc;
|
||||
|
||||
/// Not an EnumMap as it is way too slow for this purpose
|
||||
@@ -79,6 +85,7 @@ pub fn initRuntimeDispatcher() void {
|
||||
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.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;
|
||||
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Length)] = opLength;
|
||||
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Log)] = MathEngine(.Float, .Log).opSingleOperator;
|
||||
@@ -86,14 +93,26 @@ 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.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.FindILsb)] = IntBitEngine(.FindILsb).op;
|
||||
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindSMsb)] = IntBitEngine(.FindSMsb).op;
|
||||
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindUMsb)] = IntBitEngine(.FindUMsb).op;
|
||||
// zig fmt: on
|
||||
}
|
||||
|
||||
fn MathEngine(comptime T: ValueType, comptime Op: MathOp) type {
|
||||
fn isFloatOrF32Vector(comptime T: type) bool {
|
||||
return switch (@typeInfo(T)) {
|
||||
.float => true,
|
||||
.vector => |vec| vec.child == f32,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp) type {
|
||||
return struct {
|
||||
fn opSingleOperator(_: 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;
|
||||
@@ -104,33 +123,41 @@ fn MathEngine(comptime T: ValueType, comptime Op: MathOp) type {
|
||||
|
||||
const operator = struct {
|
||||
fn operation(comptime TT: type, x: TT) RuntimeError!TT {
|
||||
return switch (Op) {
|
||||
.Ceil => @ceil(x),
|
||||
.Cos => @cos(x),
|
||||
.Exp => @exp(x),
|
||||
.Exp2 => @exp2(x),
|
||||
.FAbs => @abs(x),
|
||||
.Floor => @floor(x),
|
||||
.Log => @log(x),
|
||||
.Log2 => @log2(x),
|
||||
.Round => @round(x),
|
||||
.SAbs => if (comptime @typeInfo(TT) == .int) @intCast(@abs(x)) else return RuntimeError.InvalidSpirV,
|
||||
.Sin => @sin(x),
|
||||
.Sqrt => @sqrt(x),
|
||||
.Tan => @tan(x),
|
||||
.Trunc => @trunc(x),
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
if (comptime isFloatOrF32Vector(TT)) {
|
||||
return switch (Op) {
|
||||
.Ceil => @ceil(x),
|
||||
.Cos => @cos(x),
|
||||
.Exp => @exp(x),
|
||||
.Exp2 => @exp2(x),
|
||||
.FAbs => @abs(x),
|
||||
.FSign => std.math.sign(x),
|
||||
.Floor => @floor(x),
|
||||
.Log => @log(x),
|
||||
.Log2 => @log2(x),
|
||||
.Round => @round(x),
|
||||
.Sin => @sin(x),
|
||||
.Sqrt => @sqrt(x),
|
||||
.Tan => @tan(x),
|
||||
.Trunc => @trunc(x),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
} else {
|
||||
return switch (Op) {
|
||||
.SAbs => @intCast(@abs(x)),
|
||||
.SSign => std.math.sign(x),
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn applyScalar(bit_count: SpvWord, d: *Result.Value, s: *const Result.Value) RuntimeError!void {
|
||||
fn applyScalar(bit_count: SpvWord, d: *Value, s: *const Value) RuntimeError!void {
|
||||
switch (bit_count) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
if (bits == 8 and T == .Float) return RuntimeError.InvalidSpirV;
|
||||
|
||||
const ScalarT = getValuePrimitiveFieldType(T, bits);
|
||||
const d_field = try getValuePrimitiveField(T, bits, d);
|
||||
const s_field = try getValuePrimitiveField(T, bits, @constCast(s));
|
||||
const ScalarT = Value.getPrimitiveFieldType(T, bits);
|
||||
const d_field = try Value.getPrimitiveField(T, bits, d);
|
||||
const s_field = try Value.getPrimitiveField(T, bits, @constCast(s));
|
||||
d_field.* = try operation(ScalarT, s_field.*);
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
@@ -149,13 +176,13 @@ fn MathEngine(comptime T: ValueType, comptime Op: MathOp) type {
|
||||
.Vector3f32 => |*d| d.* = try operator.operation(@Vector(3, f32), src.Vector3f32),
|
||||
.Vector2f32 => |*d| d.* = try operator.operation(@Vector(2, f32), src.Vector2f32),
|
||||
|
||||
//.Vector4i32 => |*d| d.* = try operator.operation(@Vector(4, i32), src.Vector4i32),
|
||||
//.Vector3i32 => |*d| d.* = try operator.operation(@Vector(3, i32), src.Vector3i32),
|
||||
//.Vector2i32 => |*d| d.* = try operator.operation(@Vector(2, i32), src.Vector2i32),
|
||||
.Vector4i32 => |*d| d.* = try operator.operation(@Vector(4, i32), src.Vector4i32),
|
||||
.Vector3i32 => |*d| d.* = try operator.operation(@Vector(3, i32), src.Vector3i32),
|
||||
.Vector2i32 => |*d| d.* = try operator.operation(@Vector(2, i32), src.Vector2i32),
|
||||
|
||||
//.Vector4u32 => |*d| d.* = try operator.operation(@Vector(4, u32), src.Vector4u32),
|
||||
//.Vector3u32 => |*d| d.* = try operator.operation(@Vector(3, u32), src.Vector3u32),
|
||||
//.Vector2u32 => |*d| d.* = try operator.operation(@Vector(2, u32), src.Vector2u32),
|
||||
.Vector4u32 => |*d| d.* = try operator.operation(@Vector(4, u32), src.Vector4u32),
|
||||
.Vector3u32 => |*d| d.* = try operator.operation(@Vector(3, u32), src.Vector3u32),
|
||||
.Vector2u32 => |*d| d.* = try operator.operation(@Vector(2, u32), src.Vector2u32),
|
||||
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
}
|
||||
@@ -177,15 +204,15 @@ fn MathEngine(comptime T: ValueType, comptime Op: MathOp) type {
|
||||
};
|
||||
}
|
||||
|
||||
fn applyScalar(bit_count: SpvWord, d: *Result.Value, l: *const Result.Value, r: *const Result.Value) RuntimeError!void {
|
||||
fn applyScalar(bit_count: SpvWord, d: *Value, l: *const Value, r: *const Value) RuntimeError!void {
|
||||
switch (bit_count) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
if (bits == 8 and T == .Float) return RuntimeError.InvalidSpirV;
|
||||
|
||||
const ScalarT = getValuePrimitiveFieldType(T, bits);
|
||||
const d_field = try getValuePrimitiveField(T, bits, d);
|
||||
const l_field = try getValuePrimitiveField(T, bits, @constCast(l));
|
||||
const r_field = try getValuePrimitiveField(T, bits, @constCast(r));
|
||||
const ScalarT = Value.getPrimitiveFieldType(T, bits);
|
||||
const d_field = try Value.getPrimitiveField(T, bits, d);
|
||||
const l_field = try Value.getPrimitiveField(T, bits, @constCast(l));
|
||||
const r_field = try Value.getPrimitiveField(T, bits, @constCast(r));
|
||||
d_field.* = try operation(ScalarT, l_field.*, r_field.*);
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
@@ -218,6 +245,96 @@ fn MathEngine(comptime T: ValueType, comptime Op: MathOp) type {
|
||||
};
|
||||
}
|
||||
|
||||
fn IntBitEngine(comptime op_kind: IntBitOp) type {
|
||||
return struct {
|
||||
inline fn findILsb32(x: u32) i32 {
|
||||
if (x == 0) return -1;
|
||||
return @intCast(@ctz(x));
|
||||
}
|
||||
|
||||
inline fn findUMsb32(x: u32) i32 {
|
||||
if (x == 0) return -1;
|
||||
return 31 - @as(i32, @intCast(@clz(x)));
|
||||
}
|
||||
|
||||
inline fn findSMsb32(x: i32) i32 {
|
||||
if (x == 0 or x == -1) return -1;
|
||||
|
||||
if (x > 0) {
|
||||
return findUMsb32(@bitCast(x));
|
||||
}
|
||||
|
||||
return findUMsb32(@bitCast(~x));
|
||||
}
|
||||
|
||||
inline fn computeSigned(x: i32) i32 {
|
||||
return switch (op_kind) {
|
||||
.FindILsb => findILsb32(@bitCast(x)),
|
||||
.FindSMsb => findSMsb32(x),
|
||||
.FindUMsb => findUMsb32(@bitCast(x)),
|
||||
};
|
||||
}
|
||||
|
||||
inline fn computeUnsigned(x: u32) u32 {
|
||||
const result: i32 = switch (op_kind) {
|
||||
.FindILsb => findILsb32(x),
|
||||
.FindSMsb => findSMsb32(@bitCast(x)),
|
||||
.FindUMsb => findUMsb32(x),
|
||||
};
|
||||
return @bitCast(result);
|
||||
}
|
||||
|
||||
fn readSourceLane(src: *const Value, lane_index: usize) RuntimeError!u32 {
|
||||
return switch (op_kind) {
|
||||
.FindSMsb => @bitCast(try Value.readLane(.SInt, 32, src, lane_index)),
|
||||
.FindILsb, .FindUMsb => try Value.readLane(.UInt, 32, src, lane_index),
|
||||
};
|
||||
}
|
||||
|
||||
fn writeDestLane(dst: *Value, lane_index: usize, bits: u32, dst_is_signed: bool) RuntimeError!void {
|
||||
if (dst_is_signed) {
|
||||
try Value.writeLane(.SInt, 32, dst, lane_index, @as(i32, @bitCast(bits)));
|
||||
} else {
|
||||
try Value.writeLane(.UInt, 32, dst, lane_index, bits);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply(dst: *Value, src: *const Value, lane_count: usize, dst_is_signed: bool) RuntimeError!void {
|
||||
for (0..lane_count) |lane_index| {
|
||||
const src_bits = try readSourceLane(src, lane_index);
|
||||
|
||||
const out_bits: u32 = if (dst_is_signed)
|
||||
@bitCast(computeSigned(@bitCast(src_bits)))
|
||||
else
|
||||
computeUnsigned(src_bits);
|
||||
|
||||
try writeDestLane(dst, lane_index, out_bits, dst_is_signed);
|
||||
}
|
||||
}
|
||||
|
||||
fn op(
|
||||
_: 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 src = try rt.results[try rt.it.next()].getValue();
|
||||
|
||||
const lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
||||
if (lane_bits != 32)
|
||||
return RuntimeError.InvalidSpirV;
|
||||
|
||||
const lane_count = try Result.resolveLaneCount(target_type);
|
||||
const dst_sign = try Result.resolveSign(target_type, rt);
|
||||
|
||||
try apply(dst, src, lane_count, dst_sign == .signed);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn opLength(_: 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();
|
||||
@@ -228,7 +345,7 @@ fn opLength(_: std.mem.Allocator, target_type_id: SpvWord, id: SpvWord, _: SpvWo
|
||||
switch (lane_bits) {
|
||||
inline 16, 32, 64 => |bits| {
|
||||
var sum: std.meta.Float(bits) = 0.0;
|
||||
const d_field = try getValuePrimitiveField(.Float, bits, dst);
|
||||
const d_field = try Value.getPrimitiveField(.Float, bits, dst);
|
||||
|
||||
if (bits == 32) { // More likely to be SIMD if f32
|
||||
switch (src.*) {
|
||||
@@ -251,12 +368,12 @@ fn opLength(_: std.mem.Allocator, target_type_id: SpvWord, id: SpvWord, _: SpvWo
|
||||
switch (src.*) {
|
||||
.Float => {
|
||||
// Fast path
|
||||
const s_field = try getValuePrimitiveField(.Float, bits, src);
|
||||
const s_field = try Value.getPrimitiveField(.Float, bits, src);
|
||||
d_field.* = s_field.*;
|
||||
return;
|
||||
},
|
||||
.Vector => |src_vec| for (src_vec) |*s_lane| {
|
||||
const s_field = try getValuePrimitiveField(.Float, bits, s_lane);
|
||||
const s_field = try Value.getPrimitiveField(.Float, bits, s_lane);
|
||||
sum += s_field.*;
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
@@ -304,11 +421,11 @@ fn opNormalize(_: std.mem.Allocator, target_type_id: SpvWord, id: SpvWord, _: Sp
|
||||
|
||||
switch (src.*) {
|
||||
.Float => {
|
||||
const s_field = try getValuePrimitiveField(.Float, bits, src);
|
||||
const s_field = try Value.getPrimitiveField(.Float, bits, src);
|
||||
sum = s_field.*;
|
||||
},
|
||||
.Vector => |src_vec| for (src_vec) |*s_lane| {
|
||||
const s_field = try getValuePrimitiveField(.Float, bits, s_lane);
|
||||
const s_field = try Value.getPrimitiveField(.Float, bits, s_lane);
|
||||
sum += s_field.*;
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
@@ -318,8 +435,8 @@ fn opNormalize(_: std.mem.Allocator, target_type_id: SpvWord, id: SpvWord, _: Sp
|
||||
|
||||
switch (dst.*) {
|
||||
.Vector => |dst_vec| for (dst_vec, src.Vector) |*d_lane, *s_lane| {
|
||||
const d_field = try getValuePrimitiveField(.Float, bits, d_lane);
|
||||
const s_field = try getValuePrimitiveField(.Float, bits, s_lane);
|
||||
const d_field = try Value.getPrimitiveField(.Float, bits, d_lane);
|
||||
const s_field = try Value.getPrimitiveField(.Float, bits, s_lane);
|
||||
d_field.* = s_field.* / sum;
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
|
||||
+137
-72
@@ -13,10 +13,9 @@ const SpvBinding = spv.SpvBinding;
|
||||
|
||||
const Result = @import("Result.zig");
|
||||
const Runtime = @import("Runtime.zig");
|
||||
const Value = @import("Value.zig").Value;
|
||||
const WordIterator = @import("WordIterator.zig");
|
||||
|
||||
const Value = Result.Value;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const ModuleOptions = struct {
|
||||
@@ -126,53 +125,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.populateMaps();
|
||||
|
||||
if (std.process.hasEnvVarConstant("SPIRV_INTERPRETER_DEBUG_LOGS")) {
|
||||
var capability_set_names: std.ArrayList([]const u8) = .empty;
|
||||
defer capability_set_names.deinit(allocator);
|
||||
|
||||
var it = self.capabilities.iterator();
|
||||
while (it.next()) |cap| {
|
||||
capability_set_names.append(allocator, @tagName(cap)) catch return ModuleError.OutOfMemory;
|
||||
}
|
||||
|
||||
const capabilities = std.mem.join(allocator, ", ", capability_set_names.items) catch return ModuleError.OutOfMemory;
|
||||
defer allocator.free(capabilities);
|
||||
|
||||
var entry_points_names = std.ArrayList([]const u8).initCapacity(allocator, self.entry_points.items.len) catch return ModuleError.OutOfMemory;
|
||||
defer entry_points_names.deinit(allocator);
|
||||
|
||||
for (self.entry_points.items) |entry_point| {
|
||||
entry_points_names.appendAssumeCapacity(entry_point.name);
|
||||
}
|
||||
|
||||
const entry_points = std.mem.join(allocator, ", ", entry_points_names.items) catch return ModuleError.OutOfMemory;
|
||||
defer allocator.free(entry_points);
|
||||
|
||||
std.log.scoped(.SPIRV_Interpreter).debug(
|
||||
\\Loaded shader module with infos:
|
||||
\\ SPIR-V version: {d}.{d}
|
||||
\\ Generator: {s} (ID {d}), encoded version 0x{X}
|
||||
\\ Capabilities: [{s}]
|
||||
\\ Entry points: [{s}]
|
||||
, .{
|
||||
self.version_major,
|
||||
self.version_minor,
|
||||
spv.vendorName(self.generator_id),
|
||||
self.generator_id,
|
||||
self.generator_version,
|
||||
capabilities,
|
||||
entry_points,
|
||||
});
|
||||
}
|
||||
|
||||
//@import("pretty").print(allocator, self.results, .{
|
||||
// .tab_size = 4,
|
||||
// .max_depth = 0,
|
||||
// .struct_max_len = 0,
|
||||
// .array_max_len = 0,
|
||||
//}) catch return ModuleError.OutOfMemory;
|
||||
try self.applyDecorations();
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -189,8 +142,14 @@ fn checkEndiannessFromSpvMagic(magic: SpvWord) bool {
|
||||
}
|
||||
|
||||
fn pass(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
|
||||
var rt = Runtime.init(allocator, self) catch return ModuleError.OutOfMemory;
|
||||
defer rt.deinit(allocator);
|
||||
var rt = Runtime.init(allocator, self, undefined) catch return ModuleError.OutOfMemory;
|
||||
defer {
|
||||
for (self.results, rt.results) |*result, new_result| {
|
||||
result.deinit(allocator);
|
||||
result.* = new_result.dupe(allocator) catch unreachable; // OutOfMemory if unreachable
|
||||
}
|
||||
rt.deinit(allocator);
|
||||
}
|
||||
|
||||
while (rt.it.nextOrNull()) |opcode_data| {
|
||||
const word_count = ((opcode_data & (~spv.SpvOpCodeMask)) >> spv.SpvWordCountShift) - 1;
|
||||
@@ -207,43 +166,149 @@ fn pass(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
|
||||
}
|
||||
}
|
||||
|
||||
fn populateMaps(self: *Self) ModuleError!void {
|
||||
fn resolveConstantWord(self: *const Self, id: SpvWord) ?SpvWord {
|
||||
if (id >= self.results.len) return null;
|
||||
|
||||
const variant = self.results[id].variant orelse return null;
|
||||
return switch (variant) {
|
||||
.Constant => |c| switch (c.value) {
|
||||
.Int => |i| i.value.uint32,
|
||||
else => null,
|
||||
},
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
fn findAccessChainToMember(self: *const Self, base_id: SpvWord, member_index: SpvWord) ?SpvWord {
|
||||
for (self.results, 0..) |result, id| {
|
||||
if (result.variant == null or std.meta.activeTag(result.variant.?) != .Variable)
|
||||
const variant = result.variant orelse continue;
|
||||
|
||||
switch (variant) {
|
||||
.AccessChain => |a| {
|
||||
if (a.base != base_id or a.indexes.len == 0) continue;
|
||||
|
||||
const first_index = self.resolveConstantWord(a.indexes[0]) orelse continue;
|
||||
if (first_index == member_index) return @intCast(id);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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(
|
||||
std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV,
|
||||
id,
|
||||
),
|
||||
.Location => self.input_locations[decoration.literal_1] = id,
|
||||
else => {},
|
||||
},
|
||||
.Output => switch (decoration.rtype) {
|
||||
.BuiltIn => self.builtins.put(
|
||||
std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV,
|
||||
id,
|
||||
),
|
||||
.Location => self.output_locations[decoration.literal_1] = id,
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn applyStructMemberInterfaceDecorations(
|
||||
self: *Self,
|
||||
storage_class: spv.SpvStorageClass,
|
||||
type_word: SpvWord,
|
||||
id: SpvWord,
|
||||
) ModuleError!void {
|
||||
switch (storage_class) {
|
||||
.Input, .Output => {},
|
||||
else => return,
|
||||
}
|
||||
|
||||
const type_result = &self.results[type_word];
|
||||
const target_type_word = if (type_result.variant) |variant| switch (variant) {
|
||||
.Type => |t| switch (t) {
|
||||
.Pointer => |ptr| ptr.target,
|
||||
else => type_word,
|
||||
},
|
||||
else => type_word,
|
||||
} else type_word;
|
||||
|
||||
const target_result = &self.results[target_type_word];
|
||||
if (target_result.variant) |variant| {
|
||||
switch (variant) {
|
||||
.Type => |t| switch (t) {
|
||||
.Structure => {
|
||||
for (target_result.decorations.items) |decoration| {
|
||||
switch (decoration.rtype) {
|
||||
.BuiltIn, .Location => {
|
||||
const member_id = self.findAccessChainToMember(id, decoration.index) orelse continue;
|
||||
try self.applyInterfaceDecoration(storage_class, decoration, member_id);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn applyDecorations(self: *Self) ModuleError!void {
|
||||
for (self.results, 0..) |result, id| {
|
||||
if (result.variant == null)
|
||||
continue;
|
||||
|
||||
var set: ?usize = null;
|
||||
var binding: ?usize = null;
|
||||
|
||||
for (result.decorations.items) |decoration| {
|
||||
switch (result.variant.?.Variable.storage_class) {
|
||||
.Input => {
|
||||
switch (decoration.rtype) {
|
||||
.BuiltIn => self.builtins.put(
|
||||
std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV,
|
||||
@intCast(id),
|
||||
),
|
||||
.Location => self.input_locations[decoration.literal_1] = @intCast(id),
|
||||
switch (result.variant.?) {
|
||||
.Variable => |v| {
|
||||
try self.applyInterfaceDecoration(v.storage_class, decoration, @intCast(id));
|
||||
|
||||
switch (v.storage_class) {
|
||||
.StorageBuffer, .Uniform, .UniformConstant => {
|
||||
switch (decoration.rtype) {
|
||||
.Binding => binding = decoration.literal_1,
|
||||
.DescriptorSet => set = decoration.literal_1,
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
.Output => {
|
||||
if (decoration.rtype == .Location)
|
||||
self.output_locations[decoration.literal_1] = @intCast(id);
|
||||
},
|
||||
.StorageBuffer,
|
||||
.Uniform,
|
||||
.UniformConstant,
|
||||
=> {
|
||||
switch (decoration.rtype) {
|
||||
.Binding => binding = decoration.literal_1,
|
||||
.DescriptorSet => set = decoration.literal_1,
|
||||
.Type => |t| {
|
||||
switch (t) {
|
||||
.Structure => |*s| {
|
||||
if (decoration.rtype == .Offset) {
|
||||
s.members_offsets[decoration.index] = decoration.literal_1;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
switch (result.variant.?) {
|
||||
.Variable => |v| try self.applyStructMemberInterfaceDecorations(v.storage_class, v.type_word, @intCast(id)),
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (set != null and binding != null) {
|
||||
self.bindings[set.?][binding.?] = @intCast(id);
|
||||
}
|
||||
|
||||
+78
-276
@@ -1,6 +1,8 @@
|
||||
const std = @import("std");
|
||||
const spv = @import("spv.zig");
|
||||
const op = @import("opcodes.zig");
|
||||
const lib = @import("lib.zig");
|
||||
const Value = @import("Value.zig").Value;
|
||||
|
||||
const Runtime = @import("Runtime.zig");
|
||||
const RuntimeError = Runtime.RuntimeError;
|
||||
@@ -10,17 +12,17 @@ const SpvByte = spv.SpvByte;
|
||||
const SpvWord = spv.SpvWord;
|
||||
const SpvBool = spv.SpvBool;
|
||||
|
||||
pub const Vec4f32 = @Vector(4, f32);
|
||||
pub const Vec3f32 = @Vector(3, f32);
|
||||
pub const Vec2f32 = @Vector(2, f32);
|
||||
const Vec4f32 = lib.Vec4f32;
|
||||
const Vec3f32 = lib.Vec3f32;
|
||||
const Vec2f32 = lib.Vec2f32;
|
||||
|
||||
pub const Vec4i32 = @Vector(4, i32);
|
||||
pub const Vec3i32 = @Vector(3, i32);
|
||||
pub const Vec2i32 = @Vector(2, i32);
|
||||
const Vec4i32 = lib.Vec4i32;
|
||||
const Vec3i32 = lib.Vec3i32;
|
||||
const Vec2i32 = lib.Vec2i32;
|
||||
|
||||
pub const Vec4u32 = @Vector(4, u32);
|
||||
pub const Vec3u32 = @Vector(3, u32);
|
||||
pub const Vec2u32 = @Vector(2, u32);
|
||||
const Vec4u32 = lib.Vec4u32;
|
||||
const Vec3u32 = lib.Vec3u32;
|
||||
const Vec2u32 = lib.Vec2u32;
|
||||
|
||||
pub const Variant = enum {
|
||||
String,
|
||||
@@ -70,198 +72,13 @@ const ImageInfo = struct {
|
||||
access: spv.SpvAccessQualifier,
|
||||
};
|
||||
|
||||
const Decoration = struct {
|
||||
pub const Decoration = struct {
|
||||
rtype: spv.SpvDecoration,
|
||||
literal_1: SpvWord,
|
||||
literal_2: ?SpvWord,
|
||||
index: SpvWord,
|
||||
};
|
||||
|
||||
pub const Value = union(Type) {
|
||||
Void: struct {},
|
||||
Bool: bool,
|
||||
Int: struct {
|
||||
bit_count: usize,
|
||||
value: extern union {
|
||||
sint8: i8,
|
||||
sint16: i16,
|
||||
sint32: i32,
|
||||
sint64: i64,
|
||||
uint8: u8,
|
||||
uint16: u16,
|
||||
uint32: u32,
|
||||
uint64: u64,
|
||||
},
|
||||
},
|
||||
Float: struct {
|
||||
bit_count: usize,
|
||||
value: extern union {
|
||||
float16: f16,
|
||||
float32: f32,
|
||||
float64: f64,
|
||||
},
|
||||
},
|
||||
Vector: []Value,
|
||||
Vector4f32: Vec4f32,
|
||||
Vector3f32: Vec3f32,
|
||||
Vector2f32: Vec2f32,
|
||||
Vector4i32: Vec4i32,
|
||||
Vector3i32: Vec3i32,
|
||||
Vector2i32: Vec2i32,
|
||||
Vector4u32: Vec4u32,
|
||||
Vector3u32: Vec3u32,
|
||||
Vector2u32: Vec2u32,
|
||||
Matrix: []Value,
|
||||
Array: []Value,
|
||||
RuntimeArray: ?[]Value,
|
||||
Structure: []Value,
|
||||
Function: noreturn,
|
||||
Image: struct {},
|
||||
Sampler: struct {},
|
||||
SampledImage: struct {},
|
||||
Pointer: union(enum) {
|
||||
common: *Value,
|
||||
f32_ptr: *f32,
|
||||
i32_ptr: *i32, //< For vector specializations
|
||||
u32_ptr: *u32,
|
||||
},
|
||||
|
||||
pub inline fn getCompositeDataOrNull(self: *const Value) ?[]Value {
|
||||
return switch (self.*) {
|
||||
.Vector, .Matrix, .Array, .Structure => |v| v,
|
||||
.RuntimeArray => |v| v,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
fn init(allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!Value {
|
||||
const resolved = results[target].resolveType(results);
|
||||
const member_count = resolved.getMemberCounts();
|
||||
|
||||
return switch (resolved.variant.?) {
|
||||
.Type => |t| switch (t) {
|
||||
.Bool => .{ .Bool = false },
|
||||
.Int => |i| .{ .Int = .{
|
||||
.bit_count = i.bit_length,
|
||||
.value = .{ .uint64 = 0 },
|
||||
} },
|
||||
.Float => |f| .{ .Float = .{
|
||||
.bit_count = f.bit_length,
|
||||
.value = .{ .float64 = 0 },
|
||||
} },
|
||||
.Vector => |v| blk: {
|
||||
var self: Value = .{ .Vector = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer self.deinit(allocator);
|
||||
|
||||
for (self.Vector) |*value| {
|
||||
value.* = try Value.init(allocator, results, v.components_type_word);
|
||||
}
|
||||
break :blk self;
|
||||
},
|
||||
.Vector4f32 => .{ .Vector4f32 = Vec4f32{ 0.0, 0.0, 0.0, 0.0 } },
|
||||
.Vector3f32 => .{ .Vector3f32 = Vec3f32{ 0.0, 0.0, 0.0 } },
|
||||
.Vector2f32 => .{ .Vector2f32 = Vec2f32{ 0.0, 0.0 } },
|
||||
.Vector4i32 => .{ .Vector4i32 = Vec4i32{ 0, 0, 0, 0 } },
|
||||
.Vector3i32 => .{ .Vector3i32 = Vec3i32{ 0, 0, 0 } },
|
||||
.Vector2i32 => .{ .Vector2i32 = Vec2i32{ 0, 0 } },
|
||||
.Vector4u32 => .{ .Vector4u32 = Vec4u32{ 0, 0, 0, 0 } },
|
||||
.Vector3u32 => .{ .Vector3u32 = Vec3u32{ 0, 0, 0 } },
|
||||
.Vector2u32 => .{ .Vector2u32 = Vec2u32{ 0, 0 } },
|
||||
.Matrix => |m| blk: {
|
||||
var self: Value = .{ .Matrix = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer self.deinit(allocator);
|
||||
|
||||
for (self.Matrix) |*value| {
|
||||
value.* = try Value.init(allocator, results, m.column_type_word);
|
||||
}
|
||||
break :blk self;
|
||||
},
|
||||
.Array => |a| blk: {
|
||||
var self: Value = .{ .Array = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer self.deinit(allocator);
|
||||
|
||||
for (self.Array) |*value| {
|
||||
value.* = try Value.init(allocator, results, a.components_type_word);
|
||||
}
|
||||
break :blk self;
|
||||
},
|
||||
.Structure => |s| blk: {
|
||||
var self: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer self.deinit(allocator);
|
||||
|
||||
for (self.Structure, s.members_type_word) |*value, member_type_word| {
|
||||
value.* = try Value.init(allocator, results, member_type_word);
|
||||
}
|
||||
break :blk self;
|
||||
},
|
||||
.RuntimeArray => .{ .RuntimeArray = null },
|
||||
else => unreachable,
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Performs a deep copy
|
||||
pub fn dupe(self: *const Value, allocator: std.mem.Allocator) RuntimeError!Value {
|
||||
return switch (self.*) {
|
||||
.Vector => |v| .{
|
||||
.Vector = blk: {
|
||||
const values = allocator.dupe(Value, v) catch return RuntimeError.OutOfMemory;
|
||||
for (values, v) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk values;
|
||||
},
|
||||
},
|
||||
.Matrix => |m| .{
|
||||
.Matrix = blk: {
|
||||
const values = allocator.dupe(Value, m) catch return RuntimeError.OutOfMemory;
|
||||
for (values, m) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk values;
|
||||
},
|
||||
},
|
||||
.Array => |a| .{
|
||||
.Array = blk: {
|
||||
const values = allocator.dupe(Value, a) catch return RuntimeError.OutOfMemory;
|
||||
for (values, a) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk values;
|
||||
},
|
||||
},
|
||||
.RuntimeArray => |opt_a| .{
|
||||
.RuntimeArray = blk: {
|
||||
if (opt_a) |a| {
|
||||
const values = allocator.dupe(Value, a) catch return RuntimeError.OutOfMemory;
|
||||
for (values, a) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk values;
|
||||
} else {
|
||||
break :blk null;
|
||||
}
|
||||
},
|
||||
},
|
||||
.Structure => |s| .{
|
||||
.Structure = blk: {
|
||||
const values = allocator.dupe(Value, s) catch return RuntimeError.OutOfMemory;
|
||||
for (values, s) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk values;
|
||||
},
|
||||
},
|
||||
else => self.*,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *Value, allocator: std.mem.Allocator) void {
|
||||
switch (self.*) {
|
||||
.Vector, .Matrix, .Array, .Structure => |values| {
|
||||
for (values) |*value| value.deinit(allocator);
|
||||
allocator.free(values);
|
||||
},
|
||||
.RuntimeArray => |opt_values| if (opt_values) |values| {
|
||||
for (values) |*value| value.deinit(allocator);
|
||||
allocator.free(values);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const TypeData = union(Type) {
|
||||
Void: struct {},
|
||||
Bool: struct {},
|
||||
@@ -295,13 +112,16 @@ pub const TypeData = union(Type) {
|
||||
components_type_word: SpvWord,
|
||||
components_type: Type,
|
||||
member_count: SpvWord,
|
||||
stride: SpvWord,
|
||||
},
|
||||
RuntimeArray: struct {
|
||||
components_type_word: SpvWord,
|
||||
components_type: Type,
|
||||
stride: SpvWord,
|
||||
},
|
||||
Structure: struct {
|
||||
members_type_word: []const SpvWord,
|
||||
members_offsets: []?SpvWord,
|
||||
member_names: std.ArrayList([]const u8),
|
||||
},
|
||||
Function: struct {
|
||||
@@ -309,7 +129,15 @@ pub const TypeData = union(Type) {
|
||||
return_type: SpvWord,
|
||||
params: []const SpvWord,
|
||||
},
|
||||
Image: struct {},
|
||||
Image: struct {
|
||||
dim: spv.SpvDim,
|
||||
depth: SpvByte,
|
||||
arrayed: SpvByte,
|
||||
ms: SpvByte,
|
||||
sampled: SpvByte,
|
||||
format: spv.SpvImageFormat,
|
||||
access: ?spv.SpvAccessQualifier,
|
||||
},
|
||||
Sampler: struct {},
|
||||
SampledImage: struct {},
|
||||
Pointer: struct {
|
||||
@@ -323,12 +151,18 @@ pub const TypeData = union(Type) {
|
||||
.Int => |i| @divExact(i.bit_length, 8),
|
||||
.Float => |f| @divExact(f.bit_length, 8),
|
||||
.Vector => |v| results[v.components_type_word].variant.?.Type.getSize(results),
|
||||
.Array => |a| results[a.components_type_word].variant.?.Type.getSize(results),
|
||||
.Array => |a| a.stride,
|
||||
.Matrix => |m| results[m.column_type_word].variant.?.Type.getSize(results),
|
||||
.RuntimeArray => |a| results[a.components_type_word].variant.?.Type.getSize(results),
|
||||
.RuntimeArray => |a| a.stride,
|
||||
.Structure => |s| blk: {
|
||||
var total: usize = 0;
|
||||
for (s.members_type_word) |type_word| {
|
||||
for (s.members_type_word, 0..) |type_word, i| {
|
||||
if (i + 1 < s.members_offsets.len) {
|
||||
if (s.members_offsets[i + 1]) |offset| {
|
||||
total = offset;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
total += results[type_word].variant.?.Type.getSize(results);
|
||||
}
|
||||
break :blk total;
|
||||
@@ -367,6 +201,8 @@ pub const VariantData = union(Variant) {
|
||||
},
|
||||
AccessChain: struct {
|
||||
target: SpvWord,
|
||||
base: SpvWord,
|
||||
indexes: []SpvWord,
|
||||
value: Value,
|
||||
},
|
||||
FunctionParameter: struct {
|
||||
@@ -406,12 +242,17 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
for (data.member_names.items) |name| {
|
||||
allocator.free(name);
|
||||
}
|
||||
allocator.free(data.members_offsets);
|
||||
data.member_names.deinit(allocator);
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.Constant => |*c| c.value.deinit(allocator),
|
||||
.Variable => |*v| v.value.deinit(allocator),
|
||||
.AccessChain => |*a| {
|
||||
allocator.free(a.indexes);
|
||||
a.value.deinit(allocator);
|
||||
},
|
||||
.Function => |f| allocator.free(f.params),
|
||||
else => {},
|
||||
}
|
||||
@@ -482,6 +323,7 @@ pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self {
|
||||
.Type = .{
|
||||
.Structure = .{
|
||||
.members_type_word = allocator.dupe(SpvWord, s.members_type_word) catch return RuntimeError.OutOfMemory,
|
||||
.members_offsets = allocator.dupe(?SpvWord, s.members_offsets) catch return RuntimeError.OutOfMemory,
|
||||
.member_names = blk2: {
|
||||
const member_names = s.member_names.clone(allocator) catch return RuntimeError.OutOfMemory;
|
||||
for (member_names.items, s.member_names.items) |*new_name, name| {
|
||||
@@ -526,6 +368,14 @@ pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self {
|
||||
.params = allocator.dupe(SpvWord, f.params) catch return RuntimeError.OutOfMemory,
|
||||
},
|
||||
},
|
||||
.AccessChain => |a| break :blk .{
|
||||
.AccessChain = .{
|
||||
.target = a.target,
|
||||
.base = a.base,
|
||||
.indexes = allocator.dupe(SpvWord, a.indexes) catch return RuntimeError.OutOfMemory,
|
||||
.value = try a.value.dupe(allocator),
|
||||
},
|
||||
},
|
||||
else => break :blk variant,
|
||||
}
|
||||
}
|
||||
@@ -554,6 +404,17 @@ pub fn resolveLaneBitWidth(target_type: TypeData, rt: *const Runtime) RuntimeErr
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resolveLaneCount(target_type: TypeData) RuntimeError!SpvWord {
|
||||
return switch (target_type) {
|
||||
.Bool, .Float, .Int => 1,
|
||||
.Vector => |v| v.member_count,
|
||||
.Vector4f32, .Vector4i32, .Vector4u32 => 4,
|
||||
.Vector3f32, .Vector3i32, .Vector3u32 => 3,
|
||||
.Vector2f32, .Vector2i32, .Vector2u32 => 2,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resolveSign(target_type: TypeData, rt: *const Runtime) RuntimeError!enum { signed, unsigned } {
|
||||
return sw: switch (target_type) {
|
||||
.Int => |i| if (i.is_signed) .signed else .unsigned,
|
||||
@@ -568,17 +429,21 @@ pub fn resolveSign(target_type: TypeData, rt: *const Runtime) RuntimeError!enum
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resolveType(self: *const Self, results: []const Self) *const Self {
|
||||
pub inline fn resolveType(self: *const Self, results: []const Self) *const Self {
|
||||
return if (self.resolveTypeWordOrNull()) |word| &results[word] else self;
|
||||
}
|
||||
|
||||
pub fn resolveTypeWordOrNull(self: *const Self) ?SpvWord {
|
||||
return if (self.variant) |variant|
|
||||
switch (variant) {
|
||||
.Type => |t| switch (t) {
|
||||
.Pointer => |ptr| &results[ptr.target],
|
||||
else => self,
|
||||
.Pointer => |ptr| ptr.target,
|
||||
else => null,
|
||||
},
|
||||
else => self,
|
||||
else => null,
|
||||
}
|
||||
else
|
||||
self;
|
||||
null;
|
||||
}
|
||||
|
||||
pub fn getMemberCounts(self: *const Self) usize {
|
||||
@@ -603,76 +468,13 @@ pub fn getMemberCounts(self: *const Self) usize {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn initValue(allocator: std.mem.Allocator, member_count: usize, results: []const Self, resolved: *const Self) RuntimeError!Value {
|
||||
return switch (resolved.variant.?) {
|
||||
.Type => |t| switch (t) {
|
||||
.Void => .{ .Void = .{} },
|
||||
.Bool => .{ .Bool = false },
|
||||
.Int => |i| .{ .Int = .{
|
||||
.bit_count = i.bit_length,
|
||||
.value = .{ .uint64 = 0 },
|
||||
} },
|
||||
.Float => |f| .{ .Float = .{
|
||||
.bit_count = f.bit_length,
|
||||
.value = .{ .float64 = 0 },
|
||||
} },
|
||||
.Vector => |v| blk: {
|
||||
const value: Value = .{ .Vector = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer allocator.free(value.Vector);
|
||||
for (value.Vector) |*val| {
|
||||
val.* = try Value.init(allocator, results, v.components_type_word);
|
||||
}
|
||||
break :blk value;
|
||||
},
|
||||
.Vector4f32 => .{ .Vector4f32 = Vec4f32{ 0.0, 0.0, 0.0, 0.0 } },
|
||||
.Vector3f32 => .{ .Vector3f32 = Vec3f32{ 0.0, 0.0, 0.0 } },
|
||||
.Vector2f32 => .{ .Vector2f32 = Vec2f32{ 0.0, 0.0 } },
|
||||
.Vector4i32 => .{ .Vector4i32 = Vec4i32{ 0, 0, 0, 0 } },
|
||||
.Vector3i32 => .{ .Vector3i32 = Vec3i32{ 0, 0, 0 } },
|
||||
.Vector2i32 => .{ .Vector2i32 = Vec2i32{ 0, 0 } },
|
||||
.Vector4u32 => .{ .Vector4u32 = Vec4u32{ 0, 0, 0, 0 } },
|
||||
.Vector3u32 => .{ .Vector3u32 = Vec3u32{ 0, 0, 0 } },
|
||||
.Vector2u32 => .{ .Vector2u32 = Vec2u32{ 0, 0 } },
|
||||
.Matrix => |m| blk: {
|
||||
const value: Value = .{ .Matrix = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer allocator.free(value.Matrix);
|
||||
for (value.Matrix) |*v| {
|
||||
v.* = try Value.init(allocator, results, m.column_type_word);
|
||||
}
|
||||
break :blk value;
|
||||
},
|
||||
.Array => |a| blk: {
|
||||
const value: Value = .{ .Array = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer allocator.free(value.Array);
|
||||
for (value.Array) |*val| {
|
||||
val.* = try Value.init(allocator, results, a.components_type_word);
|
||||
}
|
||||
break :blk value;
|
||||
},
|
||||
.RuntimeArray => |a| blk: {
|
||||
if (member_count == 0) {
|
||||
break :blk Value{ .RuntimeArray = null };
|
||||
}
|
||||
const value: Value = .{ .RuntimeArray = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer allocator.free(value.RuntimeArray.?);
|
||||
for (value.RuntimeArray.?) |*val| {
|
||||
val.* = try Value.init(allocator, results, a.components_type_word);
|
||||
}
|
||||
break :blk value;
|
||||
},
|
||||
.Structure => |s| blk: {
|
||||
const value: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer allocator.free(value.Structure);
|
||||
for (value.Structure, s.members_type_word) |*v, member_type_word| {
|
||||
v.* = try Value.init(allocator, results, member_type_word);
|
||||
}
|
||||
break :blk value;
|
||||
},
|
||||
.Image => RuntimeError.ToDo,
|
||||
.Sampler => RuntimeError.ToDo,
|
||||
.SampledImage => RuntimeError.ToDo,
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
},
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
pub fn flushPtr(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
if (self.variant) |*variant| {
|
||||
switch (variant.*) {
|
||||
.Constant => |*c| try c.value.flushPtr(allocator),
|
||||
.Variable => |*v| try v.value.flushPtr(allocator),
|
||||
.AccessChain => |*a| try a.value.flushPtr(allocator),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+182
-271
@@ -4,6 +4,7 @@ const std = @import("std");
|
||||
const spv = @import("spv.zig");
|
||||
const op = @import("opcodes.zig");
|
||||
const lib = @import("lib.zig");
|
||||
const pretty = @import("pretty");
|
||||
|
||||
const SpvVoid = spv.SpvVoid;
|
||||
const SpvByte = spv.SpvByte;
|
||||
@@ -29,6 +30,13 @@ pub const RuntimeError = error{
|
||||
Unreachable,
|
||||
UnsupportedSpirV,
|
||||
UnsupportedExtension,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
pub const SpecializationEntry = struct {
|
||||
id: SpvWord,
|
||||
offset: usize,
|
||||
size: usize,
|
||||
};
|
||||
|
||||
pub const Function = struct {
|
||||
@@ -37,6 +45,22 @@ pub const Function = struct {
|
||||
ret: *Result,
|
||||
};
|
||||
|
||||
pub fn Vec4(comptime T: type) type {
|
||||
return struct {
|
||||
x: T,
|
||||
y: T,
|
||||
z: T,
|
||||
w: T,
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
mod: *Module,
|
||||
it: WordIterator,
|
||||
|
||||
@@ -47,7 +71,14 @@ current_parameter_index: SpvWord,
|
||||
current_function: ?*Result,
|
||||
function_stack: std.ArrayList(Function),
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, module: *Module) RuntimeError!Self {
|
||||
current_label: ?SpvWord,
|
||||
previous_label: ?SpvWord,
|
||||
|
||||
specialization_constants: std.AutoHashMapUnmanaged(u32, []const u8),
|
||||
|
||||
image_api: ImageAPI,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, module: *Module, image_api: ImageAPI) RuntimeError!Self {
|
||||
return .{
|
||||
.mod = module,
|
||||
.it = module.it,
|
||||
@@ -61,6 +92,10 @@ pub fn init(allocator: std.mem.Allocator, module: *Module) RuntimeError!Self {
|
||||
.current_parameter_index = 0,
|
||||
.current_function = null,
|
||||
.function_stack = .empty,
|
||||
.current_label = null,
|
||||
.previous_label = null,
|
||||
.specialization_constants = .empty,
|
||||
.image_api = image_api,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,22 +105,35 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
}
|
||||
allocator.free(self.results);
|
||||
self.function_stack.deinit(allocator);
|
||||
var it = self.specialization_constants.iterator();
|
||||
while (it.next()) |entry| {
|
||||
allocator.free(entry.value_ptr.*);
|
||||
}
|
||||
self.specialization_constants.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn getEntryPointByName(self: *const Self, name: []const u8) error{NotFound}!SpvWord {
|
||||
pub fn addSpecializationInfo(self: *Self, allocator: std.mem.Allocator, entry: SpecializationEntry, data: []const u8) RuntimeError!void {
|
||||
const slice = allocator.dupe(u8, data[entry.offset .. entry.offset + entry.size]) catch return RuntimeError.OutOfMemory;
|
||||
self.specialization_constants.put(allocator, entry.id, slice) catch 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: {
|
||||
// Not using std.mem.eql as entry point names may have longer size than their content
|
||||
for (0..@min(name.len, entry_point.name.len)) |j| {
|
||||
if (name[j] != entry_point.name[j]) break :blk false;
|
||||
if (name[j] != entry_point.name[j])
|
||||
break :blk false;
|
||||
}
|
||||
if (entry_point.name.len != name.len and entry_point.name[name.len] != 0)
|
||||
break :blk false;
|
||||
break :blk true;
|
||||
}) return @intCast(i);
|
||||
}
|
||||
return error.NotFound;
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
|
||||
pub fn getResultByName(self: *const Self, name: []const u8) error{NotFound}!SpvWord {
|
||||
pub fn getResultByName(self: *const Self, name: []const u8) RuntimeError!SpvWord {
|
||||
for (self.results, 0..) |result, i| {
|
||||
if (result.name) |result_name| {
|
||||
if (blk: {
|
||||
@@ -97,14 +145,48 @@ pub fn getResultByName(self: *const Self, name: []const u8) error{NotFound}!SpvW
|
||||
}) return @intCast(i);
|
||||
}
|
||||
}
|
||||
return error.NotFound;
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
|
||||
pub fn getResultByLocation(self: *const Self, location: SpvWord, kind: enum { input, output }) RuntimeError!SpvWord {
|
||||
switch (kind) {
|
||||
.input => if (location < self.mod.input_locations.len and self.mod.input_locations[location] != 0) {
|
||||
return self.mod.input_locations[location];
|
||||
},
|
||||
.output => if (location < self.mod.output_locations.len and self.mod.output_locations[location] != 0) {
|
||||
return self.mod.output_locations[location];
|
||||
},
|
||||
}
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
|
||||
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,
|
||||
.max_depth = 0,
|
||||
.struct_max_len = 0,
|
||||
.array_max_len = 0,
|
||||
}) catch return RuntimeError.OutOfMemory;
|
||||
defer allocator.free(dump);
|
||||
writer.print("{s}", .{dump}) catch return RuntimeError.Unknown;
|
||||
writer.flush() catch return RuntimeError.Unknown;
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
if (entry_point_index > self.mod.entry_points.items.len) return RuntimeError.InvalidEntryPoint;
|
||||
if (entry_point_index > self.mod.entry_points.items.len)
|
||||
return RuntimeError.InvalidEntryPoint;
|
||||
|
||||
// Spec constants pass
|
||||
try self.pass(allocator, .initMany(&.{
|
||||
.SpecConstantTrue,
|
||||
.SpecConstantFalse,
|
||||
.SpecConstantComposite,
|
||||
.SpecConstant,
|
||||
.SpecConstantOp,
|
||||
}));
|
||||
|
||||
{
|
||||
const entry_point_desc = &self.mod.entry_points.items[entry_point_index];
|
||||
@@ -112,7 +194,8 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind
|
||||
if (entry_point_result.variant) |variant| {
|
||||
switch (variant) {
|
||||
.Function => |f| {
|
||||
if (!self.it.jumpToSourceLocation(f.source_location)) return RuntimeError.InvalidEntryPoint;
|
||||
if (!self.it.jumpToSourceLocation(f.source_location))
|
||||
return RuntimeError.InvalidEntryPoint;
|
||||
self.function_stack.append(allocator, .{
|
||||
.source_location = f.source_location,
|
||||
.result = entry_point_result,
|
||||
@@ -126,11 +209,24 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind
|
||||
}
|
||||
}
|
||||
|
||||
// Execution pass
|
||||
try self.pass(allocator, null);
|
||||
}
|
||||
|
||||
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 opcode = (opcode_data & spv.SpvOpCodeMask);
|
||||
|
||||
if (op_set) |set| {
|
||||
@branchHint(.unlikely);
|
||||
if (!set.contains(@enumFromInt(opcode))) {
|
||||
_ = self.it.skipN(word_count);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var it_tmp = self.it; // Save because operations may iter on this iterator
|
||||
if (op.runtime_dispatcher[opcode]) |pfn| {
|
||||
try pfn(allocator, word_count, self);
|
||||
@@ -142,60 +238,76 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind
|
||||
self.it.did_jump = false;
|
||||
}
|
||||
}
|
||||
|
||||
//@import("pretty").print(allocator, self.results, .{
|
||||
// .tab_size = 4,
|
||||
// .max_depth = 0,
|
||||
// .struct_max_len = 0,
|
||||
// .array_max_len = 0,
|
||||
//}) catch return RuntimeError.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn readDescriptorSet(self: *const Self, output: []u8, set: SpvWord, binding: SpvWord) RuntimeError!void {
|
||||
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) {
|
||||
_ = try self.readValue(output, &self.results[self.mod.bindings[set][binding]].variant.?.Variable.value);
|
||||
const value = &self.results[self.mod.bindings[set][binding]].variant.?.Variable.value;
|
||||
switch (value.*) {
|
||||
.Array => |arr| {
|
||||
if (descriptor_index >= arr.values.len)
|
||||
return RuntimeError.NotFound;
|
||||
_ = try arr.values[descriptor_index].writeConst(input);
|
||||
},
|
||||
else => {
|
||||
if (descriptor_index != 0)
|
||||
return RuntimeError.NotFound;
|
||||
_ = try value.writeConst(input);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeDescriptorSet(self: *const Self, allocator: std.mem.Allocator, input: []const u8, set: SpvWord, binding: SpvWord) RuntimeError!void {
|
||||
if (set < lib.SPIRV_MAX_SET and binding < lib.SPIRV_MAX_SET_BINDINGS) {
|
||||
const variable = &self.results[self.mod.bindings[set][binding]].variant.?.Variable;
|
||||
fn readResultValue(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void {
|
||||
const variant = self.results[result].variant orelse return RuntimeError.InvalidSpirV;
|
||||
switch (variant) {
|
||||
.Variable => |v| _ = try v.value.read(output),
|
||||
.AccessChain => |a| switch (a.value) {
|
||||
.Pointer => |ptr| switch (ptr.ptr) {
|
||||
.common => |value_ptr| _ = try value_ptr.read(output),
|
||||
.f32_ptr => |value_ptr| std.mem.copyForwards(u8, output[0..@sizeOf(f32)], std.mem.asBytes(value_ptr)),
|
||||
.i32_ptr => |value_ptr| std.mem.copyForwards(u8, output[0..@sizeOf(i32)], std.mem.asBytes(value_ptr)),
|
||||
.u32_ptr => |value_ptr| std.mem.copyForwards(u8, output[0..@sizeOf(u32)], std.mem.asBytes(value_ptr)),
|
||||
},
|
||||
else => _ = try a.value.read(output),
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
}
|
||||
}
|
||||
|
||||
const helper = struct {
|
||||
fn init(allocator2: std.mem.Allocator, len: usize, value: *Result.Value, type_word: SpvWord, results: []Result) RuntimeError!void {
|
||||
const resolved = results[type_word].resolveType(results);
|
||||
|
||||
switch (value.*) {
|
||||
.RuntimeArray => |a| if (a == null) {
|
||||
const elem_size = resolved.variant.?.Type.getSize(results);
|
||||
value.* = try Result.initValue(allocator2, std.math.divCeil(usize, len, elem_size) catch unreachable, results, resolved);
|
||||
},
|
||||
.Structure => |*s| for (s.*, 0..) |*elem, i| {
|
||||
try @This().init(allocator2, len, elem, resolved.variant.?.Type.Structure.members_type_word[i], results);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
try helper.init(allocator, input.len, &variable.value, variable.type_word, self.results);
|
||||
|
||||
//@import("pretty").print(allocator, variable, .{
|
||||
// .tab_size = 4,
|
||||
// .max_depth = 0,
|
||||
// .struct_max_len = 0,
|
||||
// .array_max_len = 0,
|
||||
//}) catch return RuntimeError.OutOfMemory;
|
||||
_ = try self.writeValue(input, &variable.value);
|
||||
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),
|
||||
.AccessChain => |*a| switch (a.value) {
|
||||
.Pointer => |ptr| switch (ptr.ptr) {
|
||||
.common => |value_ptr| _ = try value_ptr.writeConst(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 => return RuntimeError.InvalidSpirV,
|
||||
}
|
||||
} else {
|
||||
return RuntimeError.NotFound;
|
||||
return RuntimeError.InvalidSpirV;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void {
|
||||
if (std.mem.indexOfScalar(SpvWord, &self.mod.output_locations, result)) |_| {
|
||||
_ = try self.readValue(output, &self.results[result].variant.?.Variable.value);
|
||||
try self.readResultValue(output, result);
|
||||
} else {
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readBuiltIn(self: *const Self, output: []u8, builtin: spv.SpvBuiltIn) RuntimeError!void {
|
||||
if (self.mod.builtins.get(builtin)) |result| {
|
||||
try self.readResultValue(output, result);
|
||||
} else {
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
@@ -203,7 +315,7 @@ pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError
|
||||
|
||||
pub fn writeInput(self: *const Self, input: []const u8, result: SpvWord) RuntimeError!void {
|
||||
if (std.mem.indexOfScalar(SpvWord, &self.mod.input_locations, result)) |_| {
|
||||
_ = try self.writeValue(input, &self.results[result].variant.?.Variable.value);
|
||||
try self.writeResultValue(input, result);
|
||||
} else {
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
@@ -211,235 +323,34 @@ pub fn writeInput(self: *const Self, input: []const u8, result: SpvWord) Runtime
|
||||
|
||||
pub fn writeBuiltIn(self: *const Self, input: []const u8, builtin: spv.SpvBuiltIn) RuntimeError!void {
|
||||
if (self.mod.builtins.get(builtin)) |result| {
|
||||
_ = try self.writeValue(input, &self.results[result].variant.?.Variable.value);
|
||||
try self.writeResultValue(input, result);
|
||||
} else {
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flushDescriptorSets(self: *const Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
for (self.results) |*result| {
|
||||
try result.flushPtr(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getResultMemorySize(self: *const Self, result: SpvWord) RuntimeError!usize {
|
||||
const value = try self.results[result].getConstValue();
|
||||
return value.getPlainMemorySize();
|
||||
}
|
||||
|
||||
pub fn hasResultDecoration(self: *const Self, result: SpvWord, decoration: spv.SpvDecoration) bool {
|
||||
for (self.results[result].decorations.items) |result_decoration| {
|
||||
if (result_decoration.rtype == decoration)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn reset(self: *Self) void {
|
||||
self.function_stack.clearRetainingCapacity();
|
||||
self.current_function = null;
|
||||
}
|
||||
|
||||
fn readValue(self: *const Self, output: []u8, value: *const Result.Value) RuntimeError!usize {
|
||||
switch (value.*) {
|
||||
.Bool => |b| {
|
||||
output[0] = if (b == true) 1 else 0;
|
||||
return 1;
|
||||
},
|
||||
.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)),
|
||||
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)),
|
||||
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;
|
||||
},
|
||||
.Vector,
|
||||
.Matrix,
|
||||
.Array,
|
||||
.Structure,
|
||||
=> |values| {
|
||||
var offset: usize = 0;
|
||||
for (values) |v| {
|
||||
offset += try self.readValue(output[offset..], &v);
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
.RuntimeArray => |opt_values| if (opt_values) |values| {
|
||||
var offset: usize = 0;
|
||||
for (values) |v| {
|
||||
offset += try self.readValue(output[offset..], &v);
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn writeValue(self: *const Self, input: []const u8, value: *Result.Value) RuntimeError!usize {
|
||||
switch (value.*) {
|
||||
.Bool => |*b| {
|
||||
b.* = if (input[0] != 0) true else false;
|
||||
return 1;
|
||||
},
|
||||
.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]),
|
||||
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]),
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]);
|
||||
}
|
||||
return 2 * 4;
|
||||
},
|
||||
.Vector,
|
||||
.Matrix,
|
||||
.Array,
|
||||
.Structure,
|
||||
=> |*values| {
|
||||
var offset: usize = 0;
|
||||
for (values.*) |*v| {
|
||||
offset += try self.writeValue(input[offset..], v);
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
.RuntimeArray => |opt_values| if (opt_values) |*values| {
|
||||
var offset: usize = 0;
|
||||
for (values.*) |*v| {
|
||||
offset += try self.writeValue(input[offset..], v);
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
return 0;
|
||||
self.current_label = null;
|
||||
self.previous_label = null;
|
||||
}
|
||||
|
||||
+834
@@ -0,0 +1,834 @@
|
||||
const std = @import("std");
|
||||
const lib = @import("lib.zig");
|
||||
|
||||
const Result = @import("Result.zig");
|
||||
const Runtime = @import("Runtime.zig");
|
||||
const RuntimeError = Runtime.RuntimeError;
|
||||
|
||||
const SpvVoid = lib.SpvVoid;
|
||||
const SpvByte = lib.SpvByte;
|
||||
const SpvWord = lib.SpvWord;
|
||||
const SpvBool = lib.SpvBool;
|
||||
|
||||
const Vec4f32 = lib.Vec4f32;
|
||||
const Vec3f32 = lib.Vec3f32;
|
||||
const Vec2f32 = lib.Vec2f32;
|
||||
|
||||
const Vec4i32 = lib.Vec4i32;
|
||||
const Vec3i32 = lib.Vec3i32;
|
||||
const Vec2i32 = lib.Vec2i32;
|
||||
|
||||
const Vec4u32 = lib.Vec4u32;
|
||||
const Vec3u32 = lib.Vec3u32;
|
||||
const Vec2u32 = lib.Vec2u32;
|
||||
|
||||
const Type = Result.Type;
|
||||
|
||||
pub const PrimitiveType = enum {
|
||||
Bool,
|
||||
Float,
|
||||
SInt,
|
||||
UInt,
|
||||
};
|
||||
|
||||
pub const Value = union(Type) {
|
||||
const Self = @This();
|
||||
|
||||
Void: struct {},
|
||||
Bool: bool,
|
||||
Int: struct {
|
||||
bit_count: usize,
|
||||
is_signed: bool,
|
||||
value: extern union {
|
||||
sint8: i8,
|
||||
sint16: i16,
|
||||
sint32: i32,
|
||||
sint64: i64,
|
||||
uint8: u8,
|
||||
uint16: u16,
|
||||
uint32: u32,
|
||||
uint64: u64,
|
||||
},
|
||||
},
|
||||
Float: struct {
|
||||
bit_count: usize,
|
||||
value: extern union {
|
||||
float16: f16,
|
||||
float32: f32,
|
||||
float64: f64,
|
||||
},
|
||||
},
|
||||
Vector: []Self,
|
||||
Vector4f32: Vec4f32,
|
||||
Vector3f32: Vec3f32,
|
||||
Vector2f32: Vec2f32,
|
||||
Vector4i32: Vec4i32,
|
||||
Vector3i32: Vec3i32,
|
||||
Vector2i32: Vec2i32,
|
||||
Vector4u32: Vec4u32,
|
||||
Vector3u32: Vec3u32,
|
||||
Vector2u32: Vec2u32,
|
||||
Matrix: []Self,
|
||||
Array: struct {
|
||||
stride: SpvWord,
|
||||
values: []Self,
|
||||
},
|
||||
RuntimeArray: struct {
|
||||
type_word: SpvWord,
|
||||
stride: SpvWord,
|
||||
data: []u8,
|
||||
|
||||
pub inline fn createValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!*Value {
|
||||
const value = allocator.create(Value) catch return RuntimeError.OutOfMemory;
|
||||
errdefer allocator.destroy(value);
|
||||
|
||||
value.* = try Value.init(allocator, results, self.type_word, false);
|
||||
_ = try value.writeConst(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)..]);
|
||||
return value;
|
||||
}
|
||||
|
||||
pub inline fn getOffsetOfIndex(self: *const @This(), index: usize) usize {
|
||||
return self.stride * index;
|
||||
}
|
||||
|
||||
pub inline fn getLen(self: *const @This()) usize {
|
||||
return @divTrunc(self.data.len, self.stride);
|
||||
}
|
||||
},
|
||||
Structure: struct {
|
||||
offsets: []const ?SpvWord,
|
||||
values: []Self,
|
||||
},
|
||||
Function: noreturn,
|
||||
Image: struct {
|
||||
type_word: SpvWord,
|
||||
driver_image: *anyopaque,
|
||||
},
|
||||
Sampler: struct {},
|
||||
SampledImage: struct {},
|
||||
Pointer: struct {
|
||||
ptr: union(enum) {
|
||||
common: *Self,
|
||||
f32_ptr: *f32,
|
||||
i32_ptr: *i32, //< For vector specializations
|
||||
u32_ptr: *u32,
|
||||
},
|
||||
is_owner_of_uniform_slice: bool = false,
|
||||
uniform_slice_window: ?[]u8 = null,
|
||||
},
|
||||
|
||||
pub inline fn getCompositeDataOrNull(self: *const Self) ?[]Self {
|
||||
return switch (self.*) {
|
||||
.Structure => |*s| s.values,
|
||||
.Array => |*a| a.values,
|
||||
.Vector, .Matrix => |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 switch (results[resolved].variant.?) {
|
||||
.Type => |t| switch (t) {
|
||||
.Void => .{ .Void = .{} },
|
||||
.Bool => .{ .Bool = false },
|
||||
.Int => |i| .{ .Int = .{
|
||||
.bit_count = i.bit_length,
|
||||
.is_signed = i.is_signed,
|
||||
.value = .{ .uint64 = 0 },
|
||||
} },
|
||||
.Float => |f| .{ .Float = .{
|
||||
.bit_count = f.bit_length,
|
||||
.value = .{ .float64 = 0 },
|
||||
} },
|
||||
.Vector => |v| blk: {
|
||||
const self: Self = .{ .Vector = allocator.alloc(Self, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer allocator.free(self.Vector);
|
||||
|
||||
for (self.Vector) |*value| {
|
||||
value.* = try Self.init(allocator, results, v.components_type_word, is_externally_visible);
|
||||
}
|
||||
break :blk self;
|
||||
},
|
||||
.Vector4f32 => .{ .Vector4f32 = Vec4f32{ 0.0, 0.0, 0.0, 0.0 } },
|
||||
.Vector3f32 => .{ .Vector3f32 = Vec3f32{ 0.0, 0.0, 0.0 } },
|
||||
.Vector2f32 => .{ .Vector2f32 = Vec2f32{ 0.0, 0.0 } },
|
||||
.Vector4i32 => .{ .Vector4i32 = Vec4i32{ 0, 0, 0, 0 } },
|
||||
.Vector3i32 => .{ .Vector3i32 = Vec3i32{ 0, 0, 0 } },
|
||||
.Vector2i32 => .{ .Vector2i32 = Vec2i32{ 0, 0 } },
|
||||
.Vector4u32 => .{ .Vector4u32 = Vec4u32{ 0, 0, 0, 0 } },
|
||||
.Vector3u32 => .{ .Vector3u32 = Vec3u32{ 0, 0, 0 } },
|
||||
.Vector2u32 => .{ .Vector2u32 = Vec2u32{ 0, 0 } },
|
||||
.Matrix => |m| blk: {
|
||||
const self: Self = .{ .Matrix = allocator.alloc(Self, member_count) catch return RuntimeError.OutOfMemory };
|
||||
errdefer allocator.free(self.Matrix);
|
||||
|
||||
for (self.Matrix) |*value| {
|
||||
value.* = try Self.init(allocator, results, m.column_type_word, is_externally_visible);
|
||||
}
|
||||
break :blk self;
|
||||
},
|
||||
.Array => |a| blk: {
|
||||
// If an array is in externally visible storage we treat it as a runtime array
|
||||
if (is_externally_visible) {
|
||||
break :blk .{
|
||||
.RuntimeArray = .{
|
||||
.type_word = a.components_type_word,
|
||||
.stride = a.stride,
|
||||
.data = &.{},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const self: Self = .{
|
||||
.Array = .{
|
||||
.stride = a.stride,
|
||||
.values = allocator.alloc(Self, member_count) catch return RuntimeError.OutOfMemory,
|
||||
},
|
||||
};
|
||||
errdefer allocator.free(self.Array.values);
|
||||
|
||||
for (self.Array.values) |*value| {
|
||||
value.* = try Self.init(allocator, results, a.components_type_word, is_externally_visible);
|
||||
}
|
||||
break :blk self;
|
||||
},
|
||||
.Structure => |s| blk: {
|
||||
const self: Self = .{
|
||||
.Structure = .{
|
||||
.offsets = allocator.dupe(?SpvWord, s.members_offsets) catch return RuntimeError.OutOfMemory,
|
||||
.values = allocator.alloc(Self, member_count) catch return RuntimeError.OutOfMemory,
|
||||
},
|
||||
};
|
||||
errdefer allocator.free(self.Structure.values);
|
||||
|
||||
for (self.Structure.values, s.members_type_word) |*value, type_word| {
|
||||
value.* = try Self.init(allocator, results, type_word, is_externally_visible);
|
||||
}
|
||||
break :blk self;
|
||||
},
|
||||
.RuntimeArray => |a| .{
|
||||
.RuntimeArray = .{
|
||||
.type_word = a.components_type_word,
|
||||
.stride = a.stride,
|
||||
.data = &.{},
|
||||
},
|
||||
},
|
||||
.Image => .{
|
||||
.Image = .{
|
||||
.type_word = resolved,
|
||||
.driver_image = undefined,
|
||||
},
|
||||
},
|
||||
.Sampler => RuntimeError.ToDo,
|
||||
.SampledImage => RuntimeError.ToDo,
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
},
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
/// Performs a deep copy
|
||||
pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self {
|
||||
return switch (self.*) {
|
||||
.Vector => |v| .{
|
||||
.Vector = blk: {
|
||||
const values = allocator.dupe(Self, v) catch return RuntimeError.OutOfMemory;
|
||||
errdefer allocator.free(values);
|
||||
for (values, v) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk values;
|
||||
},
|
||||
},
|
||||
.Matrix => |m| .{
|
||||
.Matrix = blk: {
|
||||
const values = allocator.dupe(Self, m) catch return RuntimeError.OutOfMemory;
|
||||
errdefer allocator.free(values);
|
||||
for (values, m) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk values;
|
||||
},
|
||||
},
|
||||
.Array => |*a| .{
|
||||
.Array = blk: {
|
||||
const values = allocator.dupe(Self, a.values) catch return RuntimeError.OutOfMemory;
|
||||
errdefer allocator.free(values);
|
||||
for (values, a.values) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk .{
|
||||
.stride = a.stride,
|
||||
.values = values,
|
||||
};
|
||||
},
|
||||
},
|
||||
.Structure => |*s| .{
|
||||
.Structure = blk: {
|
||||
const values = allocator.dupe(Self, s.values) catch return RuntimeError.OutOfMemory;
|
||||
errdefer allocator.free(values);
|
||||
for (values, s.values) |*new_value, value| new_value.* = try value.dupe(allocator);
|
||||
break :blk .{
|
||||
.offsets = allocator.dupe(?SpvWord, s.offsets) catch return RuntimeError.OutOfMemory,
|
||||
.values = values,
|
||||
};
|
||||
},
|
||||
},
|
||||
else => self.*,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn read(self: *const Self, output: []u8) RuntimeError!usize {
|
||||
switch (self.*) {
|
||||
.Bool => |b| {
|
||||
output[0] = if (b == true) 1 else 0;
|
||||
return 1;
|
||||
},
|
||||
.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)),
|
||||
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)),
|
||||
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;
|
||||
},
|
||||
.Vector, .Matrix => |values| {
|
||||
var offset: usize = 0;
|
||||
for (values) |v| {
|
||||
offset += try v.read(output[offset..]);
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
.Array => |arr| {
|
||||
var offset: usize = 0;
|
||||
for (arr.values) |v| {
|
||||
_ = try v.read(output[offset..]);
|
||||
offset += arr.stride;
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
.Structure => |s| {
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
offset += read_size;
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
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: []u8) RuntimeError!usize {
|
||||
switch (self.*) {
|
||||
.Bool => |*b| {
|
||||
b.* = if (input[0] != 0) true else false;
|
||||
return 1;
|
||||
},
|
||||
.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]),
|
||||
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]),
|
||||
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;
|
||||
},
|
||||
.Vector, .Matrix => |*values| {
|
||||
var offset: usize = 0;
|
||||
for (values.*) |*v| {
|
||||
offset += try v.write(input[offset..]);
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
.Array => |*arr| {
|
||||
var offset: usize = 0;
|
||||
for (arr.values) |*v| {
|
||||
_ = try v.write(input[offset..]);
|
||||
offset += arr.stride;
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
.Structure => |s| {
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
offset += write_size;
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
.RuntimeArray => |*arr| arr.data = input[0..],
|
||||
.Image => |*img| img.driver_image = @ptrFromInt(std.mem.bytesToValue(usize, input[0..])),
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn getPlainMemorySize(self: *const Self) RuntimeError!usize {
|
||||
return switch (self.*) {
|
||||
.Bool => 1,
|
||||
.Int => |i| @divExact(i.bit_count, 8),
|
||||
.Float => |f| @divExact(f.bit_count, 8),
|
||||
.Vector4f32, .Vector4i32, .Vector4u32 => 4 * 4,
|
||||
.Vector3f32, .Vector3i32, .Vector3u32 => 3 * 4,
|
||||
.Vector2f32, .Vector2i32, .Vector2u32 => 2 * 4,
|
||||
.Vector, .Matrix => |values| blk: {
|
||||
var size: usize = 0;
|
||||
for (values) |v| {
|
||||
size += try v.getPlainMemorySize();
|
||||
}
|
||||
break :blk size;
|
||||
},
|
||||
.Array => |arr| arr.stride * arr.values.len,
|
||||
.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();
|
||||
}
|
||||
break :blk size;
|
||||
},
|
||||
.RuntimeArray => |arr| arr.getLen(),
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn getLaneCount(self: *const Self) RuntimeError!usize {
|
||||
return switch (self.*) {
|
||||
.Vector => |lanes| lanes.len,
|
||||
.Vector2i32, .Vector2u32, .Vector2f32 => 2,
|
||||
.Vector3i32, .Vector3u32, .Vector3f32 => 3,
|
||||
.Vector4i32, .Vector4u32, .Vector4f32 => 4,
|
||||
.Int, .Float, .Bool => 1,
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn isScalar(self: *const Self) bool {
|
||||
return switch (self.*) {
|
||||
.Bool, .Int, .Float => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn isVector(self: *const Self) bool {
|
||||
return switch (self.*) {
|
||||
.Vector,
|
||||
.Vector2i32,
|
||||
.Vector2u32,
|
||||
.Vector2f32,
|
||||
.Vector3i32,
|
||||
.Vector3u32,
|
||||
.Vector3f32,
|
||||
.Vector4i32,
|
||||
.Vector4u32,
|
||||
.Vector4f32,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn flushPtr(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
switch (self.*) {
|
||||
.Pointer => |*p| {
|
||||
if (p.uniform_slice_window) |window| {
|
||||
switch (p.ptr) {
|
||||
.common => |ptr| {
|
||||
_ = try ptr.read(window);
|
||||
ptr.deinit(allocator);
|
||||
if (p.is_owner_of_uniform_slice)
|
||||
allocator.destroy(ptr);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
p.uniform_slice_window = null;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
switch (self.*) {
|
||||
.Vector, .Matrix => |*values| {
|
||||
for (values.*) |*value| value.deinit(allocator);
|
||||
allocator.free(values.*);
|
||||
},
|
||||
.Array => |*arr| {
|
||||
for (arr.values) |*value| value.deinit(allocator);
|
||||
allocator.free(arr.values);
|
||||
},
|
||||
.Structure => |*s| {
|
||||
for (s.values) |*value| value.deinit(allocator);
|
||||
allocator.free(s.values);
|
||||
allocator.free(s.offsets);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn readLane(comptime T: PrimitiveType, comptime bits: u32, v: *const Value, lane_index: usize) RuntimeError!getPrimitiveFieldType(T, bits) {
|
||||
const TT = getPrimitiveFieldType(T, bits);
|
||||
|
||||
return switch (v.*) {
|
||||
.Int => (try getPrimitiveField(T, bits, @constCast(v))).*,
|
||||
|
||||
.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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn writeLane(comptime T: PrimitiveType, comptime bits: u32, dst: *Value, lane_index: usize, value: getPrimitiveFieldType(T, bits)) RuntimeError!void {
|
||||
switch (dst.*) {
|
||||
.Int => (try getPrimitiveField(T, bits, dst)).* = value,
|
||||
|
||||
.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,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
.Vector3i32 => |*vec| switch (lane_index) {
|
||||
inline 0...2 => |i| vec[i] = if (bits == 32) @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,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
|
||||
.Vector2u32 => |*vec| switch (lane_index) {
|
||||
inline 0...1 => |i| vec[i] = if (bits == 32) @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,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
.Vector4u32 => |*vec| switch (lane_index) {
|
||||
inline 0...3 => |i| vec[i] = if (bits == 32) @bitCast(value) else return RuntimeError.InvalidSpirV,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
}
|
||||
}
|
||||
|
||||
fn setScalarLaneValue(comptime value_type: PrimitiveType, comptime bits: u32, dst: *Value, v: getPrimitiveFieldType(value_type, bits)) RuntimeError!void {
|
||||
switch (bits) {
|
||||
inline 8, 16, 32, 64 => {
|
||||
dst.* = .{ .Int = .{
|
||||
.bit_count = bits,
|
||||
.is_signed = if (value_type == .SInt) true else false,
|
||||
.value = switch (value_type) {
|
||||
.SInt => switch (bits) {
|
||||
8 => .{ .sint8 = v },
|
||||
16 => .{ .sint16 = v },
|
||||
32 => .{ .sint32 = v },
|
||||
64 => .{ .sint64 = v },
|
||||
else => unreachable,
|
||||
},
|
||||
.UInt => switch (bits) {
|
||||
8 => .{ .uint8 = v },
|
||||
16 => .{ .uint16 = v },
|
||||
32 => .{ .uint32 = v },
|
||||
64 => .{ .uint64 = v },
|
||||
else => unreachable,
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
} };
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
.common => |value| getPrimitiveField(T, BitCount, value),
|
||||
.f32_ptr => |ptr| @ptrCast(@alignCast(ptr)),
|
||||
.u32_ptr => |ptr| @ptrCast(@alignCast(ptr)),
|
||||
.i32_ptr => |ptr| @ptrCast(@alignCast(ptr)),
|
||||
};
|
||||
}
|
||||
return switch (T) {
|
||||
.Bool => &v.Bool,
|
||||
.Float => switch (BitCount) {
|
||||
inline 16, 32, 64 => |i| &@field(v.Float.value, std.fmt.comptimePrint("float{}", .{i})),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
.SInt => switch (BitCount) {
|
||||
inline 8, 16, 32, 64 => |i| &@field(v.Int.value, std.fmt.comptimePrint("sint{}", .{i})),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
.UInt => switch (BitCount) {
|
||||
inline 8, 16, 32, 64 => |i| &@field(v.Int.value, std.fmt.comptimePrint("uint{}", .{i})),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getPrimitiveFieldType(comptime T: PrimitiveType, comptime BitCount: SpvWord) type {
|
||||
return switch (T) {
|
||||
.Bool => bool,
|
||||
.Float => std.meta.Float(BitCount),
|
||||
.SInt => std.meta.Int(.signed, BitCount),
|
||||
.UInt => std.meta.Int(.unsigned, BitCount),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resolveLaneBitWidth(self: *const Self) RuntimeError!SpvWord {
|
||||
return switch (self.*) {
|
||||
.Bool => 8,
|
||||
.Float => |f| f.bit_length,
|
||||
.Int => |i| i.bit_length,
|
||||
.Vector => |v| v[0].resolveLaneBitWidth(),
|
||||
.Vector4f32,
|
||||
.Vector3f32,
|
||||
.Vector2f32,
|
||||
.Vector4i32,
|
||||
.Vector3i32,
|
||||
.Vector2i32,
|
||||
.Vector4u32,
|
||||
.Vector3u32,
|
||||
.Vector2u32,
|
||||
=> return 32,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resolveLaneCount(self: *const Self) RuntimeError!SpvWord {
|
||||
return switch (self.*) {
|
||||
.Bool, .Float, .Int => 1,
|
||||
.Vector => |v| @intCast(v.len),
|
||||
.Vector4f32, .Vector4i32, .Vector4u32 => 4,
|
||||
.Vector3f32, .Vector3i32, .Vector3u32 => 3,
|
||||
.Vector2f32, .Vector2i32, .Vector2u32 => 2,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resolveSign(self: *const Self) RuntimeError!enum { signed, unsigned } {
|
||||
return switch (self.*) {
|
||||
.Int => |i| if (i.is_signed) .signed else .unsigned,
|
||||
.Vector => |v| v[0].resolveSign(),
|
||||
.Vector4i32 => .signed,
|
||||
.Vector3i32 => .signed,
|
||||
.Vector2i32 => .signed,
|
||||
.Vector4u32 => .unsigned,
|
||||
.Vector3u32 => .unsigned,
|
||||
.Vector2u32 => .unsigned,
|
||||
else => .unsigned,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -10,12 +10,14 @@ const Self = @This();
|
||||
buffer: []const SpvWord,
|
||||
index: usize,
|
||||
did_jump: bool,
|
||||
next_force_skip: ?usize,
|
||||
|
||||
pub fn init(buffer: []const SpvWord) Self {
|
||||
return .{
|
||||
.buffer = buffer,
|
||||
.index = 0,
|
||||
.did_jump = false,
|
||||
.next_force_skip = null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,15 +27,37 @@ pub inline fn nextOrNull(self: *Self) ?SpvWord {
|
||||
return word;
|
||||
}
|
||||
|
||||
/// self.index + index will be automatically skipped
|
||||
pub inline fn forceSkipIndex(self: *Self, index: SpvWord) void {
|
||||
self.next_force_skip = self.index + index;
|
||||
}
|
||||
|
||||
pub inline fn nextAsOrNull(self: *Self, comptime E: type) ?E {
|
||||
if (self.next_force_skip) |skip_index| {
|
||||
if (self.index == skip_index) {
|
||||
_ = self.skip();
|
||||
self.next_force_skip = null;
|
||||
}
|
||||
}
|
||||
return if (self.nextOrNull()) |word| std.enums.fromInt(E, word) else null;
|
||||
}
|
||||
|
||||
pub inline fn next(self: *Self) RuntimeError!SpvWord {
|
||||
if (self.next_force_skip) |skip_index| {
|
||||
if (self.index == skip_index) {
|
||||
_ = self.skip();
|
||||
self.next_force_skip = null;
|
||||
}
|
||||
}
|
||||
return self.nextOrNull() orelse return RuntimeError.InvalidSpirV;
|
||||
}
|
||||
|
||||
pub inline fn nextAs(self: *Self, comptime E: type) RuntimeError!E {
|
||||
if (self.next_force_skip) |skip_index| {
|
||||
if (self.index == skip_index) {
|
||||
_ = self.skip();
|
||||
}
|
||||
}
|
||||
return self.nextAsOrNull(E) orelse return RuntimeError.InvalidSpirV;
|
||||
}
|
||||
|
||||
@@ -59,6 +83,7 @@ pub inline fn skipN(self: *Self, count: usize) bool {
|
||||
|
||||
pub inline fn skipToEnd(self: *Self) void {
|
||||
self.index = self.buffer.len;
|
||||
self.did_jump = true;
|
||||
}
|
||||
|
||||
pub inline fn emitSourceLocation(self: *const Self) usize {
|
||||
@@ -71,3 +96,10 @@ pub inline fn jumpToSourceLocation(self: *Self, source_location: usize) bool {
|
||||
self.did_jump = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Like jumpToSourceLocation without toggling self.did_jump
|
||||
pub inline fn goToSourceLocation(self: *Self, source_location: usize) bool {
|
||||
if (source_location > self.buffer.len) return false;
|
||||
self.index = source_location;
|
||||
return true;
|
||||
}
|
||||
|
||||
+14
-3
@@ -21,7 +21,7 @@
|
||||
//!
|
||||
//! try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
//! var output: [4]f32 = undefined;
|
||||
//! try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color"));
|
||||
//! try rt.readOutput(std.mem.asBytes(output[0..output.len]), try rt.getResultByName("color"));
|
||||
//! std.log.info("Output: Vec4{any}", .{output});
|
||||
//! }
|
||||
//! std.log.info("Successfully executed", .{});
|
||||
@@ -30,18 +30,29 @@
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Image = @import("Image.zig");
|
||||
pub const Module = @import("Module.zig");
|
||||
pub const Runtime = @import("Runtime.zig");
|
||||
|
||||
const opcodes = @import("opcodes.zig");
|
||||
const spv = @import("spv.zig");
|
||||
pub const spv = @import("spv.zig");
|
||||
|
||||
pub const SpvVoid = spv.SpvVoid;
|
||||
pub const SpvByte = spv.SpvByte;
|
||||
pub const SpvWord = spv.SpvWord;
|
||||
pub const SpvBool = spv.SpvBool;
|
||||
|
||||
pub const Vec4f32 = @Vector(4, f32);
|
||||
pub const Vec3f32 = @Vector(3, f32);
|
||||
pub const Vec2f32 = @Vector(2, f32);
|
||||
|
||||
pub const Vec4i32 = @Vector(4, i32);
|
||||
pub const Vec3i32 = @Vector(3, i32);
|
||||
pub const Vec2i32 = @Vector(2, i32);
|
||||
|
||||
pub const Vec4u32 = @Vector(4, u32);
|
||||
pub const Vec3u32 = @Vector(3, u32);
|
||||
pub const Vec2u32 = @Vector(2, u32);
|
||||
|
||||
pub const GLSL_std_450 = @import("GLSL_std_450/opcodes.zig");
|
||||
|
||||
/// Maximum number of input locations per module
|
||||
|
||||
+1804
-492
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -26,5 +26,10 @@ test "Simple array" {
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expectOutput(f32, 4, code, "color", &.{ 4, 3, 2, 1 });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]f32{ 4, 3, 2, 1 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+6
-1
@@ -25,5 +25,10 @@ test "Simple fragment shader" {
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expectOutput(f32, 4, code, "color", &.{ 4, 3, 2, 1 });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]f32{ 4, 3, 2, 1 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+13
-3
@@ -73,7 +73,12 @@ test "Bitwise primitives" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T, 4, code, "color", &.{ expected, expected, expected, expected });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]T{ expected, expected, expected, expected }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,7 +101,7 @@ test "Bitwise vectors" {
|
||||
inline for (types) |T| {
|
||||
const op1: case.Vec(L, T) = .{ .val = case.random(@Vector(L, T)) };
|
||||
var op2: case.Vec(L, T) = .{ .val = case.random(@Vector(L, T)) };
|
||||
for (0..L) |i| op2.val[i] = @mod(op2.val[i], @bitSizeOf(T));
|
||||
inline for (0..L) |i| op2.val[i] = @mod(op2.val[i], @bitSizeOf(T));
|
||||
const expected = switch (op.key) {
|
||||
.BitwiseAnd => op1.val & op2.val,
|
||||
.BitwiseOr => op1.val | op2.val,
|
||||
@@ -142,7 +147,12 @@ test "Bitwise vectors" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T, L, code, "color", &@as([L]T, expected));
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&@as([L]T, expected)),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -93,7 +93,12 @@ test "Simple branching" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T, 4, code, "color", &.{ expected, expected, expected, expected });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]T{ expected, expected, expected, expected }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-2
@@ -55,7 +55,12 @@ test "Primitives casts" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T[1], 4, code, "color", &.{ expected, expected, expected, expected });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]T[1]{ expected, expected, expected, expected }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +108,11 @@ test "Primitives bitcasts" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T[1], 4, code, "color", &.{ expected, expected, expected, expected });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]T[1]{ expected, expected, expected, expected }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+12
-2
@@ -44,7 +44,12 @@ test "Simple function calls" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T, 4, code, "color", &.{ n, n, n, n });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]T{ n, n, n, n }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +100,11 @@ test "Nested function calls" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T, 4, code, "color", &.{ n, n, n, n });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]T{ n, n, n, n }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -45,7 +45,15 @@ test "Inputs" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutputWithInput(T, L, code, "color", &@as([L]T, input.val), "pos", &@as([L]T, input.val));
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.inputs = &.{
|
||||
std.mem.asBytes(&@as([L]T, input.val)),
|
||||
},
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&@as([L]T, input.val)),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -47,5 +47,10 @@ test "Simple while loop" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(f32, 4, code, "color", &.{ expected, expected, expected, expected });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]f32{ expected, expected, expected, expected }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+12
-2
@@ -72,7 +72,12 @@ test "Maths primitives" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T, 4, code, "color", &.{ expected, expected, expected, expected });
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]T{ expected, expected, expected, expected }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +144,12 @@ test "Maths vectors" {
|
||||
defer allocator.free(shader);
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
try case.expectOutput(T, L, code, "color", &@as([L]T, expected));
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&@as([L]T, expected)),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-35
@@ -20,34 +20,15 @@ pub fn compileNzsl(allocator: std.mem.Allocator, source: []const u8) ![]const u3
|
||||
}
|
||||
|
||||
pub const case = struct {
|
||||
pub fn expectOutput(comptime T: type, comptime len: usize, source: []const u32, output_name: []const u8, expected: []const T) !void {
|
||||
const allocator = std.testing.allocator;
|
||||
pub const Config = struct {
|
||||
source: []const u32,
|
||||
inputs: []const []const u8 = &.{},
|
||||
expected_outputs: []const []const u8 = &.{},
|
||||
descriptor_sets: []const []const []u8 = &.{},
|
||||
expected_descriptor_sets: []const []const []const u8 = &.{},
|
||||
};
|
||||
|
||||
const module_options = [_]spv.Module.ModuleOptions{
|
||||
.{
|
||||
.use_simd_vectors_specializations = true,
|
||||
},
|
||||
.{
|
||||
.use_simd_vectors_specializations = false,
|
||||
},
|
||||
};
|
||||
|
||||
for (module_options) |opt| {
|
||||
var module = try spv.Module.init(allocator, source, opt);
|
||||
defer module.deinit(allocator);
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &module);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
var output: [len]T = undefined;
|
||||
try rt.readOutput(std.mem.sliceAsBytes(output[0..]), try rt.getResultByName(output_name));
|
||||
|
||||
try std.testing.expectEqualSlices(T, expected, &output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expectOutputWithInput(comptime T: type, comptime len: usize, source: []const u32, output_name: []const u8, expected: []const T, input_name: []const u8, input: []const T) !void {
|
||||
pub fn expect(config: Config) !void {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
// To test with all important module options
|
||||
@@ -61,24 +42,43 @@ pub const case = struct {
|
||||
};
|
||||
|
||||
for (module_options) |opt| {
|
||||
var module = try spv.Module.init(allocator, source, opt);
|
||||
var module = try spv.Module.init(allocator, config.source, opt);
|
||||
defer module.deinit(allocator);
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &module);
|
||||
var rt = try spv.Runtime.init(allocator, &module, undefined);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
try rt.writeInput(std.mem.sliceAsBytes(input[0..len]), try rt.getResultByName(input_name));
|
||||
for (config.inputs, 0..) |input, n| {
|
||||
try rt.writeInput(input[0..], module.input_locations[n]);
|
||||
}
|
||||
|
||||
for (config.descriptor_sets, 0..) |descriptor_set, set_index| {
|
||||
for (descriptor_set, 0..) |descriptor_binding, binding_index| {
|
||||
try rt.writeDescriptorSet(descriptor_binding, @intCast(set_index), @intCast(binding_index), 0);
|
||||
}
|
||||
}
|
||||
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
var output: [len]T = undefined;
|
||||
try rt.readOutput(std.mem.sliceAsBytes(output[0..]), try rt.getResultByName(output_name));
|
||||
try rt.flushDescriptorSets(allocator);
|
||||
|
||||
try std.testing.expectEqualSlices(T, expected, &output);
|
||||
for (config.expected_outputs, 0..) |expected, n| {
|
||||
const output = try allocator.alloc(u8, expected.len);
|
||||
defer allocator.free(output);
|
||||
|
||||
try rt.readOutput(output[0..], module.output_locations[n]);
|
||||
try std.testing.expectEqualSlices(u8, expected, output);
|
||||
}
|
||||
|
||||
for (config.expected_descriptor_sets, config.descriptor_sets) |expected_descriptor_set, descriptor_set| {
|
||||
for (expected_descriptor_set, descriptor_set) |expected_descriptor_binding, descriptor_binding| {
|
||||
try std.testing.expectEqualSlices(u8, expected_descriptor_binding, descriptor_binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random(comptime T: type) T {
|
||||
var prng: std.Random.DefaultPrng = .init(@intCast(std.time.microTimestamp()));
|
||||
var prng: std.Random.DefaultPrng = .init(@intCast(std.Io.Timestamp.now(std.testing.io, .real).toMicroseconds()));
|
||||
const rand = prng.random();
|
||||
|
||||
return switch (@typeInfo(T)) {
|
||||
@@ -86,7 +86,7 @@ pub const case = struct {
|
||||
.float => rand.float(T),
|
||||
.vector => |v| blk: {
|
||||
var vec: @Vector(v.len, v.child) = undefined;
|
||||
for (0..v.len) |i| {
|
||||
inline for (0..v.len) |i| {
|
||||
vec[i] = random(v.child);
|
||||
}
|
||||
break :blk vec;
|
||||
@@ -119,4 +119,5 @@ test {
|
||||
std.testing.refAllDecls(@import("inputs.zig"));
|
||||
std.testing.refAllDecls(@import("loops.zig"));
|
||||
std.testing.refAllDecls(@import("maths.zig"));
|
||||
std.testing.refAllDecls(@import("ssbo.zig"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root.zig");
|
||||
const compileNzsl = root.compileNzsl;
|
||||
const case = root.case;
|
||||
|
||||
test "Simple SSBO" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ [layout(std430)]
|
||||
\\ struct SSBO
|
||||
\\ {
|
||||
\\ data: dyn_array[u32]
|
||||
\\ }
|
||||
\\
|
||||
\\ external
|
||||
\\ {
|
||||
\\ [set(0), binding(0)] ssbo: storage[SSBO],
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(compute)]
|
||||
\\ [workgroup(16, 1, 1)]
|
||||
\\ fn main()
|
||||
\\ {
|
||||
\\ for i in 0 -> 256
|
||||
\\ {
|
||||
\\ ssbo.data[i] = u32(i);
|
||||
\\ }
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
var ssbo = [_]u32{0} ** 256;
|
||||
|
||||
var expected = [_]u32{0} ** 256;
|
||||
for (expected[0..], 0..) |*val, i| {
|
||||
val.* = @intCast(i);
|
||||
}
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.descriptor_sets = &.{
|
||||
// Set 0
|
||||
&.{
|
||||
// Binding 0
|
||||
std.mem.asBytes(&ssbo),
|
||||
},
|
||||
},
|
||||
.expected_descriptor_sets = &.{
|
||||
// Set 0
|
||||
&.{
|
||||
// Binding 0
|
||||
std.mem.asBytes(&expected),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
+21
-76
@@ -16,11 +16,8 @@ pub fn main() !void {
|
||||
|
||||
const allocator = fba.allocator();
|
||||
|
||||
const env = Env.init(allocator);
|
||||
defer env.deinit(allocator);
|
||||
|
||||
var slowest = SlowTracker.init(allocator, 5);
|
||||
defer slowest.deinit();
|
||||
defer slowest.deinit(allocator);
|
||||
|
||||
var pass: usize = 0;
|
||||
var fail: usize = 0;
|
||||
@@ -46,13 +43,6 @@ pub fn main() !void {
|
||||
var status = Status.pass;
|
||||
slowest.startTiming();
|
||||
|
||||
const is_unnamed_test = isUnnamed(t);
|
||||
if (env.filter) |f| {
|
||||
if (!is_unnamed_test and std.mem.indexOf(u8, t.name, f) == null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const friendly_name = blk: {
|
||||
const name = t.name;
|
||||
var it = std.mem.splitScalar(u8, name, '.');
|
||||
@@ -70,7 +60,7 @@ pub fn main() !void {
|
||||
const result = t.func();
|
||||
current_test = null;
|
||||
|
||||
const ns_taken = slowest.endTiming(friendly_name);
|
||||
const ns_taken = slowest.endTiming(allocator, friendly_name);
|
||||
|
||||
if (std.testing.allocator_instance.deinit() == .leak) {
|
||||
leak += 1;
|
||||
@@ -89,20 +79,13 @@ pub fn main() !void {
|
||||
fail += 1;
|
||||
Printer.status(.fail, "\n{s}\n\"{s}\" - {s}\n{s}\n", .{ BORDER, friendly_name, @errorName(err), BORDER });
|
||||
if (@errorReturnTrace()) |trace| {
|
||||
std.debug.dumpStackTrace(trace.*);
|
||||
}
|
||||
if (env.fail_first) {
|
||||
break;
|
||||
std.debug.dumpErrorReturnTrace(trace);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if (env.verbose) {
|
||||
const ms = @as(f64, @floatFromInt(ns_taken)) / 1_000_000.0;
|
||||
Printer.status(status, "\x1b[35m[{d: >10.2} ms]\x1b[0m {s: <30}", .{ ms, friendly_name });
|
||||
} else {
|
||||
Printer.status(status, ".", .{});
|
||||
}
|
||||
const ms = @as(f64, @floatFromInt(ns_taken)) / 1_000_000.0;
|
||||
Printer.status(status, "\x1b[35m[{d: >10.2} ms]\x1b[0m {s: <30}", .{ ms, friendly_name });
|
||||
}
|
||||
|
||||
for (builtin.test_functions) |t| {
|
||||
@@ -126,7 +109,7 @@ pub fn main() !void {
|
||||
Printer.fmt("\n", .{});
|
||||
try slowest.display();
|
||||
Printer.fmt("\n", .{});
|
||||
std.posix.exit(if (fail == 0) 0 else 1);
|
||||
std.process.exit(if (fail == 0) 0 else 1);
|
||||
}
|
||||
|
||||
const Printer = struct {
|
||||
@@ -155,42 +138,41 @@ const SlowTracker = struct {
|
||||
const SlowestQueue = std.PriorityDequeue(TestInfo, void, compareTiming);
|
||||
max: usize,
|
||||
slowest: SlowestQueue,
|
||||
timer: std.time.Timer,
|
||||
timer: std.Io.Timestamp,
|
||||
|
||||
fn init(allocator: Allocator, count: u32) SlowTracker {
|
||||
const timer = std.time.Timer.start() catch @panic("failed to start timer");
|
||||
var slowest = SlowestQueue.init(allocator, {});
|
||||
slowest.ensureTotalCapacity(count) catch @panic("OOM");
|
||||
var slowest = SlowestQueue.empty;
|
||||
slowest.ensureTotalCapacity(allocator, count) catch @panic("OOM");
|
||||
return .{
|
||||
.max = count,
|
||||
.timer = timer,
|
||||
.timer = std.Io.Timestamp.now(std.testing.io, .real),
|
||||
.slowest = slowest,
|
||||
};
|
||||
}
|
||||
|
||||
const TestInfo = struct {
|
||||
ns: u64,
|
||||
ns: i96,
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
fn deinit(self: SlowTracker) void {
|
||||
self.slowest.deinit();
|
||||
fn deinit(self: *SlowTracker, allocator: std.mem.Allocator) void {
|
||||
self.slowest.deinit(allocator);
|
||||
}
|
||||
|
||||
fn startTiming(self: *SlowTracker) void {
|
||||
self.timer.reset();
|
||||
self.timer = std.Io.Timestamp.now(std.testing.io, .real);
|
||||
}
|
||||
|
||||
fn endTiming(self: *SlowTracker, test_name: []const u8) u64 {
|
||||
var timer = self.timer;
|
||||
const ns = timer.lap();
|
||||
fn endTiming(self: *SlowTracker, allocator: std.mem.Allocator, test_name: []const u8) i96 {
|
||||
const duration = self.timer.untilNow(std.testing.io, .real);
|
||||
const ns = duration.toNanoseconds();
|
||||
|
||||
var slowest = &self.slowest;
|
||||
|
||||
if (slowest.count() < self.max) {
|
||||
// Capacity is fixed to the # of slow tests we want to track
|
||||
// If we've tracked fewer tests than this capacity, than always add
|
||||
slowest.add(TestInfo{ .ns = ns, .name = test_name }) catch @panic("failed to track test timing");
|
||||
slowest.push(allocator, TestInfo{ .ns = ns, .name = test_name }) catch @panic("failed to track test timing");
|
||||
return ns;
|
||||
}
|
||||
|
||||
@@ -205,8 +187,8 @@ const SlowTracker = struct {
|
||||
}
|
||||
|
||||
// the previous fastest of our slow tests, has been pushed off.
|
||||
_ = slowest.removeMin();
|
||||
slowest.add(TestInfo{ .ns = ns, .name = test_name }) catch @panic("failed to track test timing");
|
||||
_ = slowest.popMin();
|
||||
slowest.push(allocator, TestInfo{ .ns = ns, .name = test_name }) catch @panic("failed to track test timing");
|
||||
return ns;
|
||||
}
|
||||
|
||||
@@ -214,7 +196,7 @@ const SlowTracker = struct {
|
||||
var slowest = self.slowest;
|
||||
const count = slowest.count();
|
||||
Printer.fmt("Slowest {d} test{s}: \n", .{ count, if (count != 1) "s" else "" });
|
||||
while (slowest.removeMinOrNull()) |info| {
|
||||
while (slowest.popMin()) |info| {
|
||||
const ms = @as(f64, @floatFromInt(info.ns)) / 1_000_000.0;
|
||||
Printer.fmt(" {d:.2}ms\t{s}\n", .{ ms, info.name });
|
||||
}
|
||||
@@ -226,43 +208,6 @@ const SlowTracker = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const Env = struct {
|
||||
verbose: bool,
|
||||
fail_first: bool,
|
||||
filter: ?[]const u8,
|
||||
|
||||
fn init(allocator: Allocator) Env {
|
||||
return .{
|
||||
.verbose = readEnvBool(allocator, "TEST_VERBOSE", true),
|
||||
.fail_first = readEnvBool(allocator, "TEST_FAIL_FIRST", false),
|
||||
.filter = readEnv(allocator, "TEST_FILTER"),
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: Env, allocator: Allocator) void {
|
||||
if (self.filter) |f| {
|
||||
allocator.free(f);
|
||||
}
|
||||
}
|
||||
|
||||
fn readEnv(allocator: Allocator, key: []const u8) ?[]const u8 {
|
||||
const v = std.process.getEnvVarOwned(allocator, key) catch |err| {
|
||||
if (err == error.EnvironmentVariableNotFound) {
|
||||
return null;
|
||||
}
|
||||
std.log.warn("failed to get env var {s} due to err {}", .{ key, err });
|
||||
return null;
|
||||
};
|
||||
return v;
|
||||
}
|
||||
|
||||
fn readEnvBool(allocator: Allocator, key: []const u8, deflt: bool) bool {
|
||||
const value = readEnv(allocator, key) orelse return deflt;
|
||||
defer allocator.free(value);
|
||||
return std.ascii.eqlIgnoreCase(value, "true");
|
||||
}
|
||||
};
|
||||
|
||||
pub const panic = std.debug.FullPanic(struct {
|
||||
pub fn panicFn(msg: []const u8, first_trace_addr: ?usize) noreturn {
|
||||
if (current_test) |ct| {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <stdio.h>
|
||||
#include <SpirvInterpreter.h>
|
||||
|
||||
static const unsigned char shader_source[] = {
|
||||
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x82, 0x10, 0x27, 0x00, 0x17, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x0f, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
|
||||
0x09, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x04, 0x00, 0x05, 0x00, 0x56, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
|
||||
0x07, 0x00, 0x00, 0x00, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00,
|
||||
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00,
|
||||
0x05, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00,
|
||||
0x05, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
|
||||
0x47, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x48, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||
0x15, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x2b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x2b, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
|
||||
0x2b, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
|
||||
0x2b, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40,
|
||||
0x2b, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40,
|
||||
0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x3b, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x36, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
|
||||
0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
||||
0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||
0x13, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
|
||||
0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||
0x15, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
|
||||
0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
SpvModule module;
|
||||
SpvModuleOptions options;
|
||||
options.use_simd_vectors_specializations = 1;
|
||||
|
||||
if(SpvInitModule(&module, (SpvWord*)shader_source, sizeof(shader_source) / 4, options) != SPV_RESULT_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "Module init failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
SpvRuntime runtime;
|
||||
if(SpvInitRuntime(&runtime, module, (SpvImageAPI){0}) != SPV_RESULT_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "Runtime init failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
SpvWord main_entry_index;
|
||||
SpvGetEntryPointByName(runtime, "main", &main_entry_index);
|
||||
SpvCallEntryPoint(runtime, main_entry_index);
|
||||
|
||||
float output[4];
|
||||
SpvWord output_result;
|
||||
SpvGetResultByName(runtime, "color", &output_result);
|
||||
SpvReadOutput(runtime, (SpvByte*)output, sizeof(output), output_result);
|
||||
|
||||
printf("Output: Vec4[%f, %f, %f, %f]\n", output[0], output[1], output[2], output[3]);
|
||||
|
||||
SpvDeinitRuntime(runtime);
|
||||
SpvDeinitModule(module);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user