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