Compare commits
3 Commits
8566345f5e
...
d2a246fce0
Author | SHA1 | Date | |
---|---|---|---|
d2a246fce0 | |||
e1d41ded4a | |||
8dacc9b080 |
185
build.zig
185
build.zig
@ -1,79 +1,12 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const CommonArgs = struct {
|
const BuildConfig = struct {
|
||||||
target: std.Build.ResolvedTarget,
|
module_target: std.Build.ResolvedTarget,
|
||||||
|
spirv_target: std.Build.ResolvedTarget,
|
||||||
optimize: std.builtin.OptimizeMode,
|
optimize: std.builtin.OptimizeMode,
|
||||||
ona_module: *std.Build.Module,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
fn scan_demos(self: BuildConfig, ona_module: *std.Build.Module) void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const b = ona_module.owner;
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
|
||||||
|
|
||||||
const coral_module = b.addModule("coral", .{
|
|
||||||
.root_source_file = b.path("src/coral/coral.zig"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ext_module = b.createModule(.{
|
|
||||||
.root_source_file = b.path("src/ext/ext.zig"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
.link_libc = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
ext_module.linkSystemLibrary("SDL3", .{
|
|
||||||
.needed = true,
|
|
||||||
.preferred_link_mode = .dynamic,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ona_module = b.addModule("ona", .{
|
|
||||||
.root_source_file = b.path("src/ona/ona.zig"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
.link_libc = true,
|
|
||||||
|
|
||||||
.imports = &.{
|
|
||||||
.{
|
|
||||||
.name = "ext",
|
|
||||||
.module = ext_module,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.name = "coral",
|
|
||||||
.module = coral_module,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
scan_demos(b, .{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
.ona_module = ona_module,
|
|
||||||
});
|
|
||||||
|
|
||||||
const test_tests = b.addTest(.{
|
|
||||||
.root_module = b.createModule(.{
|
|
||||||
.root_source_file = b.path("src/tests.zig"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
|
|
||||||
.imports = &.{
|
|
||||||
.{
|
|
||||||
.name = "coral",
|
|
||||||
.module = coral_module,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const test_step = b.step("test", "Run unit tests");
|
|
||||||
|
|
||||||
test_step.dependOn(&b.addRunArtifact(test_tests).step);
|
|
||||||
test_step.dependOn(&b.addInstallArtifact(test_tests, .{}).step);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scan_demos(b: *std.Build, common: CommonArgs) void {
|
|
||||||
const build_demos_step = b.step("demos", "Build demos");
|
const build_demos_step = b.step("demos", "Build demos");
|
||||||
|
|
||||||
b.default_step.dependOn(build_demos_step);
|
b.default_step.dependOn(build_demos_step);
|
||||||
@ -108,13 +41,13 @@ fn scan_demos(b: *std.Build, common: CommonArgs) void {
|
|||||||
|
|
||||||
.root_module = b.createModule(.{
|
.root_module = b.createModule(.{
|
||||||
.root_source_file = b.path(b.pathJoin(&.{ "src/demos/", entry.name })),
|
.root_source_file = b.path(b.pathJoin(&.{ "src/demos/", entry.name })),
|
||||||
.target = common.target,
|
.target = self.module_target,
|
||||||
.optimize = common.optimize,
|
.optimize = self.optimize,
|
||||||
|
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{
|
.{
|
||||||
.name = "ona",
|
.name = "ona",
|
||||||
.module = common.ona_module,
|
.module = ona_module,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -130,3 +63,105 @@ fn scan_demos(b: *std.Build, common: CommonArgs) void {
|
|||||||
build_demos_step.dependOn(&demo_installation.step);
|
build_demos_step.dependOn(&demo_installation.step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Revisit Zig shaders once the SPIRV target becomes more mature.
|
||||||
|
// pub fn addShaders(self: BuildConfig, module: *std.Build.Module, shader_paths: []const []const u8) void {
|
||||||
|
// const b = module.owner;
|
||||||
|
|
||||||
|
// for (shader_paths) |shader_path| {
|
||||||
|
// const shader_path_stem = std.fs.path.stem(shader_path);
|
||||||
|
|
||||||
|
// const module_identifier = std.mem.join(b.allocator, ".", &.{ shader_path_stem, "spirv" }) catch {
|
||||||
|
// @panic("OOM");
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const spirv_object = b.addObject(.{
|
||||||
|
// .name = module_identifier,
|
||||||
|
// .root_source_file = b.path(shader_path),
|
||||||
|
// .target = self.spirv_target,
|
||||||
|
// .use_llvm = false,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// module.addAnonymousImport(module_identifier, .{ .root_source_file = spirv_object.getEmittedBin() });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
const CommonArgs = struct {
|
||||||
|
target: std.Build.ResolvedTarget,
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
ona_module: *std.Build.Module,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const config = BuildConfig{
|
||||||
|
.optimize = b.standardOptimizeOption(.{}),
|
||||||
|
.module_target = b.standardTargetOptions(.{}),
|
||||||
|
|
||||||
|
.spirv_target = b.resolveTargetQuery(.{
|
||||||
|
.cpu_arch = .spirv64,
|
||||||
|
.os_tag = .vulkan,
|
||||||
|
.cpu_model = .{ .explicit = &std.Target.spirv.cpu.vulkan_v1_2 },
|
||||||
|
.cpu_features_add = std.Target.spirv.featureSet(&.{.int64}),
|
||||||
|
.ofmt = .spirv,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const shaderc_dependency = b.dependency("shaderc_zig", .{});
|
||||||
|
|
||||||
|
const sdl_dependency = b.dependency("sdl", .{
|
||||||
|
.target = config.module_target,
|
||||||
|
.optimize = config.optimize,
|
||||||
|
.preferred_linkage = .static,
|
||||||
|
});
|
||||||
|
|
||||||
|
const coral_module = b.addModule("coral", .{
|
||||||
|
.root_source_file = b.path("src/coral/coral.zig"),
|
||||||
|
.target = config.module_target,
|
||||||
|
.optimize = config.optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const ona_module = b.addModule("ona", .{
|
||||||
|
.root_source_file = b.path("src/ona/ona.zig"),
|
||||||
|
.target = config.module_target,
|
||||||
|
.optimize = config.optimize,
|
||||||
|
.link_libc = true,
|
||||||
|
|
||||||
|
.imports = &.{
|
||||||
|
.{
|
||||||
|
.name = "coral",
|
||||||
|
.module = coral_module,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ona_module.linkLibrary(shaderc_dependency.artifact("shaderc"));
|
||||||
|
ona_module.linkLibrary(sdl_dependency.artifact("SDL3"));
|
||||||
|
|
||||||
|
// config.addShaders(ona_module, &.{
|
||||||
|
// "./src/ona/gfx/effect_shader.zig",
|
||||||
|
// "./src/ona/gfx/effect_fragment.zig",
|
||||||
|
// });
|
||||||
|
|
||||||
|
config.scan_demos(ona_module);
|
||||||
|
|
||||||
|
const test_tests = b.addTest(.{
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.root_source_file = b.path("src/tests.zig"),
|
||||||
|
.target = config.module_target,
|
||||||
|
.optimize = config.optimize,
|
||||||
|
|
||||||
|
.imports = &.{
|
||||||
|
.{
|
||||||
|
.name = "coral",
|
||||||
|
.module = coral_module,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
|
||||||
|
test_step.dependOn(&b.addRunArtifact(test_tests).step);
|
||||||
|
test_step.dependOn(&b.addInstallArtifact(test_tests, .{}).step);
|
||||||
|
}
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
.{
|
.{
|
||||||
.name = .ona,
|
.name = .ona,
|
||||||
.version = "0.0.0",
|
.version = "0.0.0",
|
||||||
|
|
||||||
|
.dependencies = .{
|
||||||
|
.shaderc_zig = .{
|
||||||
|
.url = "git+https://github.com/tiawl/shaderc.zig#06565d2af3beec9780b11524984211ebd104fd21",
|
||||||
|
.hash = "shaderc_zig-1.0.0-mOl840tjAwBiAnMSfRskq0Iq3JJ9jPRHy2JoEgnUvSpV",
|
||||||
|
},
|
||||||
|
.sdl = .{
|
||||||
|
.url = "git+https://github.com/castholm/SDL.git#0f81c0affb2584b242b2fb5744e7dfebcfd904a5",
|
||||||
|
.hash = "sdl-0.2.6+3.2.20-7uIn9JkjfwGIQ6j3-etow2rCe-Zt16Yj-2gdp9jW7WZ9",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
.fingerprint = 0x7d0142e88b22421d,
|
.fingerprint = 0x7d0142e88b22421d,
|
||||||
.minimum_zig_version = "0.14.0",
|
.minimum_zig_version = "0.14.0",
|
||||||
|
|
||||||
|
18
readme.md
18
readme.md
@ -4,12 +4,14 @@
|
|||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
1. [Overview](#overview)
|
- [Ona](#ona)
|
||||||
1. [Goals](#goals)
|
- [Table of Contents](#table-of-contents)
|
||||||
1. [Technical Details](#technical-details)
|
- [Overview](#overview)
|
||||||
1. [Requirements](#requirements)
|
- [Goals](#goals)
|
||||||
1. [Building](#building)
|
- [Technical Details](#technical-details)
|
||||||
1. [Packaging](#packaging)
|
- [Requirements](#requirements)
|
||||||
|
- [Building](#building)
|
||||||
|
- [Packaging](#packaging)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@ -84,5 +86,5 @@ app.root_module.addImport("coral", ona_dependency.module("coral"));
|
|||||||
b.installArtifact(app);
|
b.installArtifact(app);
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Create a `main.zig` containing a valid Ona app declaration.
|
1. Create a `main.zig` containing a valid Ona app declaration.
|
||||||
6. Run `zig build` to build your new game application.
|
2. Run `zig build` to build your new game application.
|
||||||
|
@ -2,40 +2,61 @@ const coral = @import("./coral.zig");
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
delete: *const fn (*anyopaque) void,
|
destroy: *const fn (*anyopaque) void,
|
||||||
erased: *anyopaque,
|
erased: *anyopaque,
|
||||||
|
|
||||||
fn Layout(comptime Type: type) type {
|
fn Layout(comptime Type: type) type {
|
||||||
return struct {
|
return switch (Type) {
|
||||||
|
void => struct {
|
||||||
|
fn destroy(_: *anyopaque) void {}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
value: Type,
|
value: Type,
|
||||||
|
|
||||||
const TypeLayout = @This();
|
const TypeLayout = @This();
|
||||||
|
|
||||||
fn delete(erased: *anyopaque) void {
|
fn destroy(erased: *anyopaque) void {
|
||||||
const layout: *TypeLayout = @ptrCast(@alignCast(erased));
|
const layout: *TypeLayout = @ptrCast(@alignCast(erased));
|
||||||
|
|
||||||
if (@hasDecl(Type, "deinit")) {
|
if (@hasDecl(Type, "deinit")) {
|
||||||
|
const deinit_fn = switch (@typeInfo(@TypeOf(Type.deinit))) {
|
||||||
|
.@"fn" => |@"fn"| @"fn",
|
||||||
|
|
||||||
|
else => @compileError(std.fmt.comptimePrint("Declaration `{s}.deinit` must be an fn type to be boxable", .{
|
||||||
|
@typeName(Type),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (deinit_fn.params.len != 1 or deinit_fn.params[0].type != *Type) {
|
||||||
|
@compileError(std.fmt.comptimePrint("Fn `{s}.deinit` is only permitted 1 parameter to be boxable and it must be of type {s}", .{
|
||||||
|
@typeName(Type),
|
||||||
|
@typeName(*Type),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
layout.value.deinit();
|
layout.value.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.allocator.destroy(layout);
|
layout.allocator.destroy(layout);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.delete(self.erased);
|
self.destroy(self.erased);
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has(self: Self, comptime Type: type) ?*Type {
|
pub fn has(self: Self, comptime Value: type) ?*Value {
|
||||||
const ValueLayout = Layout(Type);
|
const ValueLayout = Layout(Value);
|
||||||
|
|
||||||
if (self.delete == ValueLayout.delete) {
|
if (self.destroy == ValueLayout.destroy) {
|
||||||
const layout: *ValueLayout = @ptrCast(@alignCast(self.erased));
|
const layout: *ValueLayout = @ptrCast(@alignCast(self.erased));
|
||||||
|
|
||||||
return &layout.value;
|
return &layout.value;
|
||||||
@ -44,25 +65,25 @@ pub fn has(self: Self, comptime Type: type) ?*Type {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(value: anytype) std.mem.Allocator.Error!Self {
|
pub fn init(allocator: std.mem.Allocator, value: anytype) std.mem.Allocator.Error!Self {
|
||||||
return initWithAllocator(coral.heap.allocator, value);
|
const Value = @TypeOf(value);
|
||||||
}
|
const ValueLayout = Layout(Value);
|
||||||
|
|
||||||
pub fn initWithAllocator(allocator: std.mem.Allocator, value: anytype) std.mem.Allocator.Error!Self {
|
|
||||||
const ValueLayout = Layout(@TypeOf(value));
|
|
||||||
const value_layout = try allocator.create(ValueLayout);
|
const value_layout = try allocator.create(ValueLayout);
|
||||||
|
|
||||||
errdefer {
|
errdefer {
|
||||||
allocator.destroy(value_layout);
|
allocator.destroy(value_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
value_layout.* = .{
|
if (@hasField(ValueLayout, "allocator")) {
|
||||||
.allocator = allocator,
|
value_layout.allocator = allocator;
|
||||||
.value = value,
|
}
|
||||||
};
|
|
||||||
|
if (@hasField(ValueLayout, "value")) {
|
||||||
|
value_layout.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.erased = @ptrCast(value_layout),
|
.erased = @ptrCast(value_layout),
|
||||||
.delete = ValueLayout.delete,
|
.destroy = ValueLayout.destroy,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -251,10 +251,8 @@ pub const TaskQueue = struct {
|
|||||||
|
|
||||||
std.debug.assert(threads_spawned == thread_count);
|
std.debug.assert(threads_spawned == thread_count);
|
||||||
|
|
||||||
const name = comptime try coral.ShortString.init("ona worker");
|
|
||||||
|
|
||||||
for (threads) |thread| {
|
for (threads) |thread| {
|
||||||
thread.setName(name.slice()) catch |set_name_error| {
|
thread.setName("ona worker") catch |set_name_error| {
|
||||||
switch (set_name_error) {
|
switch (set_name_error) {
|
||||||
error.Unsupported, error.NameTooLong => break,
|
error.Unsupported, error.NameTooLong => break,
|
||||||
else => continue,
|
else => continue,
|
||||||
|
@ -101,6 +101,18 @@ pub fn allocFormatted(allocator: std.mem.Allocator, comptime format: []const u8,
|
|||||||
return @constCast(printFormatted(buffer, format, args) catch unreachable);
|
return @constCast(printFormatted(buffer, format, args) catch unreachable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn altFormat(value: anytype, comptime format: fn (@TypeOf(value), Writable) ReadWriteError!void) struct {
|
||||||
|
formattable: @TypeOf(value),
|
||||||
|
|
||||||
|
pub fn writeFormat(self: @This(), output: Writable) ReadWriteError!void {
|
||||||
|
return format(self.formattable, output);
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
return .{
|
||||||
|
.formattable = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn countFormatted(comptime format: []const u8, args: anytype) usize {
|
pub fn countFormatted(comptime format: []const u8, args: anytype) usize {
|
||||||
var count = WriteCount{};
|
var count = WriteCount{};
|
||||||
|
|
||||||
|
@ -10,13 +10,11 @@ pub const hashes = @import("./hashes.zig");
|
|||||||
|
|
||||||
pub const heap = @import("./heap.zig");
|
pub const heap = @import("./heap.zig");
|
||||||
|
|
||||||
pub const list = @import("./list.zig");
|
|
||||||
|
|
||||||
pub const map = @import("./map.zig");
|
pub const map = @import("./map.zig");
|
||||||
|
|
||||||
pub const scalars = @import("./scalars.zig");
|
pub const scalars = @import("./scalars.zig");
|
||||||
|
|
||||||
pub const shaders = @import("./shaders.zig");
|
pub const stack = @import("./stack.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
@ -115,243 +113,6 @@ pub fn KeyValuePair(comptime Key: type, comptime Value: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ShortString = extern struct {
|
|
||||||
buffer: [max]u8,
|
|
||||||
remaining: u8,
|
|
||||||
|
|
||||||
pub const Error = error{
|
|
||||||
StringTooLong,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn append(self: *ShortString, c: u8) Error!void {
|
|
||||||
const post_remaining = scalars.sub(self.remaining, 1) orelse {
|
|
||||||
return error.StringTooLong;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.buffer[self.len()] = c;
|
|
||||||
self.remaining = post_remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assign(self: *ShortString, data: []const u8) Error!void {
|
|
||||||
self.clear();
|
|
||||||
|
|
||||||
try self.join(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(self: *ShortString) void {
|
|
||||||
self.* = empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(data: []const u8) Error!ShortString {
|
|
||||||
var string = empty;
|
|
||||||
|
|
||||||
try string.join(data);
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn isEmpty(self: ShortString) bool {
|
|
||||||
return self.remaining == max;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn join(self: *ShortString, data: []const u8) Error!void {
|
|
||||||
const remaining = scalars.sub(self.remaining, data.len) orelse {
|
|
||||||
return error.StringTooLong;
|
|
||||||
};
|
|
||||||
|
|
||||||
@memcpy(self.buffer[self.len()..(max - remaining)], data);
|
|
||||||
|
|
||||||
self.remaining = @intCast(remaining);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const empty = ShortString{
|
|
||||||
.buffer = [_]u8{0} ** max,
|
|
||||||
.remaining = max,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn len(self: ShortString) usize {
|
|
||||||
return max - self.remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const max = 255;
|
|
||||||
|
|
||||||
pub fn ptr(self: *const ShortString) [*:0]const u8 {
|
|
||||||
return @ptrCast(&self.buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn slice(self: *const ShortString) [:0]const u8 {
|
|
||||||
return @ptrCast(self.ptr()[0..self.len()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn writeFormat(self: ShortString, output: bytes.Writable) bytes.ReadWriteError!void {
|
|
||||||
return bytes.writeAll(output, self.slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn writer(self: *ShortString) bytes.Writable {
|
|
||||||
const writing = struct {
|
|
||||||
fn write(string: *ShortString, buffer: []const u8) usize {
|
|
||||||
string.append(buffer) catch {
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
return buffer.len;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return .init(ShortString, self, writing.write);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn Stack(comptime Value: type) type {
|
|
||||||
return struct {
|
|
||||||
values: []Value,
|
|
||||||
cap: usize,
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub fn clear(self: *Self) void {
|
|
||||||
self.values = self.values[0..0];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
|
||||||
if (self.cap == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
heap.allocator.free(self.values.ptr[0..self.cap]);
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const empty = Self{
|
|
||||||
.values = &.{},
|
|
||||||
.cap = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn grow(self: *Self, additional: usize) std.mem.Allocator.Error!void {
|
|
||||||
const grown_capacity = self.values.len + additional;
|
|
||||||
|
|
||||||
if (grown_capacity <= self.cap) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buffer = try heap.allocator.alloc(Value, grown_capacity);
|
|
||||||
|
|
||||||
errdefer {
|
|
||||||
heap.allocator.deallocate(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.cap != 0) {
|
|
||||||
@memcpy(buffer[0..self.values.len], self.values);
|
|
||||||
heap.allocator.free(self.values.ptr[0..self.cap]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.values = buffer[0..self.values.len];
|
|
||||||
self.cap = grown_capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn isEmpty(self: Self) bool {
|
|
||||||
return self.values.len == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn isFull(self: Self) bool {
|
|
||||||
return self.values.len == self.cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(self: Self) ?*Value {
|
|
||||||
return if (self.isEmpty()) null else &self.values[self.values.len - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(self: *Self) ?Value {
|
|
||||||
if (self.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tail_index = self.values.len - 1;
|
|
||||||
|
|
||||||
defer self.values = self.values[0..tail_index];
|
|
||||||
|
|
||||||
return self.values[tail_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(self: *Self, value: Value) bool {
|
|
||||||
if (self.isFull()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const offset_index = self.values.len;
|
|
||||||
|
|
||||||
self.values = self.values.ptr[0 .. self.values.len + 1];
|
|
||||||
self.values[offset_index] = value;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pushAll(self: *Self, values: []const Value) bool {
|
|
||||||
const new_length = self.values.len + values.len;
|
|
||||||
|
|
||||||
if (new_length > self.cap) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const offset_index = self.values.len;
|
|
||||||
|
|
||||||
self.values = self.values.ptr[0..new_length];
|
|
||||||
|
|
||||||
for (0..values.len) |index| {
|
|
||||||
self.values[offset_index + index] = values[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pushGrow(self: *Self, value: Value) std.mem.Allocator.Error!void {
|
|
||||||
try self.grow(@max(1, self.values.len));
|
|
||||||
|
|
||||||
std.debug.assert(self.push(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pushMany(self: *Self, n: usize, value: Value) bool {
|
|
||||||
const new_length = self.values.len + n;
|
|
||||||
|
|
||||||
if (new_length > self.cap) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const offset_index = self.values.len;
|
|
||||||
|
|
||||||
self.values = self.values.ptr[0..new_length];
|
|
||||||
|
|
||||||
for (0..n) |index| {
|
|
||||||
self.values[offset_index + index] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(self: *Self, size: usize, default_value: Value) std.mem.Allocator.Error!void {
|
|
||||||
if (self.cap == size) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = try heap.allocator.alloc(Value, size);
|
|
||||||
|
|
||||||
for (0..@min(values.len, self.values.len)) |i| {
|
|
||||||
values[i] = self.values[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.len > self.values.len) {
|
|
||||||
for (self.values.len..values.len) |i| {
|
|
||||||
values[i] = default_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.values = values[0..values.len];
|
|
||||||
self.cap = values.len;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn require(function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) switch (@typeInfo(@typeInfo(@TypeOf(function)).@"fn".return_type.?)) {
|
pub fn require(function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) switch (@typeInfo(@typeInfo(@TypeOf(function)).@"fn".return_type.?)) {
|
||||||
.error_union => |error_union| error_union.payload,
|
.error_union => |error_union| error_union.payload,
|
||||||
else => @compileError("fn parameter `function` must return an error union"),
|
else => @compileError("fn parameter `function` must return an error union"),
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn Linked(comptime Value: type, comptime block_size: usize) type {
|
|
||||||
return struct {
|
|
||||||
has_head_block: ?*Block,
|
|
||||||
has_tail_block: ?*Block,
|
|
||||||
|
|
||||||
const Block = struct {
|
|
||||||
values: std.BoundedArray(Value, block_size) = .{},
|
|
||||||
has_next: ?*Block = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub const Values = struct {
|
|
||||||
has_block: ?*Block,
|
|
||||||
block_index: std.math.IntFittingRange(0, block_size),
|
|
||||||
|
|
||||||
pub fn next(self: *Values) ?*Value {
|
|
||||||
var block = self.has_block orelse {
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (self.block_index >= block.values.len) {
|
|
||||||
self.has_block = block.has_next;
|
|
||||||
self.block_index = 0;
|
|
||||||
|
|
||||||
block = self.has_block orelse {
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
defer {
|
|
||||||
self.block_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &block.values.slice()[self.block_index];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn append(self: *Self, allocator: std.mem.Allocator, value: Value) std.mem.Allocator.Error!*Value {
|
|
||||||
const tail_block = self.has_tail_block orelse create: {
|
|
||||||
const block = try allocator.create(Block);
|
|
||||||
|
|
||||||
block.* = .{};
|
|
||||||
self.has_head_block = block;
|
|
||||||
self.has_tail_block = block;
|
|
||||||
|
|
||||||
break :create block;
|
|
||||||
};
|
|
||||||
|
|
||||||
tail_block.values.append(value) catch {
|
|
||||||
const block = try allocator.create(Block);
|
|
||||||
|
|
||||||
block.* = .{};
|
|
||||||
tail_block.has_next = block;
|
|
||||||
self.has_tail_block = block;
|
|
||||||
|
|
||||||
block.values.append(value) catch {
|
|
||||||
unreachable;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return &tail_block.values.slice()[tail_block.values.len - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
|
||||||
var blocks = self.has_head_block;
|
|
||||||
|
|
||||||
while (blocks) |block| {
|
|
||||||
const has_next = block.has_next;
|
|
||||||
|
|
||||||
allocator.destroy(block);
|
|
||||||
|
|
||||||
blocks = has_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.has_head_block = undefined;
|
|
||||||
self.has_tail_block = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const empty = Self{
|
|
||||||
.has_head_block = null,
|
|
||||||
.has_tail_block = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn get(self: Self, index: usize) ?*Value {
|
|
||||||
if (self.has_tail_block) |tail_block| {
|
|
||||||
if (tail_block.values.len == 0) {
|
|
||||||
std.debug.assert(self.has_head_block == self.has_tail_block);
|
|
||||||
|
|
||||||
return if (tail_block.values.len == 0) null else tail_block.values.slice()[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn isEmpty(self: Self) bool {
|
|
||||||
return self.has_head_block == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(self: Self) usize {
|
|
||||||
if (self.has_tail_block) |tail_block| {
|
|
||||||
var accounted = tail_block.values.len;
|
|
||||||
var blocks = self.has_head_block;
|
|
||||||
|
|
||||||
while (blocks != self.has_tail_block) : (blocks = blocks.?.has_next) {
|
|
||||||
accounted += block_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return accounted;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn values(self: *const Self) Values {
|
|
||||||
return Values{
|
|
||||||
.has_block = self.has_head_block,
|
|
||||||
.block_index = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,349 +0,0 @@
|
|||||||
pub const Field = @import("./shaders/Field.zig");
|
|
||||||
|
|
||||||
pub const Root = @import("./shaders/Root.zig");
|
|
||||||
|
|
||||||
pub const Scope = @import("./shaders/Scope.zig");
|
|
||||||
|
|
||||||
pub const Type = @import("./shaders/Type.zig");
|
|
||||||
|
|
||||||
const coral = @import("./coral.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const tokens = @import("./shaders/tokens.zig");
|
|
||||||
|
|
||||||
pub const Argument = struct {
|
|
||||||
expression: *const Expression,
|
|
||||||
has_next: ?*const Argument = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Block = struct {
|
|
||||||
scope: *const Scope,
|
|
||||||
depth: usize,
|
|
||||||
statements: ?*const Statement = null,
|
|
||||||
|
|
||||||
pub fn hasLastStatement(self: Block) ?*const Statement {
|
|
||||||
var statement = self.statements orelse {
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
statement = switch (statement.*) {
|
|
||||||
.declare_local => |local_declaration| local_declaration.has_next orelse {
|
|
||||||
return statement;
|
|
||||||
},
|
|
||||||
|
|
||||||
.mutate_local => |local_mutation| local_mutation.has_next orelse {
|
|
||||||
return statement;
|
|
||||||
},
|
|
||||||
|
|
||||||
.mutate_output => |output_mutation| output_mutation.has_next orelse {
|
|
||||||
return statement;
|
|
||||||
},
|
|
||||||
|
|
||||||
.return_expression => {
|
|
||||||
return statement;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const DefinitionError = std.mem.Allocator.Error || error{
|
|
||||||
TooManySymbols,
|
|
||||||
DuplicateIdentifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Expression = union(enum) {
|
|
||||||
constant: [:0]const u8,
|
|
||||||
group_expression: *const Expression,
|
|
||||||
add: BinaryOperation,
|
|
||||||
subtract: BinaryOperation,
|
|
||||||
multiply: BinaryOperation,
|
|
||||||
divide: BinaryOperation,
|
|
||||||
equal: BinaryOperation,
|
|
||||||
greater_than: BinaryOperation,
|
|
||||||
greater_equal: BinaryOperation,
|
|
||||||
lesser_than: BinaryOperation,
|
|
||||||
lesser_equal: BinaryOperation,
|
|
||||||
negate_expression: *const Expression,
|
|
||||||
get_local: *const Local,
|
|
||||||
get_parameter: *const Parameter,
|
|
||||||
get_uniform: GetUniform,
|
|
||||||
get_object: GetObject,
|
|
||||||
mutate_local: LocalMutation,
|
|
||||||
mutate_output: OutputMutation,
|
|
||||||
get_texture: *const Texture,
|
|
||||||
get_output: *const Output,
|
|
||||||
get_input: *const Input,
|
|
||||||
invoke: Invocation,
|
|
||||||
convert: Conversion,
|
|
||||||
pow: Intrinsic,
|
|
||||||
abs: Intrinsic,
|
|
||||||
sin: Intrinsic,
|
|
||||||
sample: Builtin,
|
|
||||||
|
|
||||||
pub const BinaryOperation = struct {
|
|
||||||
rhs_expression: *const Expression,
|
|
||||||
lhs_expression: *const Expression,
|
|
||||||
type: *const Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Builtin = struct {
|
|
||||||
has_argument: ?*const Argument,
|
|
||||||
signatures: []const Signature,
|
|
||||||
|
|
||||||
pub const Signature = struct {
|
|
||||||
parameter_types: []const *const Type,
|
|
||||||
return_type: *const Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn inferType(self: Builtin) TypeError!*const Type {
|
|
||||||
signature_matching: for (self.signatures) |signature| {
|
|
||||||
var parameter_index: usize = 0;
|
|
||||||
var has_argument = self.has_argument;
|
|
||||||
|
|
||||||
while (has_argument) |argument| : ({
|
|
||||||
has_argument = argument.has_next;
|
|
||||||
parameter_index += 1;
|
|
||||||
}) {
|
|
||||||
if (signature.parameter_types.len == parameter_index) {
|
|
||||||
continue :signature_matching;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signature.parameter_types[parameter_index] != try argument.expression.inferType()) {
|
|
||||||
continue :signature_matching;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return signature.return_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.IncompatibleArguments;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Conversion = struct {
|
|
||||||
target_type: *const Type,
|
|
||||||
first_argument: *const Argument,
|
|
||||||
parameter_types: []const *const Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Intrinsic = struct {
|
|
||||||
allowed_parameter_types: []const *const Type,
|
|
||||||
expected_parameter_count: usize,
|
|
||||||
first_argument: *const Argument,
|
|
||||||
|
|
||||||
pub fn inferType(self: Intrinsic) TypeError!*const Type {
|
|
||||||
std.debug.assert(self.expected_parameter_count != 0);
|
|
||||||
|
|
||||||
const return_type = try self.first_argument.expression.inferType();
|
|
||||||
|
|
||||||
if (std.mem.indexOfScalar(*const Type, self.allowed_parameter_types, return_type) == null) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
var has_next_argument = self.first_argument.has_next;
|
|
||||||
var arguments_remaining = self.expected_parameter_count - 1;
|
|
||||||
|
|
||||||
while (has_next_argument) |next_argument| : ({
|
|
||||||
has_next_argument = next_argument.has_next;
|
|
||||||
arguments_remaining -= 1;
|
|
||||||
}) {
|
|
||||||
if (arguments_remaining == 0) {
|
|
||||||
return error.IncompatibleArguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try next_argument.expression.inferType() != return_type) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments_remaining != 0) {
|
|
||||||
return error.IncompatibleArguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
return return_type;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GetObject = struct {
|
|
||||||
field: *const Field,
|
|
||||||
object_expression: *const Expression,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GetUniform = struct {
|
|
||||||
field: *const Field,
|
|
||||||
uniform: *const Uniform,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Invocation = struct {
|
|
||||||
function: *const Function,
|
|
||||||
arguments: ?*const Argument = null,
|
|
||||||
argument_count: usize = 0,
|
|
||||||
|
|
||||||
pub fn inferType(self: Invocation) TypeError!*const Type {
|
|
||||||
var parameters = self.function.parameters;
|
|
||||||
var arguments = self.arguments;
|
|
||||||
|
|
||||||
while (parameters) |parameter| {
|
|
||||||
const argument = arguments orelse {
|
|
||||||
return error.IncompatibleArguments;
|
|
||||||
};
|
|
||||||
|
|
||||||
defer {
|
|
||||||
parameters = parameter.has_next;
|
|
||||||
arguments = argument.has_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameter.type != try argument.expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments != null) {
|
|
||||||
return error.IncompatibleArguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.function.has_return_type orelse error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const OutputMutation = struct {
|
|
||||||
output: *const Output,
|
|
||||||
expression: *const Expression,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const LocalMutation = struct {
|
|
||||||
local: *const Local,
|
|
||||||
expression: *const Expression,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn inferType(self: Expression) TypeError!*const Type {
|
|
||||||
return switch (self) {
|
|
||||||
.constant => |constant| switch (std.mem.indexOfScalar(u8, constant, '.') == null) {
|
|
||||||
true => .int,
|
|
||||||
false => .float,
|
|
||||||
},
|
|
||||||
|
|
||||||
.negate_expression, .group_expression => |expression| expression.inferType(),
|
|
||||||
.abs, .pow, .sin => |generic| generic.inferType(),
|
|
||||||
.convert => |convert| convert.target_type,
|
|
||||||
.invoke => |invoke| invoke.inferType(),
|
|
||||||
.mutate_local => |mutate_local| mutate_local.local.type,
|
|
||||||
.get_local => |local| local.type,
|
|
||||||
.get_object => |get_object| get_object.field.type,
|
|
||||||
.get_parameter => |get_parameter| get_parameter.type,
|
|
||||||
.sample => |builtin| builtin.inferType(),
|
|
||||||
.get_uniform => |uniform| uniform.field.type,
|
|
||||||
.get_input => |input| input.type,
|
|
||||||
.get_output => |output| output.type,
|
|
||||||
.mutate_output => |mutate_output| mutate_output.output.type,
|
|
||||||
|
|
||||||
.get_texture => |texture| switch (texture.layout) {
|
|
||||||
.dimensions_2 => .texture2,
|
|
||||||
},
|
|
||||||
|
|
||||||
.add,
|
|
||||||
.subtract,
|
|
||||||
.multiply,
|
|
||||||
.divide,
|
|
||||||
.equal,
|
|
||||||
.greater_than,
|
|
||||||
.greater_equal,
|
|
||||||
.lesser_than,
|
|
||||||
.lesser_equal,
|
|
||||||
=> |binary_op| binary_op.type,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Function = struct {
|
|
||||||
identifier: [:0]const u8,
|
|
||||||
signature: [:0]const u8,
|
|
||||||
has_return_type: ?*const Type = null,
|
|
||||||
parameters: ?*const Parameter = null,
|
|
||||||
parameter_count: usize = 0,
|
|
||||||
block: *const Block,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Input = struct {
|
|
||||||
identifier: [:0]const u8,
|
|
||||||
type: *const Type,
|
|
||||||
location: u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const InternError = std.mem.Allocator.Error || error{
|
|
||||||
TooManyConstants,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Output = struct {
|
|
||||||
identifier: [:0]const u8,
|
|
||||||
type: *const Type,
|
|
||||||
location: u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Parameter = struct {
|
|
||||||
identifier: [:0]const u8,
|
|
||||||
type: *const Type,
|
|
||||||
has_next: ?*const Parameter = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ParsingError = std.mem.Allocator.Error || tokens.ExpectationError || DefinitionError || TypeError || InternError || error{
|
|
||||||
ImmutableStorage,
|
|
||||||
UndefinedIdentifier,
|
|
||||||
MissingReturn,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Texture = struct {
|
|
||||||
identifier: [:0]const u8,
|
|
||||||
binding: u8,
|
|
||||||
layout: Layout,
|
|
||||||
|
|
||||||
pub const Layout = enum {
|
|
||||||
dimensions_2,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Statement = union(enum) {
|
|
||||||
declare_local: LocalDeclaration,
|
|
||||||
mutate_local: LocalMutation,
|
|
||||||
mutate_output: OutputMutation,
|
|
||||||
return_expression: *const Expression,
|
|
||||||
|
|
||||||
pub const LocalDeclaration = struct {
|
|
||||||
local: *const Local,
|
|
||||||
has_next: ?*const Statement = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const LocalMutation = struct {
|
|
||||||
local: *const Local,
|
|
||||||
expression: *const Expression,
|
|
||||||
has_next: ?*const Statement = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const OutputMutation = struct {
|
|
||||||
output: *const Output,
|
|
||||||
expression: *const Expression,
|
|
||||||
has_next: ?*const Statement = null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Local = struct {
|
|
||||||
identifier: [:0]const u8,
|
|
||||||
expression: *const Expression,
|
|
||||||
type: *const Type,
|
|
||||||
is_constant: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const TypeError = error{
|
|
||||||
IncompatibleTypes,
|
|
||||||
IncompatibleArguments,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Uniform = struct {
|
|
||||||
identifier: [:0]const u8,
|
|
||||||
semantic: [:0]const u8,
|
|
||||||
binding: u8,
|
|
||||||
has_field: ?*const Field = null,
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
@ -1,266 +0,0 @@
|
|||||||
const coral = @import("../coral.zig");
|
|
||||||
|
|
||||||
const spirv = @import("./spirv.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const tokens = @import("./tokens.zig");
|
|
||||||
|
|
||||||
scope: *coral.shaders.Scope,
|
|
||||||
inputs: std.BoundedArray(*const coral.shaders.Input, 15) = .{},
|
|
||||||
outputs: std.BoundedArray(*const coral.shaders.Output, 15) = .{},
|
|
||||||
uniforms: std.BoundedArray(*const coral.shaders.Uniform, 15) = .{},
|
|
||||||
textures: std.BoundedArray(*const coral.shaders.Texture, 15) = .{},
|
|
||||||
functions: std.BoundedArray(*const coral.shaders.Function, max_functions) = .{},
|
|
||||||
|
|
||||||
pub const BuildError = spirv.BuildError || error{
|
|
||||||
InvalidEntryPoint,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ParseResult = union(enum) {
|
|
||||||
ok,
|
|
||||||
failure: [:0]const u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub fn buildSpirvFragment(self: *Self, arena: *std.heap.ArenaAllocator, identifier: []const u8) BuildError!spirv.Module {
|
|
||||||
var module = spirv.Module{
|
|
||||||
.capabilities = &.{.shader},
|
|
||||||
.memory_model = .{ .logical, .glsl450 },
|
|
||||||
};
|
|
||||||
|
|
||||||
_ = try module.shaderEntryPoint(arena, .fragment, self.hasFunction(identifier) orelse {
|
|
||||||
return error.InvalidEntryPoint;
|
|
||||||
});
|
|
||||||
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(self: *Self) void {
|
|
||||||
self.scope.clear();
|
|
||||||
self.inputs.clear();
|
|
||||||
self.outputs.clear();
|
|
||||||
self.uniforms.clear();
|
|
||||||
self.textures.clear();
|
|
||||||
self.functions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn defineFunction(self: *Self, arena: *std.heap.ArenaAllocator, function: coral.shaders.Function) coral.shaders.DefinitionError!void {
|
|
||||||
self.functions.append(try self.scope.define(arena, function)) catch |append_error| {
|
|
||||||
return switch (append_error) {
|
|
||||||
error.Overflow => error.TooManySymbols,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn defineInput(self: *Self, arena: *std.heap.ArenaAllocator, input: coral.shaders.Input) coral.shaders.DefinitionError!void {
|
|
||||||
self.inputs.append(try self.scope.define(arena, input)) catch |append_error| {
|
|
||||||
return switch (append_error) {
|
|
||||||
error.Overflow => error.TooManySymbols,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn defineOutput(self: *Self, arena: *std.heap.ArenaAllocator, output: coral.shaders.Output) coral.shaders.DefinitionError!void {
|
|
||||||
self.outputs.append(try self.scope.define(arena, output)) catch |append_error| {
|
|
||||||
return switch (append_error) {
|
|
||||||
error.Overflow => error.TooManySymbols,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn defineTexture(self: *Self, arena: *std.heap.ArenaAllocator, texture: coral.shaders.Texture) coral.shaders.DefinitionError!void {
|
|
||||||
self.textures.append(try self.scope.define(arena, texture)) catch |append_error| {
|
|
||||||
return switch (append_error) {
|
|
||||||
error.Overflow => error.TooManySymbols,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn defineUniform(self: *Self, arena: *std.heap.ArenaAllocator, uniform: coral.shaders.Uniform) coral.shaders.DefinitionError!void {
|
|
||||||
self.uniforms.append(try self.scope.define(arena, uniform)) catch |append_error| {
|
|
||||||
return switch (append_error) {
|
|
||||||
error.Overflow => error.TooManySymbols,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hasFunction(self: Self, identifier: []const u8) ?*const coral.shaders.Function {
|
|
||||||
return self.scope.hasLocal(coral.shaders.Function, identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hasInput(self: Self, identifier: []const u8) ?*const coral.shaders.Input {
|
|
||||||
return self.scope.hasLocal(coral.shaders.Input, identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hasOutput(self: Self, identifier: []const u8) ?*const coral.shaders.Output {
|
|
||||||
return self.scope.hasLocal(coral.shaders.Output, identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hasTexture(self: Self, identifier: []const u8) ?*const coral.shaders.Texture {
|
|
||||||
return self.scope.hasLocal(coral.shaders.Texture, identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hasUniform(self: Self, identifier: []const u8) ?*const coral.shaders.Uniform {
|
|
||||||
return self.scope.hasLocal(coral.shaders.Uniform, identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(arena: *std.heap.ArenaAllocator) std.mem.Allocator.Error!Self {
|
|
||||||
const scope = try arena.allocator().create(coral.shaders.Scope);
|
|
||||||
|
|
||||||
scope.* = .{};
|
|
||||||
|
|
||||||
return .{ .scope = scope };
|
|
||||||
}
|
|
||||||
|
|
||||||
const max_functions = 255;
|
|
||||||
|
|
||||||
pub fn parse(self: *Self, arena: *std.heap.ArenaAllocator, source_text: []const u8) std.mem.Allocator.Error!ParseResult {
|
|
||||||
errdefer {
|
|
||||||
self.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
var source = tokens.Stream.init(source_text);
|
|
||||||
|
|
||||||
self.parseDocument(arena, &source) catch |parse_error| {
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.failure = try switch (parse_error) {
|
|
||||||
error.OutOfMemory => error.OutOfMemory,
|
|
||||||
|
|
||||||
error.TooManyConstants => coral.bytes.allocFormatted(arena_allocator, "{location}: number of literals in the current scope exceeded", .{
|
|
||||||
.location = source.location,
|
|
||||||
}),
|
|
||||||
|
|
||||||
error.ImmutableStorage => coral.bytes.allocFormatted(arena_allocator, "{location}: attempt to modify an immutable value", .{
|
|
||||||
.location = source.location,
|
|
||||||
}),
|
|
||||||
|
|
||||||
error.TooManySymbols => coral.bytes.allocFormatted(arena_allocator, "{location}: number of definitions in the current scope exceeded", .{
|
|
||||||
.location = source.location,
|
|
||||||
}),
|
|
||||||
|
|
||||||
error.IncompatibleArguments => coral.bytes.allocFormatted(arena_allocator, "{location}: incompatible set of arguments", .{
|
|
||||||
.location = source.location,
|
|
||||||
}),
|
|
||||||
|
|
||||||
error.IncompatibleTypes => coral.bytes.allocFormatted(arena_allocator, "{location}: incompatible types", .{
|
|
||||||
.location = source.location,
|
|
||||||
}),
|
|
||||||
|
|
||||||
error.DuplicateIdentifier => coral.bytes.allocFormatted(arena_allocator, "{location}: {kind} {token} already defined", .{
|
|
||||||
.location = source.location,
|
|
||||||
.token = source.current,
|
|
||||||
.kind = std.meta.activeTag(source.current),
|
|
||||||
}),
|
|
||||||
|
|
||||||
error.UnexpectedToken => coral.bytes.allocFormatted(arena_allocator, "{location}: unexpected {token}", .{
|
|
||||||
.location = source.location,
|
|
||||||
.token = source.current,
|
|
||||||
}),
|
|
||||||
|
|
||||||
error.UndefinedIdentifier => coral.bytes.allocFormatted(arena_allocator, "{location}: undefined identifier {token}", .{
|
|
||||||
.location = source.location,
|
|
||||||
.token = source.current,
|
|
||||||
}),
|
|
||||||
|
|
||||||
error.MissingReturn => coral.bytes.allocFormatted(arena_allocator, "{location}: value-returning function does not return at end", .{
|
|
||||||
.location = source.location,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return .ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseDocument(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!void {
|
|
||||||
while (source.skip(.newline) != .end) {
|
|
||||||
try switch (try source.current.expectAny(&.{.keyword_function})) {
|
|
||||||
.keyword_function => self.parseFunction(arena, source),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseFunction(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!void {
|
|
||||||
const identifier = try arena.allocator().dupeZ(u8, try source.skip(.newline).expectIdentifier());
|
|
||||||
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
const inner_scope = try self.scope.createScope(arena);
|
|
||||||
|
|
||||||
const parameters = switch (try source.skip(.newline).expectAny(&.{ .symbol_paren_right, .identifier })) {
|
|
||||||
.identifier => try inner_scope.parseParameter(arena, source),
|
|
||||||
.symbol_paren_right => null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const parameter_count = inner_scope.defined;
|
|
||||||
|
|
||||||
try source.skip(.newline).expect(.symbol_arrow);
|
|
||||||
|
|
||||||
var peeking = source.*;
|
|
||||||
const has_return_type = if (peeking.skip(.newline) == .symbol_brace_left) null else try self.scope.parseType(source);
|
|
||||||
const block = try inner_scope.parseBlock(arena, source);
|
|
||||||
|
|
||||||
if (has_return_type) |return_type| {
|
|
||||||
const last_statement = block.hasLastStatement() orelse {
|
|
||||||
return error.MissingReturn;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (last_statement.* != .return_expression) {
|
|
||||||
return error.MissingReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try last_statement.return_expression.inferType() != return_type) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.defineFunction(arena, .{
|
|
||||||
.has_return_type = has_return_type,
|
|
||||||
.parameters = parameters,
|
|
||||||
.parameter_count = parameter_count,
|
|
||||||
.identifier = identifier,
|
|
||||||
.block = block,
|
|
||||||
.signature = try self.scope.intern(arena, try signature(arena, parameters, has_return_type)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(arena: *std.heap.ArenaAllocator, parameters: ?*const coral.shaders.Parameter, has_return_type: ?*const coral.shaders.Type) std.mem.Allocator.Error![:0]const u8 {
|
|
||||||
var buffer_size = 2 + (if (has_return_type) |return_type| return_type.identifier.len else 0);
|
|
||||||
|
|
||||||
if (parameters) |first_parameter| {
|
|
||||||
buffer_size += first_parameter.type.identifier.len;
|
|
||||||
|
|
||||||
var has_parameter = first_parameter.has_next;
|
|
||||||
|
|
||||||
while (has_parameter) |parameter| : (has_parameter = parameter.has_next) {
|
|
||||||
buffer_size += 1 + parameter.type.identifier.len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = coral.bytes.span(try arena.allocator().allocSentinel(u8, buffer_size, 0));
|
|
||||||
|
|
||||||
std.debug.assert(buffer.put('('));
|
|
||||||
|
|
||||||
if (parameters) |first_parameter| {
|
|
||||||
std.debug.assert(buffer.write(first_parameter.type.identifier) == first_parameter.type.identifier.len);
|
|
||||||
|
|
||||||
var has_parameter = first_parameter.has_next;
|
|
||||||
|
|
||||||
while (has_parameter) |parameter| : (has_parameter = parameter.has_next) {
|
|
||||||
std.debug.assert(buffer.put(','));
|
|
||||||
std.debug.assert(buffer.write(parameter.type.identifier) == parameter.type.identifier.len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std.debug.assert(buffer.put(')'));
|
|
||||||
|
|
||||||
if (has_return_type) |return_type| {
|
|
||||||
std.debug.assert(buffer.write(return_type.identifier) == return_type.identifier.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return @ptrCast(buffer.bytes);
|
|
||||||
}
|
|
@ -1,747 +0,0 @@
|
|||||||
const coral = @import("../coral.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const tokens = @import("./tokens.zig");
|
|
||||||
|
|
||||||
interned: coral.tree.Binary([:0]const u8, void, coral.tree.sliceTraits([:0]const u8)) = .empty,
|
|
||||||
identifiers: [max_definitions][:0]const u8 = undefined,
|
|
||||||
symbols: [max_definitions]coral.Box = undefined,
|
|
||||||
defined: usize = 0,
|
|
||||||
has_enclosing: ?*Self = null,
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub fn clear(self: *Self) void {
|
|
||||||
self.defined = 0;
|
|
||||||
|
|
||||||
if (self.has_enclosing) |enclosing| {
|
|
||||||
enclosing.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create(arena: *std.heap.ArenaAllocator, node: anytype) std.mem.Allocator.Error!*@TypeOf(node) {
|
|
||||||
const allocation = try arena.allocator().create(@TypeOf(node));
|
|
||||||
|
|
||||||
allocation.* = node;
|
|
||||||
|
|
||||||
return allocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createScope(self: *Self, arena: *std.heap.ArenaAllocator) std.mem.Allocator.Error!*Self {
|
|
||||||
return create(arena, Self{
|
|
||||||
.has_enclosing = self,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn define(self: *Self, arena: *std.heap.ArenaAllocator, symbol: anytype) coral.shaders.DefinitionError!*@TypeOf(symbol) {
|
|
||||||
const Symbol = @TypeOf(symbol);
|
|
||||||
const identifier_field = "identifier";
|
|
||||||
const Identifier = [:0]const u8;
|
|
||||||
|
|
||||||
const identifier = switch (@hasField(Symbol, identifier_field)) {
|
|
||||||
true => switch (@TypeOf(symbol.identifier)) {
|
|
||||||
Identifier => symbol.identifier,
|
|
||||||
|
|
||||||
else => @compileError(std.fmt.comptimePrint("field {s}.{s} must be of type `{s}`", .{
|
|
||||||
@typeName(Symbol),
|
|
||||||
identifier_field,
|
|
||||||
@typeName(Identifier),
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
|
|
||||||
false => @compileError(std.fmt.comptimePrint("Type `{s}` must contain a field identifierd `{s}`", .{
|
|
||||||
@typeName(Symbol),
|
|
||||||
identifier_field,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (self.definitionExists(identifier)) {
|
|
||||||
return error.DuplicateIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.defined >= max_definitions) {
|
|
||||||
return error.TooManySymbols;
|
|
||||||
}
|
|
||||||
|
|
||||||
const stored_symbol = &self.symbols[self.defined];
|
|
||||||
|
|
||||||
self.identifiers[self.defined] = identifier;
|
|
||||||
stored_symbol.* = try .initWithAllocator(arena.allocator(), symbol);
|
|
||||||
self.defined += 1;
|
|
||||||
|
|
||||||
return stored_symbol.has(Symbol).?;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn definitionExists(self: Self, identifier: []const u8) bool {
|
|
||||||
for (self.identifiers[0..self.defined]) |existing_identifier| {
|
|
||||||
if (std.mem.eql(u8, existing_identifier, identifier)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.has_enclosing) |enclosing| {
|
|
||||||
return enclosing.definitionExists(identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hasGlobal(self: *const Self, comptime Symbol: type, identifier: []const u8) ?*const Symbol {
|
|
||||||
if (self.hasLocal(Symbol, identifier)) |local| {
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.has_enclosing) |enclosing| {
|
|
||||||
return enclosing.hasGlobal(Symbol, identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hasLocal(self: *const Self, comptime Symbol: type, identifier: []const u8) ?*const Symbol {
|
|
||||||
for (0..self.defined) |i| {
|
|
||||||
if (std.mem.eql(u8, self.identifiers[i], identifier)) {
|
|
||||||
if (self.symbols[i].has(Symbol)) |symbol| {
|
|
||||||
return symbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intern(self: *Self, arena: *std.heap.ArenaAllocator, value: [:0]const u8) std.mem.Allocator.Error![:0]const u8 {
|
|
||||||
var scope: ?*Self = self;
|
|
||||||
|
|
||||||
while (scope) |current| : (scope = current.has_enclosing) {
|
|
||||||
if (current.interned.getKey(value)) |existing| {
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std.debug.assert(try self.interned.insert(arena.allocator(), value, {}) != null);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const max_definitions = 256;
|
|
||||||
|
|
||||||
fn parseArgument(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Argument {
|
|
||||||
const expression = try self.parseExpression(arena, source);
|
|
||||||
|
|
||||||
return switch (try source.current.expectAny(&.{ .symbol_paren_right, .symbol_comma })) {
|
|
||||||
.symbol_paren_right => try create(arena, coral.shaders.Argument{
|
|
||||||
.expression = expression,
|
|
||||||
}),
|
|
||||||
|
|
||||||
.symbol_comma => {
|
|
||||||
var peeking = source.*;
|
|
||||||
|
|
||||||
if (peeking.skip(.newline) == .symbol_paren_right) {
|
|
||||||
source.* = peeking;
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Argument{ .expression = expression });
|
|
||||||
}
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Argument{
|
|
||||||
.expression = expression,
|
|
||||||
.has_next = try self.parseArgument(arena, source),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parseBlock(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Block {
|
|
||||||
try source.skip(.newline).expect(.symbol_brace_left);
|
|
||||||
|
|
||||||
return create(arena, coral.shaders.Block{
|
|
||||||
.scope = self,
|
|
||||||
.depth = source.depth,
|
|
||||||
|
|
||||||
.statements = switch (source.skip(.newline)) {
|
|
||||||
.symbol_brace_right => null,
|
|
||||||
else => try self.parseBlockStatement(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseBlockStatement(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Statement {
|
|
||||||
switch (try source.current.expectAny(&.{ .keyword_let, .keyword_var, .keyword_return, .identifier })) {
|
|
||||||
.identifier => {
|
|
||||||
const identifier = try source.current.expectIdentifier();
|
|
||||||
|
|
||||||
if (self.hasLocal(coral.shaders.Local, identifier)) |local| {
|
|
||||||
if (local.is_constant) {
|
|
||||||
return error.ImmutableStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
try source.skip(.newline).expect(.symbol_equals);
|
|
||||||
|
|
||||||
const expression = try self.parseExpression(arena, source);
|
|
||||||
|
|
||||||
if (try expression.inferType() != local.type) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Statement{
|
|
||||||
.mutate_local = .{
|
|
||||||
.local = local,
|
|
||||||
.expression = expression,
|
|
||||||
|
|
||||||
.has_next = switch (source.current) {
|
|
||||||
.symbol_brace_right => null,
|
|
||||||
else => try self.parseBlockStatement(arena, source),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.hasGlobal(coral.shaders.Output, identifier)) |output| {
|
|
||||||
try source.skip(.newline).expect(.symbol_equals);
|
|
||||||
|
|
||||||
const expression = try self.parseExpression(arena, source);
|
|
||||||
|
|
||||||
if (try expression.inferType() != output.type) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Statement{
|
|
||||||
.mutate_output = .{
|
|
||||||
.output = output,
|
|
||||||
.expression = expression,
|
|
||||||
|
|
||||||
.has_next = switch (source.current) {
|
|
||||||
.symbol_brace_right => null,
|
|
||||||
else => try self.parseBlockStatement(arena, source),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.ImmutableStorage;
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_return => {
|
|
||||||
return try create(arena, coral.shaders.Statement{
|
|
||||||
.return_expression = try self.parseExpression(arena, source),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_var, .keyword_let => {
|
|
||||||
const local = try self.define(arena, coral.shaders.Local{
|
|
||||||
.is_constant = source.current != .keyword_var,
|
|
||||||
.identifier = try arena.allocator().dupeZ(u8, try source.next().expectIdentifier()),
|
|
||||||
.expression = &.{ .constant = try self.intern(arena, "0") },
|
|
||||||
.type = .int,
|
|
||||||
});
|
|
||||||
|
|
||||||
try source.skip(.newline).expect(.symbol_equals);
|
|
||||||
|
|
||||||
local.expression = try self.parseExpression(arena, source);
|
|
||||||
local.type = try local.expression.inferType();
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Statement{
|
|
||||||
.declare_local = .{
|
|
||||||
.local = local,
|
|
||||||
|
|
||||||
.has_next = try switch (source.current) {
|
|
||||||
.symbol_brace_right => null,
|
|
||||||
else => self.parseBlockStatement(arena, source),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
|
||||||
const expression = try self.parseAdditiveExpression(arena, source);
|
|
||||||
|
|
||||||
if (source.current == .symbol_equals) {
|
|
||||||
return switch (expression.*) {
|
|
||||||
.get_local => |local| switch (local.is_constant) {
|
|
||||||
false => try create(arena, coral.shaders.Expression{
|
|
||||||
.mutate_local = .{
|
|
||||||
.local = local,
|
|
||||||
.expression = try self.parseExpression(arena, source),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
true => error.ImmutableStorage,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => error.UnexpectedToken,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseAdditiveExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
|
||||||
const lhs_expression = try self.parseEqualityExpression(arena, source);
|
|
||||||
|
|
||||||
if (source.current == .symbol_plus) {
|
|
||||||
const rhs_expression = try self.parseEqualityExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(arena, coral.shaders.Expression{
|
|
||||||
.add = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source.current == .symbol_minus) {
|
|
||||||
const rhs_expression = try self.parseEqualityExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(arena, coral.shaders.Expression{
|
|
||||||
.subtract = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return lhs_expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseComparativeExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
|
||||||
const lhs_expression = try self.parseTermExpression(arena, source);
|
|
||||||
|
|
||||||
if (source.current == .symbol_greater_than) {
|
|
||||||
const rhs_expression = try self.parseTermExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(arena, coral.shaders.Expression{
|
|
||||||
.greater_than = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source.current == .symbol_greater_equals) {
|
|
||||||
const rhs_expression = try self.parseTermExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(arena, coral.shaders.Expression{
|
|
||||||
.greater_equal = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source.current == .symbol_lesser_than) {
|
|
||||||
const rhs_expression = try self.parseTermExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(arena, coral.shaders.Expression{
|
|
||||||
.divide = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source.current == .symbol_lesser_equals) {
|
|
||||||
const rhs_expression = try self.parseTermExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(arena, coral.shaders.Expression{
|
|
||||||
.divide = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return lhs_expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseEqualityExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
|
||||||
const lhs_expression = try self.parseComparativeExpression(arena, source);
|
|
||||||
|
|
||||||
if (source.current == .symbol_double_equals) {
|
|
||||||
const rhs_expression = try self.parseComparativeExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(arena, coral.shaders.Expression{
|
|
||||||
.equal = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return lhs_expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseFactorExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
|
||||||
var expression = try self.parseOperandExpression(arena, source);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
switch (source.skip(.newline)) {
|
|
||||||
.symbol_period => {
|
|
||||||
const object_type = try expression.inferType();
|
|
||||||
|
|
||||||
expression = try create(arena, coral.shaders.Expression{
|
|
||||||
.get_object = .{
|
|
||||||
.object_expression = expression,
|
|
||||||
|
|
||||||
.field = object_type.hasField(try source.skip(.newline).expectIdentifier()) orelse {
|
|
||||||
return error.UndefinedIdentifier;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
return expression;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseOperandExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
|
||||||
switch (source.skip(.newline)) {
|
|
||||||
.symbol_minus => {
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.negate_expression = try self.parseExpression(arena, source),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.symbol_paren_left => {
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.group_expression = try self.parseExpression(arena, source),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.scalar => {
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.constant = try arena.allocator().dupeZ(u8, try source.current.expectScalar()),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.identifier => |identifier| {
|
|
||||||
if (self.hasGlobal(coral.shaders.Function, identifier)) |function| {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
var peeking = source.*;
|
|
||||||
|
|
||||||
switch (peeking.skip(.newline)) {
|
|
||||||
.symbol_paren_left => {
|
|
||||||
source.* = peeking;
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.invoke = .{
|
|
||||||
.function = function,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
const arguments = try self.parseArgument(arena, source);
|
|
||||||
var argument_count: usize = 1;
|
|
||||||
|
|
||||||
{
|
|
||||||
var subsequent_arguments = arguments.has_next;
|
|
||||||
|
|
||||||
while (subsequent_arguments) |argument| : (subsequent_arguments = argument.has_next) {
|
|
||||||
argument_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.invoke = .{
|
|
||||||
.function = function,
|
|
||||||
.argument_count = argument_count,
|
|
||||||
.arguments = arguments,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.hasGlobal(coral.shaders.Uniform, identifier)) |uniform| {
|
|
||||||
try source.skip(.newline).expect(.symbol_period);
|
|
||||||
|
|
||||||
const field_name = try source.skip(.newline).expectIdentifier();
|
|
||||||
|
|
||||||
const first_field = uniform.has_field orelse {
|
|
||||||
return error.UndefinedIdentifier;
|
|
||||||
};
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.get_uniform = .{
|
|
||||||
.uniform = uniform,
|
|
||||||
|
|
||||||
.field = first_field.has(field_name) orelse {
|
|
||||||
return error.UndefinedIdentifier;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.hasGlobal(coral.shaders.Texture, identifier)) |texture| {
|
|
||||||
return try create(arena, coral.shaders.Expression{ .get_texture = texture });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.hasGlobal(coral.shaders.Output, identifier)) |output| {
|
|
||||||
return try create(arena, coral.shaders.Expression{ .get_output = output });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.hasGlobal(coral.shaders.Input, identifier)) |input| {
|
|
||||||
return try create(arena, coral.shaders.Expression{ .get_input = input });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.hasLocal(coral.shaders.Local, identifier)) |local| {
|
|
||||||
return try create(arena, coral.shaders.Expression{ .get_local = local });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.hasLocal(coral.shaders.Parameter, identifier)) |parameter| {
|
|
||||||
return try create(arena, coral.shaders.Expression{ .get_parameter = parameter });
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.UndefinedIdentifier;
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_pow => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.pow = .{
|
|
||||||
.allowed_parameter_types = &.{ .float, .float2, .float3, .float4 },
|
|
||||||
.expected_parameter_count = 2,
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_abs => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.abs = .{
|
|
||||||
.allowed_parameter_types = &.{ .float, .float2, .float3, .float4 },
|
|
||||||
.expected_parameter_count = 1,
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_sin => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.sin = .{
|
|
||||||
.allowed_parameter_types = &.{ .float, .float2, .float3, .float4 },
|
|
||||||
.expected_parameter_count = 1,
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_int => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.convert = .{
|
|
||||||
.target_type = .int,
|
|
||||||
.parameter_types = &.{.float},
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_float => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.convert = .{
|
|
||||||
.target_type = .float,
|
|
||||||
.parameter_types = &.{.int},
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_float2 => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.convert = .{
|
|
||||||
.target_type = .float2,
|
|
||||||
.parameter_types = &.{ .float, .float },
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_float3 => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.convert = .{
|
|
||||||
.target_type = .float3,
|
|
||||||
.parameter_types = &.{ .float, .float, .float },
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_float4 => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.convert = .{
|
|
||||||
.target_type = .float4,
|
|
||||||
.parameter_types = &.{ .float, .float, .float, .float },
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_float4x4 => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.convert = .{
|
|
||||||
.target_type = .float4x4,
|
|
||||||
.parameter_types = &.{ .float4, .float4, .float4, .float4 },
|
|
||||||
.first_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
.keyword_sample => {
|
|
||||||
try source.skip(.newline).expect(.symbol_paren_left);
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.sample = .{
|
|
||||||
.signatures = &.{
|
|
||||||
.{
|
|
||||||
.parameter_types = &.{ .texture2, .float2 },
|
|
||||||
.return_type = .float4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.has_argument = try self.parseArgument(arena, source),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
return error.UnexpectedToken;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseTermExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
|
||||||
const lhs_expression = try self.parseFactorExpression(arena, source);
|
|
||||||
|
|
||||||
if (source.current == .symbol_asterisk) {
|
|
||||||
const rhs_expression = try self.parseFactorExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.multiply = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source.current == .symbol_forward_slash) {
|
|
||||||
const rhs_expression = try self.parseFactorExpression(arena, source);
|
|
||||||
const lhs_type = try lhs_expression.inferType();
|
|
||||||
|
|
||||||
if (lhs_type != try rhs_expression.inferType()) {
|
|
||||||
return error.IncompatibleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return try create(arena, coral.shaders.Expression{
|
|
||||||
.divide = .{
|
|
||||||
.rhs_expression = rhs_expression,
|
|
||||||
.lhs_expression = lhs_expression,
|
|
||||||
.type = lhs_type,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return lhs_expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parseParameter(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!?*const coral.shaders.Parameter {
|
|
||||||
const parameter = try self.define(arena, coral.shaders.Parameter{
|
|
||||||
.identifier = try arena.allocator().dupeZ(u8, try source.current.expectIdentifier()),
|
|
||||||
.type = .int,
|
|
||||||
});
|
|
||||||
|
|
||||||
parameter.type = try self.parseType(source);
|
|
||||||
|
|
||||||
if (try source.skip(.newline).expectAny(&.{ .symbol_paren_right, .symbol_comma }) == .symbol_comma) {
|
|
||||||
parameter.has_next = switch (try source.skip(.newline).expectAny(&.{ .symbol_paren_right, .identifier })) {
|
|
||||||
.symbol_paren_right => null,
|
|
||||||
.identifier => try self.parseParameter(arena, source),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parseType(self: *const Self, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Type {
|
|
||||||
return switch (source.skip(.newline)) {
|
|
||||||
.keyword_float => .float,
|
|
||||||
.keyword_float4x4 => .float4x4,
|
|
||||||
.keyword_float2 => .float2,
|
|
||||||
.keyword_float3 => .float3,
|
|
||||||
.keyword_float4 => .float4,
|
|
||||||
.keyword_int => .int,
|
|
||||||
.identifier => |identifier| self.hasGlobal(coral.shaders.Type, identifier) orelse error.UndefinedIdentifier,
|
|
||||||
else => error.UnexpectedToken,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,984 +0,0 @@
|
|||||||
const coral = @import("../coral.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
identifier: [:0]const u8,
|
|
||||||
layout: Layout,
|
|
||||||
|
|
||||||
pub const Layout = union(enum) {
|
|
||||||
float: Scalar,
|
|
||||||
signed: Scalar,
|
|
||||||
unsigned: Scalar,
|
|
||||||
vector: Vector,
|
|
||||||
matrix: Matrix,
|
|
||||||
texture: Texture,
|
|
||||||
record: Record,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub const Dimensions = enum(u2) {
|
|
||||||
@"2",
|
|
||||||
@"3",
|
|
||||||
@"4",
|
|
||||||
|
|
||||||
pub fn count(self: Dimensions) std.math.IntFittingRange(2, 4) {
|
|
||||||
return switch (self) {
|
|
||||||
.@"2" => 2,
|
|
||||||
.@"3" => 3,
|
|
||||||
.@"4" => 4,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Matrix = struct {
|
|
||||||
element: *const Self,
|
|
||||||
dimensions: Dimensions,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Record = struct {
|
|
||||||
field_count: usize = 0,
|
|
||||||
fields: ?*const coral.shaders.Field = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Scalar = struct {
|
|
||||||
bits: u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Texture = struct {
|
|
||||||
dimensions: Dimensions,
|
|
||||||
is_depth: bool,
|
|
||||||
is_arary: bool,
|
|
||||||
is_multi_sampled: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Vector = struct {
|
|
||||||
element: *const Self,
|
|
||||||
dimensions: Dimensions,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const float = &Self{
|
|
||||||
.identifier = "float",
|
|
||||||
.layout = .{ .float = .{ .bits = 32 } },
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const float2 = &Self{
|
|
||||||
.identifier = "float2",
|
|
||||||
|
|
||||||
.layout = .{
|
|
||||||
.vector = .{
|
|
||||||
.element = .float,
|
|
||||||
.dimensions = .@"2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const float3 = &Self{
|
|
||||||
.identifier = "float3",
|
|
||||||
|
|
||||||
.layout = .{
|
|
||||||
.vector = .{
|
|
||||||
.element = .float,
|
|
||||||
.dimensions = .@"3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const float4 = &Self{
|
|
||||||
.identifier = "float4",
|
|
||||||
|
|
||||||
.layout = .{
|
|
||||||
.vector = .{
|
|
||||||
.element = .float,
|
|
||||||
.dimensions = .@"4",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const float4x4 = &Self{
|
|
||||||
.identifier = "float4x4",
|
|
||||||
|
|
||||||
.layout = .{
|
|
||||||
.matrix = .{
|
|
||||||
.element = .float,
|
|
||||||
.dimensions = .@"2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn hasField(self: Self, field_identifier: []const u8) ?*const coral.shaders.Field {
|
|
||||||
return switch (self.layout) {
|
|
||||||
.float, .signed, .unsigned, .matrix, .texture => null,
|
|
||||||
.record => |record| if (record.fields) |field| field.has(field_identifier) else null,
|
|
||||||
|
|
||||||
.vector => |vector| switch (vector.dimensions) {
|
|
||||||
.@"2" => switch (field_identifier.len) {
|
|
||||||
1 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => .vector_x,
|
|
||||||
'y', 'g' => .vector_y,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
2 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => .vector_xx,
|
|
||||||
'y', 'g' => .vector_xy,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => .vector_yx,
|
|
||||||
'y', 'g' => .vector_yy,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
.@"3" => switch (field_identifier.len) {
|
|
||||||
1 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => .vector_x,
|
|
||||||
'y', 'g' => .vector_y,
|
|
||||||
'z', 'b' => .vector_z,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
2 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => .vector_xx,
|
|
||||||
'y', 'g' => .vector_xy,
|
|
||||||
'z', 'b' => .vector_xz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => .vector_yx,
|
|
||||||
'y', 'g' => .vector_yy,
|
|
||||||
'z', 'b' => .vector_yz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
3 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_xxx,
|
|
||||||
'y', 'g' => .vector_xxy,
|
|
||||||
'z', 'b' => .vector_xxz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_xyx,
|
|
||||||
'y', 'g' => .vector_xyy,
|
|
||||||
'z', 'b' => .vector_xyz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_xzx,
|
|
||||||
'y', 'g' => .vector_xzy,
|
|
||||||
'z', 'b' => .vector_xzz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_yxx,
|
|
||||||
'y', 'g' => .vector_yxy,
|
|
||||||
'z', 'b' => .vector_yxz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_yyx,
|
|
||||||
'y', 'g' => .vector_yyy,
|
|
||||||
'z', 'b' => .vector_yyz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_yzx,
|
|
||||||
'y', 'g' => .vector_yzy,
|
|
||||||
'z', 'b' => .vector_yzz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_zxx,
|
|
||||||
'y', 'g' => .vector_zxy,
|
|
||||||
'z', 'b' => .vector_zxz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_zyx,
|
|
||||||
'y', 'g' => .vector_zyy,
|
|
||||||
'z', 'b' => .vector_zyz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_zzx,
|
|
||||||
'y', 'g' => .vector_zzy,
|
|
||||||
'z', 'b' => .vector_zzz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
.@"4" => switch (field_identifier.len) {
|
|
||||||
1 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => .vector_x,
|
|
||||||
'y', 'g' => .vector_y,
|
|
||||||
'z', 'b' => .vector_z,
|
|
||||||
'w', 'a' => .vector_w,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
2 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => .vector_xx,
|
|
||||||
'y', 'g' => .vector_xy,
|
|
||||||
'z', 'b' => .vector_xz,
|
|
||||||
'w', 'a' => .vector_xw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => .vector_yx,
|
|
||||||
'y', 'g' => .vector_yy,
|
|
||||||
'z', 'b' => .vector_yz,
|
|
||||||
'w', 'a' => .vector_yz,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
3 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_xxx,
|
|
||||||
'y', 'g' => .vector_xxy,
|
|
||||||
'z', 'b' => .vector_xxz,
|
|
||||||
'w', 'a' => .vector_xxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_xyx,
|
|
||||||
'y', 'g' => .vector_xyy,
|
|
||||||
'z', 'b' => .vector_xyz,
|
|
||||||
'w', 'a' => .vector_xyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_xzx,
|
|
||||||
'y', 'g' => .vector_xzy,
|
|
||||||
'z', 'b' => .vector_xzz,
|
|
||||||
'w', 'a' => .vector_xzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_yxx,
|
|
||||||
'y', 'g' => .vector_yxy,
|
|
||||||
'z', 'b' => .vector_yxz,
|
|
||||||
'w', 'a' => .vector_yxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_yyx,
|
|
||||||
'y', 'g' => .vector_yyy,
|
|
||||||
'z', 'b' => .vector_yyz,
|
|
||||||
'w', 'a' => .vector_yyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_yzx,
|
|
||||||
'y', 'g' => .vector_yzy,
|
|
||||||
'z', 'b' => .vector_yzz,
|
|
||||||
'w', 'a' => .vector_yzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_zxx,
|
|
||||||
'y', 'g' => .vector_zxy,
|
|
||||||
'z', 'b' => .vector_zxz,
|
|
||||||
'w', 'a' => .vector_zxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_zyx,
|
|
||||||
'y', 'g' => .vector_zyy,
|
|
||||||
'z', 'b' => .vector_zyz,
|
|
||||||
'w', 'a' => .vector_zyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => .vector_zzx,
|
|
||||||
'y', 'g' => .vector_zzy,
|
|
||||||
'z', 'b' => .vector_zzz,
|
|
||||||
'w', 'a' => .vector_zzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
4 => switch (field_identifier[0]) {
|
|
||||||
'x', 'r' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xxxx,
|
|
||||||
'y', 'g' => .vector_xxxy,
|
|
||||||
'z', 'b' => .vector_xxxz,
|
|
||||||
'w', 'a' => .vector_xxxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xxyx,
|
|
||||||
'y', 'g' => .vector_xxyy,
|
|
||||||
'z', 'b' => .vector_xxyz,
|
|
||||||
'w', 'a' => .vector_xxyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xxzx,
|
|
||||||
'y', 'g' => .vector_xxzy,
|
|
||||||
'z', 'b' => .vector_xxzz,
|
|
||||||
'w', 'a' => .vector_xxzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xxwx,
|
|
||||||
'y', 'g' => .vector_xxwy,
|
|
||||||
'z', 'b' => .vector_xxwz,
|
|
||||||
'w', 'a' => .vector_xxww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xyxx,
|
|
||||||
'y', 'g' => .vector_xyxy,
|
|
||||||
'z', 'b' => .vector_xyxz,
|
|
||||||
'w', 'a' => .vector_xyxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xyyx,
|
|
||||||
'y', 'g' => .vector_xyyy,
|
|
||||||
'z', 'b' => .vector_xyyz,
|
|
||||||
'w', 'a' => .vector_xyyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xyzx,
|
|
||||||
'y', 'g' => .vector_xyzy,
|
|
||||||
'z', 'b' => .vector_xyzz,
|
|
||||||
'w', 'a' => .vector_xyzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xywx,
|
|
||||||
'y', 'g' => .vector_xywy,
|
|
||||||
'z', 'b' => .vector_xywz,
|
|
||||||
'w', 'a' => .vector_xyww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xzxx,
|
|
||||||
'y', 'g' => .vector_xzxy,
|
|
||||||
'z', 'b' => .vector_xzxz,
|
|
||||||
'w', 'a' => .vector_xzxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xzyx,
|
|
||||||
'y', 'g' => .vector_xzyy,
|
|
||||||
'z', 'b' => .vector_xzyz,
|
|
||||||
'w', 'a' => .vector_xzyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xzzx,
|
|
||||||
'y', 'g' => .vector_xzzy,
|
|
||||||
'z', 'b' => .vector_xzzz,
|
|
||||||
'w', 'a' => .vector_xzzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xzwx,
|
|
||||||
'y', 'g' => .vector_xzwy,
|
|
||||||
'z', 'b' => .vector_xzwz,
|
|
||||||
'w', 'a' => .vector_xzww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xwxx,
|
|
||||||
'y', 'g' => .vector_xwxy,
|
|
||||||
'z', 'b' => .vector_xwxz,
|
|
||||||
'w', 'a' => .vector_xwxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xwyx,
|
|
||||||
'y', 'g' => .vector_xwyy,
|
|
||||||
'z', 'b' => .vector_xwyz,
|
|
||||||
'w', 'a' => .vector_xwyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xwzx,
|
|
||||||
'y', 'g' => .vector_xwzy,
|
|
||||||
'z', 'b' => .vector_xwzz,
|
|
||||||
'w', 'a' => .vector_xwzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_xwwx,
|
|
||||||
'y', 'g' => .vector_xwwy,
|
|
||||||
'z', 'b' => .vector_xwwz,
|
|
||||||
'w', 'a' => .vector_xwww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yxxx,
|
|
||||||
'y', 'g' => .vector_yxxy,
|
|
||||||
'z', 'b' => .vector_yxxz,
|
|
||||||
'w', 'a' => .vector_yxxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yxyx,
|
|
||||||
'y', 'g' => .vector_yxyy,
|
|
||||||
'z', 'b' => .vector_yxyz,
|
|
||||||
'w', 'a' => .vector_yxyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yxzx,
|
|
||||||
'y', 'g' => .vector_yxzy,
|
|
||||||
'z', 'b' => .vector_yxzz,
|
|
||||||
'w', 'a' => .vector_yxzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yxwx,
|
|
||||||
'y', 'g' => .vector_yxwy,
|
|
||||||
'z', 'b' => .vector_yxwz,
|
|
||||||
'w', 'a' => .vector_yxww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yyxx,
|
|
||||||
'y', 'g' => .vector_yyxy,
|
|
||||||
'z', 'b' => .vector_yyxz,
|
|
||||||
'w', 'a' => .vector_yyxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yyyx,
|
|
||||||
'y', 'g' => .vector_yyyy,
|
|
||||||
'z', 'b' => .vector_yyyz,
|
|
||||||
'w', 'a' => .vector_yyyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yyzx,
|
|
||||||
'y', 'g' => .vector_yyzy,
|
|
||||||
'z', 'b' => .vector_yyzz,
|
|
||||||
'w', 'a' => .vector_yyzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yywx,
|
|
||||||
'y', 'g' => .vector_yywy,
|
|
||||||
'z', 'b' => .vector_yywz,
|
|
||||||
'w', 'a' => .vector_yyww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yzxx,
|
|
||||||
'y', 'g' => .vector_yzxy,
|
|
||||||
'z', 'b' => .vector_yzxz,
|
|
||||||
'w', 'a' => .vector_yzxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yzyx,
|
|
||||||
'y', 'g' => .vector_yzyy,
|
|
||||||
'z', 'b' => .vector_yzyz,
|
|
||||||
'w', 'a' => .vector_yzyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yzzx,
|
|
||||||
'y', 'g' => .vector_yzzy,
|
|
||||||
'z', 'b' => .vector_yzzz,
|
|
||||||
'w', 'a' => .vector_yzzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_yzwx,
|
|
||||||
'y', 'g' => .vector_yzwy,
|
|
||||||
'z', 'b' => .vector_yzwz,
|
|
||||||
'w', 'a' => .vector_yzww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_ywxx,
|
|
||||||
'y', 'g' => .vector_ywxy,
|
|
||||||
'z', 'b' => .vector_ywxz,
|
|
||||||
'w', 'a' => .vector_ywxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_ywyx,
|
|
||||||
'y', 'g' => .vector_ywyy,
|
|
||||||
'z', 'b' => .vector_ywyz,
|
|
||||||
'w', 'a' => .vector_ywyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_ywzx,
|
|
||||||
'y', 'g' => .vector_ywzy,
|
|
||||||
'z', 'b' => .vector_ywzz,
|
|
||||||
'w', 'a' => .vector_ywzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_ywwx,
|
|
||||||
'y', 'g' => .vector_ywwy,
|
|
||||||
'z', 'b' => .vector_ywwz,
|
|
||||||
'w', 'a' => .vector_ywww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zxxx,
|
|
||||||
'y', 'g' => .vector_zxxy,
|
|
||||||
'z', 'b' => .vector_zxxz,
|
|
||||||
'w', 'a' => .vector_zxxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zxyx,
|
|
||||||
'y', 'g' => .vector_zxyy,
|
|
||||||
'z', 'b' => .vector_zxyz,
|
|
||||||
'w', 'a' => .vector_zxyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zxzx,
|
|
||||||
'y', 'g' => .vector_zxzy,
|
|
||||||
'z', 'b' => .vector_zxzz,
|
|
||||||
'w', 'a' => .vector_zxzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zxwx,
|
|
||||||
'y', 'g' => .vector_zxwy,
|
|
||||||
'z', 'b' => .vector_zxwz,
|
|
||||||
'w', 'a' => .vector_zxww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zyxx,
|
|
||||||
'y', 'g' => .vector_zyxy,
|
|
||||||
'z', 'b' => .vector_zyxz,
|
|
||||||
'w', 'a' => .vector_zyxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zyyx,
|
|
||||||
'y', 'g' => .vector_zyyy,
|
|
||||||
'z', 'b' => .vector_zyyz,
|
|
||||||
'w', 'a' => .vector_zyyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zyzx,
|
|
||||||
'y', 'g' => .vector_zyzy,
|
|
||||||
'z', 'b' => .vector_zyzz,
|
|
||||||
'w', 'a' => .vector_zyzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zywx,
|
|
||||||
'y', 'g' => .vector_zywy,
|
|
||||||
'z', 'b' => .vector_zywz,
|
|
||||||
'w', 'a' => .vector_zyww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zzxx,
|
|
||||||
'y', 'g' => .vector_zzxy,
|
|
||||||
'z', 'b' => .vector_zzxz,
|
|
||||||
'w', 'a' => .vector_zzxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zzyx,
|
|
||||||
'y', 'g' => .vector_zzyy,
|
|
||||||
'z', 'b' => .vector_zzyz,
|
|
||||||
'w', 'a' => .vector_zzyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zzzx,
|
|
||||||
'y', 'g' => .vector_zzzy,
|
|
||||||
'z', 'b' => .vector_zzzz,
|
|
||||||
'w', 'a' => .vector_zzzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zzwx,
|
|
||||||
'y', 'g' => .vector_zzwy,
|
|
||||||
'z', 'b' => .vector_zzwz,
|
|
||||||
'w', 'a' => .vector_zzww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zwxx,
|
|
||||||
'y', 'g' => .vector_zwxy,
|
|
||||||
'z', 'b' => .vector_zwxz,
|
|
||||||
'w', 'a' => .vector_zwxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zwyx,
|
|
||||||
'y', 'g' => .vector_zwyy,
|
|
||||||
'z', 'b' => .vector_zwyz,
|
|
||||||
'w', 'a' => .vector_zwyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zwzx,
|
|
||||||
'y', 'g' => .vector_zwzy,
|
|
||||||
'z', 'b' => .vector_zwzz,
|
|
||||||
'w', 'a' => .vector_zwzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_zwwx,
|
|
||||||
'y', 'g' => .vector_zwwy,
|
|
||||||
'z', 'b' => .vector_zwwz,
|
|
||||||
'w', 'a' => .vector_zwww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[1]) {
|
|
||||||
'x', 'r' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wxxx,
|
|
||||||
'y', 'g' => .vector_wxxy,
|
|
||||||
'z', 'b' => .vector_wxxz,
|
|
||||||
'w', 'a' => .vector_wxxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wxyx,
|
|
||||||
'y', 'g' => .vector_wxyy,
|
|
||||||
'z', 'b' => .vector_wxyz,
|
|
||||||
'w', 'a' => .vector_wxyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wxzx,
|
|
||||||
'y', 'g' => .vector_wxzy,
|
|
||||||
'z', 'b' => .vector_wxzz,
|
|
||||||
'w', 'a' => .vector_wxzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wxwx,
|
|
||||||
'y', 'g' => .vector_wxwy,
|
|
||||||
'z', 'b' => .vector_wxwz,
|
|
||||||
'w', 'a' => .vector_wxww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wyxx,
|
|
||||||
'y', 'g' => .vector_wyxy,
|
|
||||||
'z', 'b' => .vector_wyxz,
|
|
||||||
'w', 'a' => .vector_wyxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wyyx,
|
|
||||||
'y', 'g' => .vector_wyyy,
|
|
||||||
'z', 'b' => .vector_wyyz,
|
|
||||||
'w', 'a' => .vector_wyyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wyzx,
|
|
||||||
'y', 'g' => .vector_wyzy,
|
|
||||||
'z', 'b' => .vector_wyzz,
|
|
||||||
'w', 'a' => .vector_wyzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wywx,
|
|
||||||
'y', 'g' => .vector_wywy,
|
|
||||||
'z', 'b' => .vector_wywz,
|
|
||||||
'w', 'a' => .vector_wyww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wzxx,
|
|
||||||
'y', 'g' => .vector_wzxy,
|
|
||||||
'z', 'b' => .vector_wzxz,
|
|
||||||
'w', 'a' => .vector_wzxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wzyx,
|
|
||||||
'y', 'g' => .vector_wzyy,
|
|
||||||
'z', 'b' => .vector_wzyz,
|
|
||||||
'w', 'a' => .vector_wzyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wzzx,
|
|
||||||
'y', 'g' => .vector_wzzy,
|
|
||||||
'z', 'b' => .vector_wzzz,
|
|
||||||
'w', 'a' => .vector_wzzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wzwx,
|
|
||||||
'y', 'g' => .vector_wzwy,
|
|
||||||
'z', 'b' => .vector_wzwz,
|
|
||||||
'w', 'a' => .vector_wzww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[2]) {
|
|
||||||
'x', 'r' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wwxx,
|
|
||||||
'y', 'g' => .vector_wwxy,
|
|
||||||
'z', 'b' => .vector_wwxz,
|
|
||||||
'w', 'a' => .vector_wwxw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'y', 'g' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wwyx,
|
|
||||||
'y', 'g' => .vector_wwyy,
|
|
||||||
'z', 'b' => .vector_wwyz,
|
|
||||||
'w', 'a' => .vector_wwyw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'z', 'b' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wwzx,
|
|
||||||
'y', 'g' => .vector_wwzy,
|
|
||||||
'z', 'b' => .vector_wwzz,
|
|
||||||
'w', 'a' => .vector_wwzw,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
'w', 'a' => switch (field_identifier[3]) {
|
|
||||||
'x', 'r' => .vector_wwwx,
|
|
||||||
'y', 'g' => .vector_wwwy,
|
|
||||||
'z', 'b' => .vector_wwwz,
|
|
||||||
'w', 'a' => .vector_wwww,
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const int = &Self{
|
|
||||||
.identifier = "int",
|
|
||||||
.layout = .{ .signed = .{ .bits = 32 } },
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const texture2 = &Self{
|
|
||||||
.identifier = "texture2",
|
|
||||||
|
|
||||||
.layout = .{
|
|
||||||
.texture = .{
|
|
||||||
.dimensions = .@"2",
|
|
||||||
.is_arary = false,
|
|
||||||
.is_depth = false,
|
|
||||||
.is_multi_sampled = false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,339 +0,0 @@
|
|||||||
const coral = @import("../coral.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const Version = enum {
|
|
||||||
core_430,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn generate(source: coral.bytes.Writable, root: coral.shaders.Root, version: Version) coral.shaders.GenerationError!void {
|
|
||||||
try coral.bytes.writeFormatted(source, "#version {version_name}\n", .{
|
|
||||||
.version_name = switch (version) {
|
|
||||||
.core_430 => "430 core",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
for (root.uniforms.slice()) |uniform| {
|
|
||||||
try coral.bytes.writeFormatted(source, "\nlayout (binding = {binding}) uniform {semantic} {{\n", .{
|
|
||||||
.binding = coral.utf8.cDec(uniform.binding),
|
|
||||||
.semantic = uniform.semantic,
|
|
||||||
});
|
|
||||||
|
|
||||||
var has_field = uniform.has_field;
|
|
||||||
|
|
||||||
while (has_field) |field| : (has_field = field.has_next) {
|
|
||||||
try coral.bytes.writeAll(source, "\t");
|
|
||||||
try generateType(source, field.type);
|
|
||||||
try coral.bytes.writeFormatted(source, " {identifier};\n", .{ .identifier = field.identifier });
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeFormatted(source, "}} {identifier};\n", .{
|
|
||||||
.identifier = uniform.identifier,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (root.textures.slice()) |texture| {
|
|
||||||
try coral.bytes.writeFormatted(source, "\nlayout (binding = {binding}) uniform {layout} {identifier};\n", .{
|
|
||||||
.binding = coral.utf8.cDec(texture.binding),
|
|
||||||
.identifier = texture.identifier,
|
|
||||||
|
|
||||||
.layout = switch (texture.layout) {
|
|
||||||
.dimensions_2 => "sampler2D",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (root.inputs.slice()) |input| {
|
|
||||||
try coral.bytes.writeFormatted(source, "\nlayout (location = {location}) in ", .{
|
|
||||||
.location = coral.utf8.cDec(input.location),
|
|
||||||
});
|
|
||||||
|
|
||||||
try generateType(source, input.type);
|
|
||||||
try coral.bytes.writeFormatted(source, " {identifier};\n", .{ .identifier = input.identifier });
|
|
||||||
}
|
|
||||||
|
|
||||||
for (root.outputs.slice()) |output| {
|
|
||||||
try coral.bytes.writeFormatted(source, "\nlayout (location = {location}) out ", .{
|
|
||||||
.location = coral.utf8.cDec(output.location),
|
|
||||||
});
|
|
||||||
|
|
||||||
try generateType(source, output.type);
|
|
||||||
try coral.bytes.writeFormatted(source, " {identifier};\n", .{ .identifier = output.identifier });
|
|
||||||
}
|
|
||||||
|
|
||||||
for (root.functions.slice()) |function| {
|
|
||||||
if (function.has_return_type) |return_type| {
|
|
||||||
try coral.bytes.writeAll(source, "\n");
|
|
||||||
try generateType(source, return_type);
|
|
||||||
} else {
|
|
||||||
try coral.bytes.writeAll(source, "\nvoid");
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeFormatted(source, " {identifier}(", .{ .identifier = function.identifier });
|
|
||||||
|
|
||||||
var has_parameter = function.has_parameter;
|
|
||||||
|
|
||||||
if (has_parameter) |first_parameter| {
|
|
||||||
try generateType(source, first_parameter.type);
|
|
||||||
try coral.bytes.writeFormatted(source, " {identifier}", .{ .identifier = first_parameter.identifier });
|
|
||||||
|
|
||||||
has_parameter = first_parameter.has_next;
|
|
||||||
|
|
||||||
while (has_parameter) |parameter| : (has_parameter = parameter.has_next) {
|
|
||||||
try coral.bytes.writeAll(source, ", ");
|
|
||||||
try generateType(source, parameter.type);
|
|
||||||
try coral.bytes.writeFormatted(source, " {identifier}", .{ .identifier = parameter.identifier });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeAll(source, ") ");
|
|
||||||
try generateBlock(source, function.block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generateArguments(source: coral.bytes.Writable, first_argument: *const coral.shaders.Argument) coral.bytes.ReadWriteError!void {
|
|
||||||
try generateExpression(source, first_argument.expression);
|
|
||||||
|
|
||||||
var has_next_argument = first_argument.has_next;
|
|
||||||
|
|
||||||
while (has_next_argument) |next_argument| : (has_next_argument = next_argument.has_next) {
|
|
||||||
try coral.bytes.writeAll(source, ", ");
|
|
||||||
try generateExpression(source, next_argument.expression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generateBlock(source: coral.bytes.Writable, block: *const coral.shaders.Block) coral.bytes.ReadWriteError!void {
|
|
||||||
try coral.bytes.writeAll(source, "{\n");
|
|
||||||
|
|
||||||
var has_next_statement = block.has_statement;
|
|
||||||
|
|
||||||
while (has_next_statement) |statement| {
|
|
||||||
try coral.bytes.writeN(source, "\t", block.depth);
|
|
||||||
|
|
||||||
switch (statement.*) {
|
|
||||||
.return_expression => |return_expression| {
|
|
||||||
try coral.bytes.writeAll(source, "return ");
|
|
||||||
|
|
||||||
if (return_expression) |expression| {
|
|
||||||
try generateExpression(source, expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeAll(source, ";\n");
|
|
||||||
|
|
||||||
has_next_statement = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
.declare_local => |declare_local| {
|
|
||||||
try generateType(source, declare_local.local.type);
|
|
||||||
try coral.bytes.writeFormatted(source, " {identifier} = ", .{ .identifier = declare_local.local.identifier });
|
|
||||||
try generateExpression(source, declare_local.local.expression);
|
|
||||||
try coral.bytes.writeAll(source, ";\n");
|
|
||||||
|
|
||||||
has_next_statement = declare_local.has_next;
|
|
||||||
},
|
|
||||||
|
|
||||||
.mutate_local => |mutate_local| {
|
|
||||||
try coral.bytes.writeFormatted(source, "{identifier} = ", .{ .identifier = mutate_local.local.identifier });
|
|
||||||
try generateExpression(source, mutate_local.expression);
|
|
||||||
try coral.bytes.writeAll(source, ";\n");
|
|
||||||
|
|
||||||
has_next_statement = mutate_local.has_next;
|
|
||||||
},
|
|
||||||
|
|
||||||
.mutate_output => |mutate_output| {
|
|
||||||
try coral.bytes.writeFormatted(source, "{identifier} = ", .{ .identifier = mutate_output.output.identifier });
|
|
||||||
try generateExpression(source, mutate_output.expression);
|
|
||||||
try coral.bytes.writeAll(source, ";\n");
|
|
||||||
|
|
||||||
has_next_statement = mutate_output.has_next;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeAll(source, "}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generateExpression(source: coral.bytes.Writable, expression: *const coral.shaders.Expression) coral.bytes.ReadWriteError!void {
|
|
||||||
switch (expression.*) {
|
|
||||||
.float => |float| {
|
|
||||||
try coral.bytes.writeFormatted(source, "{whole}.{decimal}", float);
|
|
||||||
},
|
|
||||||
|
|
||||||
.int => |int| {
|
|
||||||
try coral.bytes.writeAll(source, int.literal);
|
|
||||||
},
|
|
||||||
|
|
||||||
.group_expression => |group_expression| {
|
|
||||||
try coral.bytes.writeAll(source, "(");
|
|
||||||
try generateExpression(source, group_expression);
|
|
||||||
try coral.bytes.writeAll(source, ")");
|
|
||||||
},
|
|
||||||
|
|
||||||
.add => |add| {
|
|
||||||
try generateExpression(source, add.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " + ");
|
|
||||||
try generateExpression(source, add.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.subtract => |subtract| {
|
|
||||||
try generateExpression(source, subtract.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " - ");
|
|
||||||
try generateExpression(source, subtract.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.multiply => |multiply| {
|
|
||||||
try generateExpression(source, multiply.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " * ");
|
|
||||||
try generateExpression(source, multiply.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.divide => |divide| {
|
|
||||||
try generateExpression(source, divide.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " / ");
|
|
||||||
try generateExpression(source, divide.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.equal => |equal| {
|
|
||||||
try generateExpression(source, equal.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " == ");
|
|
||||||
try generateExpression(source, equal.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.greater_than => |greater_than| {
|
|
||||||
try generateExpression(source, greater_than.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " > ");
|
|
||||||
try generateExpression(source, greater_than.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.greater_equal => |greater_equal| {
|
|
||||||
try generateExpression(source, greater_equal.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " >= ");
|
|
||||||
try generateExpression(source, greater_equal.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.lesser_than => |lesser_than| {
|
|
||||||
try generateExpression(source, lesser_than.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " < ");
|
|
||||||
try generateExpression(source, lesser_than.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.lesser_equal => |lesser_equal| {
|
|
||||||
try generateExpression(source, lesser_equal.lhs_expression);
|
|
||||||
try coral.bytes.writeAll(source, " <= ");
|
|
||||||
try generateExpression(source, lesser_equal.rhs_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.negate_expression => |negate_expression| {
|
|
||||||
try coral.bytes.writeAll(source, "-");
|
|
||||||
try generateExpression(source, negate_expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_local => |local| {
|
|
||||||
try coral.bytes.writeAll(source, local.identifier);
|
|
||||||
},
|
|
||||||
|
|
||||||
.mutate_local => |mutate_local| {
|
|
||||||
try coral.bytes.writeAll(source, mutate_local.local.identifier);
|
|
||||||
try coral.bytes.writeAll(source, " = ");
|
|
||||||
try generateExpression(source, mutate_local.expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_object => |get_object| {
|
|
||||||
try generateExpression(source, get_object.object_expression);
|
|
||||||
try coral.bytes.writeAll(source, ".");
|
|
||||||
try coral.bytes.writeAll(source, get_object.field.identifier);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_uniform => |get_uniform| {
|
|
||||||
try coral.bytes.writeFormatted(source, "{identifier}.", .{ .identifier = get_uniform.uniform.identifier });
|
|
||||||
try coral.bytes.writeAll(source, get_uniform.field.identifier);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_texture => |texture| {
|
|
||||||
try coral.bytes.writeAll(source, texture.identifier);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_parameter => |parameter| {
|
|
||||||
try coral.bytes.writeAll(source, parameter.identifier);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_output => |output| {
|
|
||||||
try coral.bytes.writeAll(source, output.identifier);
|
|
||||||
},
|
|
||||||
|
|
||||||
.mutate_output => |mutate_output| {
|
|
||||||
try coral.bytes.writeAll(source, mutate_output.output.identifier);
|
|
||||||
try coral.bytes.writeAll(source, " = ");
|
|
||||||
try generateExpression(source, mutate_output.expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_input => |input| {
|
|
||||||
try coral.bytes.writeAll(source, input.identifier);
|
|
||||||
},
|
|
||||||
|
|
||||||
.invoke => |invoke| {
|
|
||||||
try coral.bytes.writeFormatted(source, "{name}(", .{ .name = invoke.function.identifier });
|
|
||||||
|
|
||||||
if (invoke.has_argument) |first_argument| {
|
|
||||||
try generateArguments(source, first_argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeAll(source, ")");
|
|
||||||
},
|
|
||||||
|
|
||||||
.convert => |convert| {
|
|
||||||
try generateType(source, convert.target_type);
|
|
||||||
try coral.bytes.writeAll(source, "(");
|
|
||||||
try generateArguments(source, convert.first_argument);
|
|
||||||
try coral.bytes.writeAll(source, ")");
|
|
||||||
},
|
|
||||||
|
|
||||||
.pow => |pow| {
|
|
||||||
try coral.bytes.writeAll(source, "pow(");
|
|
||||||
try generateArguments(source, pow.first_argument);
|
|
||||||
try coral.bytes.writeAll(source, ")");
|
|
||||||
},
|
|
||||||
|
|
||||||
.abs => |abs| {
|
|
||||||
try coral.bytes.writeAll(source, "abs(");
|
|
||||||
try generateArguments(source, abs.first_argument);
|
|
||||||
try coral.bytes.writeAll(source, ")");
|
|
||||||
},
|
|
||||||
|
|
||||||
.sin => |sin| {
|
|
||||||
try coral.bytes.writeAll(source, "sin(");
|
|
||||||
try generateArguments(source, sin.first_argument);
|
|
||||||
try coral.bytes.writeAll(source, ")");
|
|
||||||
},
|
|
||||||
|
|
||||||
.sample => |sample| {
|
|
||||||
try coral.bytes.writeAll(source, "texture(");
|
|
||||||
|
|
||||||
if (sample.has_argument) |first_argument| {
|
|
||||||
try generateArguments(source, first_argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeAll(source, ")");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generateType(source: coral.bytes.Writable, @"type": *const coral.shaders.Type) coral.bytes.ReadWriteError!void {
|
|
||||||
if (@"type" == coral.shaders.Type.float2) {
|
|
||||||
return try coral.bytes.writeAll(source, "vec2");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (@"type" == coral.shaders.Type.float3) {
|
|
||||||
return try coral.bytes.writeAll(source, "vec3");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (@"type" == coral.shaders.Type.float4) {
|
|
||||||
return try coral.bytes.writeAll(source, "vec4");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (@"type" == coral.shaders.Type.float4x4) {
|
|
||||||
return try coral.bytes.writeAll(source, "mat4");
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeAll(source, @"type".identifier);
|
|
||||||
}
|
|
@ -1,625 +0,0 @@
|
|||||||
const coral = @import("../coral.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const AccessQualifier = enum(u32) {
|
|
||||||
read_only = 0,
|
|
||||||
write_only = 1,
|
|
||||||
read_write = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const AddressingModel = enum(u32) {
|
|
||||||
logical = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BuildError = std.mem.Allocator.Error || error{OutOfIds};
|
|
||||||
|
|
||||||
pub const Capability = enum(u32) {
|
|
||||||
matrix = 0,
|
|
||||||
shader = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Dim = enum(u32) {
|
|
||||||
@"1d" = 0,
|
|
||||||
@"2d" = 1,
|
|
||||||
@"3d" = 2,
|
|
||||||
cube = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ExecutionModel = enum(u32) {
|
|
||||||
fragment = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FloatType = struct {
|
|
||||||
id: u32,
|
|
||||||
bits: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FunctionControl = packed struct(u32) {
|
|
||||||
@"inline": bool = false,
|
|
||||||
dont_inline: bool = false,
|
|
||||||
pure: bool = false,
|
|
||||||
@"const": bool = false,
|
|
||||||
reserved: u28 = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FunctionType = struct {
|
|
||||||
id: u32,
|
|
||||||
parameter_types: []const *const Type,
|
|
||||||
return_type: *const Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ImageFormat = enum(u32) {
|
|
||||||
unknown = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ImageType = struct {
|
|
||||||
id: u32,
|
|
||||||
sampled_type: *const Type,
|
|
||||||
dimensions: Dim,
|
|
||||||
depth: u32,
|
|
||||||
arrayed: u32,
|
|
||||||
multi_sampled: u32,
|
|
||||||
sampled: u32,
|
|
||||||
format: ImageFormat,
|
|
||||||
access: AccessQualifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const IntType = struct {
|
|
||||||
id: u32,
|
|
||||||
bits: u32,
|
|
||||||
is_signed: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const MemoryModel = enum(u32) {
|
|
||||||
glsl450 = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Module = struct {
|
|
||||||
ids_assigned: u32 = 0,
|
|
||||||
capabilities: []const Capability,
|
|
||||||
memory_model: struct { AddressingModel, MemoryModel },
|
|
||||||
entry_points: PtrTree(*const coral.shaders.Function, EntryPoint) = .empty,
|
|
||||||
types: PtrTree(*allowzero const anyopaque, Type) = .empty,
|
|
||||||
inputs: PtrTree(*const coral.shaders.Input, Value) = .empty,
|
|
||||||
functions: PtrTree(*const coral.shaders.Function, Function) = .empty,
|
|
||||||
constants: PtrTree([*:0]const u8, Constant) = .empty,
|
|
||||||
|
|
||||||
pub const Block = struct {
|
|
||||||
id: u32,
|
|
||||||
variables: PtrTree(*const coral.shaders.Local, Value) = .empty,
|
|
||||||
instructions: coral.list.Linked(Instruction, 8) = .empty,
|
|
||||||
|
|
||||||
pub fn shaderExpression(self: *Block, module: *Module, function: *Function, arena: *std.heap.ArenaAllocator, shader_expression: *const coral.shaders.Expression) BuildError!*const Value {
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
|
|
||||||
switch (shader_expression.*) {
|
|
||||||
.group_expression => |expression| {
|
|
||||||
return self.shaderExpression(module, function, arena, expression);
|
|
||||||
},
|
|
||||||
|
|
||||||
.constant => |constant| {
|
|
||||||
return module.shaderConstant(arena, constant);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_input => |input| {
|
|
||||||
return module.shaderInput(arena, input);
|
|
||||||
},
|
|
||||||
|
|
||||||
.get_parameter => |parameter| {
|
|
||||||
return function.parameters.get(parameter) orelse {
|
|
||||||
@panic("parameter does not exist in function scope");
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
.convert => |_| {
|
|
||||||
// TODO: Review how type conversions are represented in AST.
|
|
||||||
unreachable;
|
|
||||||
},
|
|
||||||
|
|
||||||
.invoke => |invoke| {
|
|
||||||
const arguments = try arena.allocator().alloc(*const Value, invoke.argument_count);
|
|
||||||
|
|
||||||
{
|
|
||||||
var shader_arguments = invoke.arguments;
|
|
||||||
|
|
||||||
for (arguments) |*argument| {
|
|
||||||
argument.* = try self.shaderExpression(module, function, arena, shader_arguments.?.expression);
|
|
||||||
shader_arguments = shader_arguments.?.has_next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const instruction = try self.instructions.append(arena_allocator, .{
|
|
||||||
.call_function = .{
|
|
||||||
.arguments = arguments,
|
|
||||||
.function = try module.shaderFunction(arena, invoke.function),
|
|
||||||
|
|
||||||
.result = .{
|
|
||||||
.type = try module.shaderValueType(arena, invoke.function.has_return_type),
|
|
||||||
.id = try module.nextId(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return &instruction.call_function.result;
|
|
||||||
},
|
|
||||||
|
|
||||||
.add => |binary_operation| {
|
|
||||||
const instruction = try self.instructions.append(arena_allocator, .{
|
|
||||||
.add = .{
|
|
||||||
.operands = .{
|
|
||||||
try self.shaderExpression(module, function, arena, binary_operation.lhs_expression),
|
|
||||||
try self.shaderExpression(module, function, arena, binary_operation.rhs_expression),
|
|
||||||
},
|
|
||||||
|
|
||||||
.result = .{
|
|
||||||
.type = try module.shaderValueType(arena, binary_operation.type),
|
|
||||||
.id = try module.nextId(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return &instruction.add.result;
|
|
||||||
},
|
|
||||||
|
|
||||||
.subtract => |binary_operation| {
|
|
||||||
const instruction = try self.instructions.append(arena_allocator, .{
|
|
||||||
.subtract = .{
|
|
||||||
.operands = .{
|
|
||||||
try self.shaderExpression(module, function, arena, binary_operation.lhs_expression),
|
|
||||||
try self.shaderExpression(module, function, arena, binary_operation.rhs_expression),
|
|
||||||
},
|
|
||||||
|
|
||||||
.result = .{
|
|
||||||
.type = try module.shaderValueType(arena, binary_operation.type),
|
|
||||||
.id = try module.nextId(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return &instruction.subtract.result;
|
|
||||||
},
|
|
||||||
|
|
||||||
.multiply => |binary_operation| {
|
|
||||||
const instruction = try self.instructions.append(arena_allocator, .{
|
|
||||||
.add = .{
|
|
||||||
.operands = .{
|
|
||||||
try self.shaderExpression(module, function, arena, binary_operation.lhs_expression),
|
|
||||||
try self.shaderExpression(module, function, arena, binary_operation.rhs_expression),
|
|
||||||
},
|
|
||||||
|
|
||||||
.result = .{
|
|
||||||
.type = try module.shaderValueType(arena, binary_operation.type),
|
|
||||||
.id = try module.nextId(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return &instruction.multiply.result;
|
|
||||||
},
|
|
||||||
|
|
||||||
.divide => |binary_operation| {
|
|
||||||
const instruction = try self.instructions.append(arena_allocator, .{
|
|
||||||
.add = .{
|
|
||||||
.operands = .{
|
|
||||||
try self.shaderExpression(module, function, arena, binary_operation.lhs_expression),
|
|
||||||
try self.shaderExpression(module, function, arena, binary_operation.rhs_expression),
|
|
||||||
},
|
|
||||||
|
|
||||||
.result = .{
|
|
||||||
.type = try module.shaderValueType(arena, binary_operation.type),
|
|
||||||
.id = try module.nextId(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return &instruction.divide.result;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shaderVariable(self: *Block, module: *Module, function: *Function, arena: *std.heap.ArenaAllocator, local: *const coral.shaders.Local) BuildError!*const Value {
|
|
||||||
return self.variables.get(local) orelse {
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
|
|
||||||
const variable = (try self.variables.insert(arena_allocator, local, .{
|
|
||||||
.type = try module.shaderValueType(arena, local.type),
|
|
||||||
.id = try module.nextId(),
|
|
||||||
})).?;
|
|
||||||
|
|
||||||
_ = try self.instructions.append(arena_allocator, .{
|
|
||||||
.store = .{
|
|
||||||
.target = variable,
|
|
||||||
.source = try self.shaderExpression(module, function, arena, local.expression),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return variable;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Constant = struct {
|
|
||||||
value: Value,
|
|
||||||
data: []const u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const EntryPoint = struct {
|
|
||||||
id: u32,
|
|
||||||
function: *const Function,
|
|
||||||
execution_model: ExecutionModel,
|
|
||||||
interfaces: PtrTree(*const Value, void) = .empty,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Function = struct {
|
|
||||||
id: u32,
|
|
||||||
type: *const Type,
|
|
||||||
control: FunctionControl,
|
|
||||||
parameters: PtrTree(*const coral.shaders.Parameter, Value) = .empty,
|
|
||||||
block: Block,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Instruction = union(enum) {
|
|
||||||
store: struct { target: *const Value, source: *const Value },
|
|
||||||
call_function: FunctionCall,
|
|
||||||
add: BinaryOp,
|
|
||||||
subtract: BinaryOp,
|
|
||||||
multiply: BinaryOp,
|
|
||||||
divide: BinaryOp,
|
|
||||||
|
|
||||||
pub const BinaryOp = struct {
|
|
||||||
result: Value,
|
|
||||||
operands: [2]*const Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FunctionCall = struct {
|
|
||||||
result: Value,
|
|
||||||
function: *const Function,
|
|
||||||
arguments: []*const Value,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
fn PtrTree(comptime Ptr: type, comptime Node: type) type {
|
|
||||||
return coral.tree.Binary(Ptr, Node, coral.tree.scalarTraits(Ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nextId(self: *Module) BuildError!u32 {
|
|
||||||
self.ids_assigned = coral.scalars.add(self.ids_assigned, 1) orelse {
|
|
||||||
return error.OutOfIds;
|
|
||||||
};
|
|
||||||
|
|
||||||
return self.ids_assigned;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shaderConstant(self: *Module, arena: *std.heap.ArenaAllocator, shader_constant: [:0]const u8) BuildError!*const Value {
|
|
||||||
if (self.constants.get(shader_constant)) |constant| {
|
|
||||||
return &constant.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
const data = try arena_allocator.alloc(u32, 1);
|
|
||||||
|
|
||||||
if (std.mem.indexOfScalar(u8, shader_constant, '.') == null) {
|
|
||||||
data[0] = std.mem.nativeToLittle(u32, @bitCast(coral.utf8.DecFormat.c.parse(i32, shader_constant) orelse {
|
|
||||||
@panic("malformed constant");
|
|
||||||
}));
|
|
||||||
|
|
||||||
const constant = (try self.constants.insert(arena_allocator, shader_constant, .{
|
|
||||||
.data = data,
|
|
||||||
|
|
||||||
.value = .{
|
|
||||||
.type = try self.shaderValueType(arena, .int),
|
|
||||||
.id = try self.nextId(),
|
|
||||||
},
|
|
||||||
})).?;
|
|
||||||
|
|
||||||
return &constant.value;
|
|
||||||
} else {
|
|
||||||
data[0] = std.mem.nativeToLittle(u32, @bitCast(coral.utf8.DecFormat.c.parse(f32, shader_constant) orelse {
|
|
||||||
@panic("malformed constant");
|
|
||||||
}));
|
|
||||||
|
|
||||||
const constant = (try self.constants.insert(arena_allocator, shader_constant, .{
|
|
||||||
.data = data,
|
|
||||||
|
|
||||||
.value = .{
|
|
||||||
.type = try self.shaderValueType(arena, .float),
|
|
||||||
.id = try self.nextId(),
|
|
||||||
},
|
|
||||||
})).?;
|
|
||||||
|
|
||||||
return &constant.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shaderEntryPoint(
|
|
||||||
self: *Module,
|
|
||||||
arena: *std.heap.ArenaAllocator,
|
|
||||||
execution_model: ExecutionModel,
|
|
||||||
shader_function: *const coral.shaders.Function,
|
|
||||||
) BuildError!*const EntryPoint {
|
|
||||||
return self.entry_points.get(shader_function) orelse {
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
|
|
||||||
const entry_point = (try self.entry_points.insert(arena_allocator, shader_function, .{
|
|
||||||
.execution_model = execution_model,
|
|
||||||
.function = try self.shaderFunction(arena, shader_function),
|
|
||||||
.id = try self.nextId(),
|
|
||||||
})).?;
|
|
||||||
|
|
||||||
// try entry_point.registerBlockInterfaces(shader_function.block);
|
|
||||||
|
|
||||||
return entry_point;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shaderFunction(
|
|
||||||
self: *Module,
|
|
||||||
arena: *std.heap.ArenaAllocator,
|
|
||||||
shader_function: *const coral.shaders.Function,
|
|
||||||
) BuildError!*const Function {
|
|
||||||
return self.functions.get(shader_function) orelse {
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
|
|
||||||
const function = (try self.functions.insert(arena_allocator, shader_function, .{
|
|
||||||
.control = .{},
|
|
||||||
.type = try self.shaderFunctionType(arena, shader_function),
|
|
||||||
.id = try self.nextId(),
|
|
||||||
.block = .{ .id = try self.nextId() },
|
|
||||||
})).?;
|
|
||||||
|
|
||||||
{
|
|
||||||
var parameters = shader_function.parameters;
|
|
||||||
|
|
||||||
while (parameters) |parameter| : (parameters = parameter.has_next) {
|
|
||||||
_ = try function.parameters.insert(arena_allocator, parameter, .{
|
|
||||||
.type = try self.shaderValueType(arena, parameter.type),
|
|
||||||
.id = try self.nextId(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var statements = shader_function.block.statements;
|
|
||||||
|
|
||||||
while (statements) |statement| {
|
|
||||||
switch (statement.*) {
|
|
||||||
.declare_local => |local_declaration| {
|
|
||||||
_ = try function.block.shaderVariable(self, function, arena, local_declaration.local);
|
|
||||||
statements = local_declaration.has_next;
|
|
||||||
},
|
|
||||||
|
|
||||||
.mutate_local => {},
|
|
||||||
.mutate_output => {},
|
|
||||||
|
|
||||||
.return_expression => |_| {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return function;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shaderFunctionType(
|
|
||||||
self: *Module,
|
|
||||||
arena: *std.heap.ArenaAllocator,
|
|
||||||
shader_function: *const coral.shaders.Function,
|
|
||||||
) BuildError!*const Type {
|
|
||||||
const function_signature_address: *allowzero const anyopaque = @ptrCast(shader_function.signature);
|
|
||||||
|
|
||||||
return self.types.get(function_signature_address) orelse {
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
const parameter_types = try arena_allocator.alloc(*const Type, shader_function.parameter_count);
|
|
||||||
|
|
||||||
{
|
|
||||||
var parameters = shader_function.parameters;
|
|
||||||
|
|
||||||
for (parameter_types) |*parameter_type| {
|
|
||||||
parameter_type.* = try self.shaderValueType(arena, parameters.?.type);
|
|
||||||
parameters = parameters.?.has_next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (try self.types.insert(arena_allocator, function_signature_address, .{
|
|
||||||
.function = .{
|
|
||||||
.parameter_types = parameter_types,
|
|
||||||
.return_type = try self.shaderValueType(arena, shader_function.has_return_type),
|
|
||||||
.id = try self.nextId(),
|
|
||||||
},
|
|
||||||
})).?;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shaderInput(
|
|
||||||
self: *Module,
|
|
||||||
arena: *std.heap.ArenaAllocator,
|
|
||||||
shader_input: *const coral.shaders.Input,
|
|
||||||
) BuildError!*const Value {
|
|
||||||
return self.inputs.get(shader_input) orelse (try self.inputs.insert(arena.allocator(), shader_input, .{
|
|
||||||
.type = try self.shaderValueType(arena, shader_input.type),
|
|
||||||
.id = try self.nextId(),
|
|
||||||
})).?;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shaderValueType(
|
|
||||||
self: *Module,
|
|
||||||
arena: *std.heap.ArenaAllocator,
|
|
||||||
has_shader_type: ?*const coral.shaders.Type,
|
|
||||||
) BuildError!*const Type {
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
const symbol_address: *allowzero const anyopaque = @ptrCast(has_shader_type);
|
|
||||||
|
|
||||||
return self.types.get(symbol_address) orelse {
|
|
||||||
const id = try self.nextId();
|
|
||||||
|
|
||||||
const symbol = has_shader_type orelse {
|
|
||||||
return (try self.types.insert(arena_allocator, symbol_address, .{
|
|
||||||
.void = .{
|
|
||||||
.id = id,
|
|
||||||
},
|
|
||||||
})).?;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (symbol.layout) {
|
|
||||||
.signed, .unsigned => |scalar| {
|
|
||||||
return (try self.types.insert(arena_allocator, symbol_address, .{
|
|
||||||
.int = .{
|
|
||||||
.bits = scalar.bits,
|
|
||||||
.is_signed = (symbol.layout == .signed),
|
|
||||||
.id = id,
|
|
||||||
},
|
|
||||||
})).?;
|
|
||||||
},
|
|
||||||
|
|
||||||
.float => |float| {
|
|
||||||
return (try self.types.insert(arena_allocator, symbol_address, .{
|
|
||||||
.float = .{
|
|
||||||
.bits = float.bits,
|
|
||||||
.id = id,
|
|
||||||
},
|
|
||||||
})).?;
|
|
||||||
},
|
|
||||||
|
|
||||||
.vector => |vector| {
|
|
||||||
return (try self.types.insert(arena_allocator, symbol_address, .{
|
|
||||||
.vector = .{
|
|
||||||
.id = id,
|
|
||||||
.component_type = try self.shaderValueType(arena, vector.element),
|
|
||||||
.component_len = vector.dimensions.count(),
|
|
||||||
},
|
|
||||||
})).?;
|
|
||||||
},
|
|
||||||
|
|
||||||
.matrix, .record => unreachable,
|
|
||||||
|
|
||||||
.texture => {
|
|
||||||
const image_type = try arena_allocator.create(Type);
|
|
||||||
|
|
||||||
image_type.* = .{
|
|
||||||
.image = .{
|
|
||||||
.depth = 0,
|
|
||||||
.arrayed = 0,
|
|
||||||
.multi_sampled = 0,
|
|
||||||
.sampled = 1,
|
|
||||||
.dimensions = .@"2d",
|
|
||||||
.format = .unknown,
|
|
||||||
.access = .read_only,
|
|
||||||
.sampled_type = try self.shaderValueType(arena, .float),
|
|
||||||
.id = try self.nextId(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return (try self.types.insert(arena_allocator, symbol_address, .{
|
|
||||||
.sampled_image = .{
|
|
||||||
.image_type = image_type,
|
|
||||||
.id = try self.nextId(),
|
|
||||||
},
|
|
||||||
})).?;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn writeTo(self: Module, spirv: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
|
||||||
const Header = extern struct {
|
|
||||||
magic_number: u32 = 0x07230203, // SPIR-V magic number in little-endian
|
|
||||||
version: u32, // SPIR-V version (e.g., 0x00010000 for 1.0)
|
|
||||||
generator_magic: u32, // Tool or generator identifier
|
|
||||||
id_upper_bound: u32, // Upper bound for IDs
|
|
||||||
reserved: u32 = 0, // Reserved, always 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const OpInstruction = enum(u32) {
|
|
||||||
memory_model = 14,
|
|
||||||
entry_point = 15,
|
|
||||||
execution_mode = 16,
|
|
||||||
};
|
|
||||||
|
|
||||||
try coral.bytes.writeLittle(spirv, Header{
|
|
||||||
.magic_number = 0x07230203,
|
|
||||||
.version = 0x00010000,
|
|
||||||
.generator_magic = 0,
|
|
||||||
.id_upper_bound = self.ids_assigned,
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const addressing, const memory = self.memory_model;
|
|
||||||
|
|
||||||
try coral.bytes.writeLittle(spirv, OpInstruction.memory_model);
|
|
||||||
try coral.bytes.writeLittle(spirv, addressing);
|
|
||||||
try coral.bytes.writeLittle(spirv, memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var entry_points = self.entry_points.keyValues();
|
|
||||||
|
|
||||||
while (entry_points.next()) |entry_point| {
|
|
||||||
try coral.bytes.writeLittle(spirv, OpInstruction.entry_point);
|
|
||||||
try coral.bytes.writeLittle(spirv, entry_point.value.execution_model);
|
|
||||||
try coral.bytes.writeLittle(spirv, entry_point.value.function.id);
|
|
||||||
try writeString(spirv, entry_point.key.identifier);
|
|
||||||
|
|
||||||
var interfaces = entry_point.value.interfaces.keyValues();
|
|
||||||
|
|
||||||
while (interfaces.nextKey()) |interface| {
|
|
||||||
try coral.bytes.writeLittle(spirv, interface.*.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
try coral.bytes.writeLittle(spirv, OpInstruction.execution_mode);
|
|
||||||
try coral.bytes.writeLittle(spirv, entry_point.value.function.id);
|
|
||||||
try coral.bytes.writeLittle(spirv, @as(u32, 7));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Value = struct {
|
|
||||||
id: u32,
|
|
||||||
type: *const Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const SampledImageType = struct {
|
|
||||||
id: u32,
|
|
||||||
image_type: *const Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const StructType = struct {
|
|
||||||
id: u32,
|
|
||||||
member_types: []const *const Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Type = union(enum) {
|
|
||||||
void: VoidType,
|
|
||||||
float: FloatType,
|
|
||||||
vector: VectorType,
|
|
||||||
image: ImageType,
|
|
||||||
@"struct": StructType,
|
|
||||||
function: FunctionType,
|
|
||||||
int: IntType,
|
|
||||||
sampled_image: SampledImageType,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const VectorType = struct {
|
|
||||||
id: u32,
|
|
||||||
component_type: *const Type,
|
|
||||||
component_len: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const VoidType = struct {
|
|
||||||
id: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn writeString(spirv: coral.bytes.Writable, data: []const u8) coral.bytes.ReadWriteError!void {
|
|
||||||
const data_len_with_sentinel = data.len + 1;
|
|
||||||
const word_size = @sizeOf(u32);
|
|
||||||
const word_count = (data_len_with_sentinel + word_size) / word_size;
|
|
||||||
|
|
||||||
try coral.bytes.writeSentineled(spirv, data, 0);
|
|
||||||
|
|
||||||
const padding = (word_count * word_size) - data_len_with_sentinel;
|
|
||||||
|
|
||||||
try coral.bytes.writeN(spirv, "\x00", padding);
|
|
||||||
}
|
|
@ -1,494 +0,0 @@
|
|||||||
const coral = @import("../coral.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const ExpectationError = error{
|
|
||||||
UnexpectedToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Kind = enum {
|
|
||||||
end,
|
|
||||||
unknown,
|
|
||||||
newline,
|
|
||||||
keyword_function,
|
|
||||||
keyword_return,
|
|
||||||
keyword_sample,
|
|
||||||
keyword_float,
|
|
||||||
keyword_float4x4,
|
|
||||||
keyword_float2,
|
|
||||||
keyword_float3,
|
|
||||||
keyword_float4,
|
|
||||||
keyword_int,
|
|
||||||
keyword_let,
|
|
||||||
keyword_var,
|
|
||||||
keyword_pow,
|
|
||||||
keyword_abs,
|
|
||||||
keyword_sin,
|
|
||||||
identifier,
|
|
||||||
scalar,
|
|
||||||
symbol_plus,
|
|
||||||
symbol_minus,
|
|
||||||
symbol_comma,
|
|
||||||
symbol_arrow,
|
|
||||||
symbol_equals,
|
|
||||||
symbol_period,
|
|
||||||
symbol_asterisk,
|
|
||||||
symbol_paren_left,
|
|
||||||
symbol_paren_right,
|
|
||||||
symbol_brace_left,
|
|
||||||
symbol_brace_right,
|
|
||||||
symbol_double_equals,
|
|
||||||
symbol_lesser_than,
|
|
||||||
symbol_lesser_equals,
|
|
||||||
symbol_greater_than,
|
|
||||||
symbol_greater_equals,
|
|
||||||
symbol_forward_slash,
|
|
||||||
|
|
||||||
pub fn writeFormat(self: Kind, writer: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
|
||||||
try coral.bytes.writeAll(writer, self.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(self: Kind) [:0]const u8 {
|
|
||||||
return switch (self) {
|
|
||||||
.end => "end",
|
|
||||||
.unknown => "unknown",
|
|
||||||
.newline => "newline",
|
|
||||||
.keyword_function => "`function` keyword",
|
|
||||||
.keyword_return => "`return` keyword",
|
|
||||||
.keyword_sample => "`sample` keyword",
|
|
||||||
.keyword_float => "`float` keyword",
|
|
||||||
.keyword_float4x4 => "`float4x4` keyword",
|
|
||||||
.keyword_float2 => "`float2` keyword",
|
|
||||||
.keyword_float3 => "`float3` keyword",
|
|
||||||
.keyword_float4 => "`float4` keyword",
|
|
||||||
.keyword_int => "`int` keyword",
|
|
||||||
.keyword_let => "`let` keyword",
|
|
||||||
.keyword_var => "`var` keyword",
|
|
||||||
.keyword_pow => "`pow` keyword",
|
|
||||||
.keyword_abs => "`abs` keyword",
|
|
||||||
.keyword_sin => "`sin` keyword",
|
|
||||||
.identifier => "identifier",
|
|
||||||
.scalar => "scalar literal",
|
|
||||||
.symbol_plus => "`+`",
|
|
||||||
.symbol_minus => "`-`",
|
|
||||||
.symbol_comma => "`,`",
|
|
||||||
.symbol_arrow => "`->`",
|
|
||||||
.symbol_equals => "`=`",
|
|
||||||
.symbol_period => "`.`",
|
|
||||||
.symbol_asterisk => "`*`",
|
|
||||||
.symbol_paren_left => "`(`",
|
|
||||||
.symbol_paren_right => "`)`",
|
|
||||||
.symbol_brace_left => "`{`",
|
|
||||||
.symbol_brace_right => "`}`",
|
|
||||||
.symbol_double_equals => "`==`",
|
|
||||||
.symbol_lesser_than => "`<`",
|
|
||||||
.symbol_lesser_equals => "`<=`",
|
|
||||||
.symbol_greater_than => "`>`",
|
|
||||||
.symbol_greater_equals => "`>=`",
|
|
||||||
.symbol_forward_slash => "`/`",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Location = struct {
|
|
||||||
line_number: u32 = 1,
|
|
||||||
column_number: u32 = 1,
|
|
||||||
|
|
||||||
pub fn writeFormat(self: Location, writer: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
|
||||||
try coral.bytes.writeFormatted(writer, "({line}, {column})", .{
|
|
||||||
.line = coral.utf8.cDec(self.line_number),
|
|
||||||
.column = coral.utf8.cDec(self.column_number),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Stream = struct {
|
|
||||||
source: []const u8,
|
|
||||||
current: Token,
|
|
||||||
location: Location,
|
|
||||||
depth: u32 = 0,
|
|
||||||
|
|
||||||
pub fn isFinished(self: Stream) bool {
|
|
||||||
return self.source.len == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(source: []const u8) Stream {
|
|
||||||
return .{
|
|
||||||
.source = source,
|
|
||||||
.current = .end,
|
|
||||||
.location = .{},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next(self: *Stream) Token {
|
|
||||||
const initial_len = self.source.len;
|
|
||||||
|
|
||||||
self.current = consume(&self.source);
|
|
||||||
|
|
||||||
switch (self.current) {
|
|
||||||
.end => {
|
|
||||||
self.depth = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
.symbol_brace_left => {
|
|
||||||
self.depth += 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
.symbol_brace_right => {
|
|
||||||
self.depth -= 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
.newline => {
|
|
||||||
self.location.line_number += 1;
|
|
||||||
self.location.column_number = 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
const consumed_len: u32 = @intCast(initial_len - self.source.len);
|
|
||||||
|
|
||||||
self.location.column_number += consumed_len;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.current;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn skip(self: *Stream, skip_kind: Kind) Token {
|
|
||||||
while (true) {
|
|
||||||
if (std.meta.activeTag(self.next()) != skip_kind) {
|
|
||||||
return self.current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Token = union(Kind) {
|
|
||||||
end,
|
|
||||||
unknown: []const u8,
|
|
||||||
newline,
|
|
||||||
keyword_function,
|
|
||||||
keyword_return,
|
|
||||||
keyword_sample,
|
|
||||||
keyword_float,
|
|
||||||
keyword_float4x4,
|
|
||||||
keyword_float2,
|
|
||||||
keyword_float3,
|
|
||||||
keyword_float4,
|
|
||||||
keyword_int,
|
|
||||||
keyword_let,
|
|
||||||
keyword_var,
|
|
||||||
keyword_pow,
|
|
||||||
keyword_abs,
|
|
||||||
keyword_sin,
|
|
||||||
identifier: []const u8,
|
|
||||||
scalar: []const u8,
|
|
||||||
symbol_plus,
|
|
||||||
symbol_minus,
|
|
||||||
symbol_comma,
|
|
||||||
symbol_arrow,
|
|
||||||
symbol_equals,
|
|
||||||
symbol_period,
|
|
||||||
symbol_asterisk,
|
|
||||||
symbol_paren_left,
|
|
||||||
symbol_paren_right,
|
|
||||||
symbol_brace_left,
|
|
||||||
symbol_brace_right,
|
|
||||||
symbol_double_equals,
|
|
||||||
symbol_lesser_than,
|
|
||||||
symbol_lesser_equals,
|
|
||||||
symbol_greater_than,
|
|
||||||
symbol_greater_equals,
|
|
||||||
symbol_forward_slash,
|
|
||||||
|
|
||||||
pub fn ExpectedAnyEnum(comptime expecteds: []const Kind) type {
|
|
||||||
comptime var fields: [expecteds.len]std.builtin.Type.EnumField = undefined;
|
|
||||||
|
|
||||||
inline for (0..expecteds.len) |i| {
|
|
||||||
fields[i] = .{
|
|
||||||
.name = @tagName(expecteds[i]),
|
|
||||||
.value = i,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return @Type(.{
|
|
||||||
.@"enum" = .{
|
|
||||||
.tag_type = @typeInfo(Kind).@"enum".tag_type,
|
|
||||||
.fields = &fields,
|
|
||||||
.decls = &.{},
|
|
||||||
.is_exhaustive = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expect(self: Token, expected: Kind) ExpectationError!void {
|
|
||||||
if (std.meta.activeTag(self) != expected) {
|
|
||||||
return error.UnexpectedToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expectAny(self: Token, comptime expecteds: []const Kind) ExpectationError!ExpectedAnyEnum(expecteds) {
|
|
||||||
if (std.mem.indexOfScalar(Kind, expecteds, std.meta.activeTag(self))) |index| {
|
|
||||||
return @enumFromInt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.UnexpectedToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expectIdentifier(self: Token) ExpectationError![]const u8 {
|
|
||||||
try self.expect(.identifier);
|
|
||||||
|
|
||||||
return self.identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expectScalar(self: Token) ExpectationError![]const u8 {
|
|
||||||
try self.expect(.scalar);
|
|
||||||
|
|
||||||
return self.scalar;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn writeFormat(self: Token, writer: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
|
||||||
try switch (self) {
|
|
||||||
.unknown => |unknown| coral.bytes.writeFormatted(writer, "`{name}`", .{
|
|
||||||
.name = unknown,
|
|
||||||
}),
|
|
||||||
|
|
||||||
.identifier => |identifier| coral.bytes.writeFormatted(writer, "`{name}`", .{
|
|
||||||
.name = identifier,
|
|
||||||
}),
|
|
||||||
|
|
||||||
.scalar => |literal| coral.bytes.writeFormatted(writer, "`{name}`", .{
|
|
||||||
.name = literal,
|
|
||||||
}),
|
|
||||||
|
|
||||||
else => std.meta.activeTag(self).writeFormat(writer),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn consume(source: *[]const u8) Token {
|
|
||||||
var cursor = @as(usize, 0);
|
|
||||||
|
|
||||||
defer {
|
|
||||||
source.* = source.*[cursor..];
|
|
||||||
}
|
|
||||||
|
|
||||||
while (cursor < source.len) {
|
|
||||||
switch (source.*[cursor]) {
|
|
||||||
'#' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
while (cursor < source.len and source.*[cursor] != '\n') {
|
|
||||||
cursor += 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
' ', '\t' => {
|
|
||||||
cursor += 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
'\n' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .newline;
|
|
||||||
},
|
|
||||||
|
|
||||||
'0'...'9' => {
|
|
||||||
const begin = cursor;
|
|
||||||
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
while (cursor < source.len) {
|
|
||||||
switch (source.*[cursor]) {
|
|
||||||
'0'...'9' => {
|
|
||||||
cursor += 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
'.' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
while (cursor < source.len) {
|
|
||||||
switch (source.*[cursor]) {
|
|
||||||
'0'...'9' => {
|
|
||||||
cursor += 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{ .scalar = source.*[begin..cursor] };
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{ .scalar = source.*[begin..cursor] };
|
|
||||||
},
|
|
||||||
|
|
||||||
'A'...'Z', 'a'...'z', '_' => {
|
|
||||||
const begin = cursor;
|
|
||||||
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
while (cursor < source.len) {
|
|
||||||
switch (source.*[cursor]) {
|
|
||||||
'0'...'9', 'A'...'Z', 'a'...'z', '_' => cursor += 1,
|
|
||||||
else => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const identifier = source.*[begin..cursor];
|
|
||||||
|
|
||||||
std.debug.assert(identifier.len != 0);
|
|
||||||
|
|
||||||
const identifier_keyword_pairs = [_]struct { [:0]const u8, Kind }{
|
|
||||||
.{ "float", .keyword_float },
|
|
||||||
.{ "float2", .keyword_float2 },
|
|
||||||
.{ "float3", .keyword_float3 },
|
|
||||||
.{ "float4", .keyword_float4 },
|
|
||||||
.{ "float4x4", .keyword_float4x4 },
|
|
||||||
.{ "function", .keyword_function },
|
|
||||||
.{ "let", .keyword_let },
|
|
||||||
.{ "var", .keyword_var },
|
|
||||||
.{ "return", .keyword_return },
|
|
||||||
.{ "var", .keyword_var },
|
|
||||||
.{ "pow", .keyword_pow },
|
|
||||||
.{ "abs", .keyword_abs },
|
|
||||||
.{ "sin", .keyword_sin },
|
|
||||||
.{ "sample", .keyword_sample },
|
|
||||||
};
|
|
||||||
|
|
||||||
inline for (identifier_keyword_pairs) |pair| {
|
|
||||||
const keyword_identifier, const keyword = pair;
|
|
||||||
|
|
||||||
if (std.mem.eql(u8, identifier, keyword_identifier)) {
|
|
||||||
return keyword;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{ .identifier = identifier };
|
|
||||||
},
|
|
||||||
|
|
||||||
'{' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_brace_left;
|
|
||||||
},
|
|
||||||
|
|
||||||
'}' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_brace_right;
|
|
||||||
},
|
|
||||||
|
|
||||||
',' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_comma;
|
|
||||||
},
|
|
||||||
|
|
||||||
')' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_paren_right;
|
|
||||||
},
|
|
||||||
|
|
||||||
'(' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_paren_left;
|
|
||||||
},
|
|
||||||
|
|
||||||
'/' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_forward_slash;
|
|
||||||
},
|
|
||||||
|
|
||||||
'*' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_asterisk;
|
|
||||||
},
|
|
||||||
|
|
||||||
'-' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
if (cursor < source.len and source.*[cursor] == '>') {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_arrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
return .symbol_minus;
|
|
||||||
},
|
|
||||||
|
|
||||||
'+' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_plus;
|
|
||||||
},
|
|
||||||
|
|
||||||
'=' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
if (cursor < source.len and source.*[cursor] == '=') {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_double_equals;
|
|
||||||
}
|
|
||||||
|
|
||||||
return .symbol_equals;
|
|
||||||
},
|
|
||||||
|
|
||||||
'<' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
if (cursor < source.len and (source.*[cursor] == '=')) {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_lesser_equals;
|
|
||||||
}
|
|
||||||
|
|
||||||
return .symbol_lesser_than;
|
|
||||||
},
|
|
||||||
|
|
||||||
'>' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
if (cursor < source.len and (source.*[cursor] == '=')) {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_greater_equals;
|
|
||||||
}
|
|
||||||
|
|
||||||
return .symbol_greater_than;
|
|
||||||
},
|
|
||||||
|
|
||||||
'.' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return .symbol_period;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
const begin = cursor;
|
|
||||||
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
while (cursor < source.len and !std.ascii.isWhitespace(source.*[cursor])) {
|
|
||||||
cursor += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{ .unknown = source.*[begin..cursor] };
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .end;
|
|
||||||
}
|
|
238
src/coral/stack.zig
Normal file
238
src/coral/stack.zig
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
fn Generic(comptime Item: type, comptime Buffer: type) type {
|
||||||
|
return struct {
|
||||||
|
items: Buffer,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn clear(self: *Self) void {
|
||||||
|
self.items.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||||
|
self.items.deinit(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const empty = Self{
|
||||||
|
.items = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn isEmpty(self: Self) bool {
|
||||||
|
return self.items.len == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isFull(self: Self) bool {
|
||||||
|
return self.items.len == self.items.cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: Self) ?Item {
|
||||||
|
return if (self.isEmpty()) null else self.items.get(self.items.len - 1).?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(self: *Self) ?Item {
|
||||||
|
if (self.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tail_index = self.items.len - 1;
|
||||||
|
|
||||||
|
defer {
|
||||||
|
self.items.len = tail_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.items.get(tail_index).?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(self: *Self, item: Item) bool {
|
||||||
|
if (self.isFull()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = self.items.len;
|
||||||
|
|
||||||
|
self.items.len += 1;
|
||||||
|
|
||||||
|
std.debug.assert(self.items.set(index, item));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushAll(self: *Self, items: []const Item) bool {
|
||||||
|
const new_len = self.items.len + items.len;
|
||||||
|
|
||||||
|
if (new_len < self.cap) {
|
||||||
|
const index = self.items.len;
|
||||||
|
|
||||||
|
self.items.len = new_len;
|
||||||
|
|
||||||
|
std.debug.assert(self.items.sliced(index, self.items.len).copy(items));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushGrow(self: *Self, allocator: std.mem.Allocator, item: Item) std.mem.Allocator.Error!void {
|
||||||
|
if (self.isFull()) {
|
||||||
|
try self.items.grow(allocator, @max(1, self.items.cap));
|
||||||
|
}
|
||||||
|
|
||||||
|
std.debug.assert(self.push(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushMany(self: *Self, n: usize, item: Item) bool {
|
||||||
|
const new_len = self.items.len + n;
|
||||||
|
|
||||||
|
if (new_len > self.cap) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset = self.items.len;
|
||||||
|
|
||||||
|
self.items.len = new_len;
|
||||||
|
|
||||||
|
for (offset..(offset + n)) |i| {
|
||||||
|
std.debug.assert(self.items.set(i, item));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Parallel(comptime Item: type) type {
|
||||||
|
const item_fields = switch (@typeInfo(Item)) {
|
||||||
|
.@"struct" => |@"struct"| @"struct".fields,
|
||||||
|
else => @compileError("`Item` must be a struct type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const item_align = @alignOf(Item);
|
||||||
|
const item_size = @sizeOf(usize);
|
||||||
|
const byte_size = @sizeOf(u8);
|
||||||
|
|
||||||
|
return Generic(Item, struct {
|
||||||
|
ptr: [*]align(item_align) u8 = undefined,
|
||||||
|
len: u32 = 0,
|
||||||
|
cap: u32 = 0,
|
||||||
|
|
||||||
|
pub const Field = std.meta.FieldEnum(Item);
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||||
|
if (self.cap != 0) {
|
||||||
|
allocator.free(self.ptr[0 .. item_size * self.cap]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: Self, index: usize) ?Item {
|
||||||
|
if (index >= self.cap) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr: [*]u8 = self.ptr;
|
||||||
|
var item: Item = undefined;
|
||||||
|
|
||||||
|
inline for (item_fields) |item_field| {
|
||||||
|
@field(item, item_field.name) = @as([*]item_field.type, @ptrCast(@alignCast(ptr)))[index];
|
||||||
|
ptr += @sizeOf(item_field.type) * self.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow(self: *Self, allocator: std.mem.Allocator, amount: usize) std.mem.Allocator.Error!void {
|
||||||
|
const new_cap = std.math.cast(u32, @as(usize, self.cap + amount)) orelse {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reallocation = try allocator.alignedAlloc(u8, item_align, byte_size * new_cap);
|
||||||
|
const len_in_bytes = byte_size * self.len;
|
||||||
|
|
||||||
|
@memcpy(reallocation[0..len_in_bytes], self.ptr[0..len_in_bytes]);
|
||||||
|
allocator.free(self.ptr[0 .. byte_size * self.cap]);
|
||||||
|
|
||||||
|
self.ptr = reallocation.ptr;
|
||||||
|
self.cap = @intCast(new_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(self: Self, index: usize, item: Item) bool {
|
||||||
|
if (index >= self.cap) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr: [*]u8 = self.ptr;
|
||||||
|
|
||||||
|
inline for (item_fields) |item_field| {
|
||||||
|
@as([*]item_field.type, @ptrCast(@alignCast(ptr)))[index] = @field(item, item_field.name);
|
||||||
|
ptr += @sizeOf(item_field.type) * self.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slice(self: Self, comptime field: Field) []item_fields[@intFromEnum(field)].type {
|
||||||
|
const field_index = @intFromEnum(field);
|
||||||
|
var ptr: [*]u8 = self.ptr;
|
||||||
|
|
||||||
|
inline for (item_fields[0..field_index]) |item_field| {
|
||||||
|
ptr += @sizeOf(item_field.type) * self.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return @as([*]item_fields[field_index].type, @ptrCast(@alignCast(ptr)))[0..self.len];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Sequential(comptime Item: type) type {
|
||||||
|
return Generic(Item, struct {
|
||||||
|
ptr: [*]Item = undefined,
|
||||||
|
len: u32 = 0,
|
||||||
|
cap: u32 = 0,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||||
|
if (self.cap != 0) {
|
||||||
|
allocator.free(self.ptr[0..self.cap]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: *Self, index: usize) ?Item {
|
||||||
|
if (index >= self.cap) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.ptr[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow(self: *Self, allocator: std.mem.Allocator, amount: usize) std.mem.Allocator.Error!void {
|
||||||
|
const new_cap = std.math.cast(u32, @as(usize, self.cap + amount)) orelse {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.ptr = (try allocator.realloc(self.ptr[0..self.cap], new_cap)).ptr;
|
||||||
|
self.cap = @intCast(new_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(self: *Self, index: usize, value: Item) bool {
|
||||||
|
if (index >= self.cap) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ptr[index] = value;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slice(self: Self) []Item {
|
||||||
|
return self.ptr[0..self.len];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -4,6 +4,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
ona.realtime_app
|
ona.realtime_app
|
||||||
|
.with(.initModule(ona.hid))
|
||||||
.with(.initModule(ona.gfx))
|
.with(.initModule(ona.gfx))
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
pub usingnamespace @cImport({
|
|
||||||
@cInclude("SDL3/SDL.h");
|
|
||||||
});
|
|
@ -12,7 +12,11 @@ const std = @import("std");
|
|||||||
|
|
||||||
initialized_states: coral.map.Hashed(*const coral.TypeId, coral.Box, coral.map.scalarTraits(*const coral.TypeId)),
|
initialized_states: coral.map.Hashed(*const coral.TypeId, coral.Box, coral.map.scalarTraits(*const coral.TypeId)),
|
||||||
named_systems: coral.tree.Binary([]const u8, SystemGraph, coral.tree.sliceTraits([]const u8)),
|
named_systems: coral.tree.Binary([]const u8, SystemGraph, coral.tree.sliceTraits([]const u8)),
|
||||||
is_running: bool,
|
|
||||||
|
pub const Exit = union(enum) {
|
||||||
|
success,
|
||||||
|
failure: anyerror,
|
||||||
|
};
|
||||||
|
|
||||||
pub const RunError = std.mem.Allocator.Error || error{
|
pub const RunError = std.mem.Allocator.Error || error{
|
||||||
MissingDependency,
|
MissingDependency,
|
||||||
@ -65,13 +69,14 @@ pub fn init() std.mem.Allocator.Error!Self {
|
|||||||
var self = Self{
|
var self = Self{
|
||||||
.initialized_states = .empty,
|
.initialized_states = .empty,
|
||||||
.named_systems = .empty,
|
.named_systems = .empty,
|
||||||
.is_running = true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try self.setState(Time{
|
try self.setState(Time{
|
||||||
.elapsed = 0,
|
.elapsed = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try ona.registerChannel(&self, Exit);
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +101,7 @@ pub fn run(self: *Self, tasks: *coral.asio.TaskQueue, comptime schedule: anytype
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn setState(self: *Self, state: anytype) std.mem.Allocator.Error!void {
|
pub fn setState(self: *Self, state: anytype) std.mem.Allocator.Error!void {
|
||||||
var boxed_state = try coral.Box.init(state);
|
var boxed_state = try coral.Box.init(coral.heap.allocator, state);
|
||||||
|
|
||||||
errdefer {
|
errdefer {
|
||||||
boxed_state.deinit();
|
boxed_state.deinit();
|
||||||
|
@ -7,73 +7,41 @@ const ona = @import("../ona.zig");
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
label: [*:0]const u8,
|
label: [*:0]const u8,
|
||||||
on_insertion: *const fn (*const Self, *ona.App, *SystemGraph) std.mem.Allocator.Error!void,
|
onInsertion: *const fn (*const Self, std.mem.Allocator, *ona.App, *SystemGraph) std.mem.Allocator.Error![]coral.Box,
|
||||||
on_run: *const fn (*ona.App) void,
|
onRun: *const fn (*ona.App, []const coral.Box) void,
|
||||||
traits: ona.Traits,
|
is_blocking: bool,
|
||||||
|
|
||||||
const Param = struct {
|
const Param = struct {
|
||||||
|
uses_bind: bool,
|
||||||
init_params: []const InitParam,
|
init_params: []const InitParam,
|
||||||
has_bind: ?fn (*ona.App) void = null,
|
is_blocking: bool,
|
||||||
traits: ona.Traits = .{},
|
|
||||||
|
|
||||||
const InitParam = struct {
|
const InitParam = struct {
|
||||||
type: type,
|
type_id: *const coral.TypeId,
|
||||||
is_required: bool,
|
is_required: bool,
|
||||||
is_read_only: bool,
|
is_read_only: bool,
|
||||||
traits: ona.Traits,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn has_fn(comptime SystemParam: type, comptime name: []const u8) ?std.builtin.Type.Fn {
|
fn getInitParams(params: []const std.builtin.Type.Fn.Param) []const InitParam {
|
||||||
switch (@typeInfo(SystemParam)) {
|
|
||||||
.@"struct", .@"opaque" => {},
|
|
||||||
else => @compileError("system fn params must be either struct or opaque types"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if (@hasDecl(SystemParam, name)) {
|
|
||||||
return switch (@typeInfo(@TypeOf(@field(SystemParam, name)))) {
|
|
||||||
.@"fn" => |@"fn"| @"fn",
|
|
||||||
else => @compileError(std.fmt.comptimePrint("system params with a .{s} declaration must be made an fn type", .{name})),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(comptime fn_param: std.builtin.Type.Fn.Param) Param {
|
|
||||||
if (fn_param.is_generic) {
|
|
||||||
@compileError("generic params on behavior fns are disallowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
const init_fn = has_fn(fn_param.type.?, "init") orelse {
|
|
||||||
@compileError("behavior params must declare a .init fn");
|
|
||||||
};
|
|
||||||
|
|
||||||
if (init_fn.return_type != fn_param.type.?) {
|
|
||||||
const param_type_name = @typeName(fn_param.type.?);
|
|
||||||
|
|
||||||
@compileError(std.fmt.comptimePrint("{s}.init must return {s}", .{ param_type_name, param_type_name }));
|
|
||||||
}
|
|
||||||
|
|
||||||
const init_params = struct {
|
const init_params = struct {
|
||||||
const instance = get: {
|
const instance = get: {
|
||||||
var buffer: [init_fn.params.len]InitParam = undefined;
|
var buffer: [params.len]InitParam = undefined;
|
||||||
|
|
||||||
for (&buffer, init_fn.params) |*init_param, init_fn_param| {
|
for (&buffer, params) |*init_param, fn_param| {
|
||||||
if (init_fn_param.is_generic) {
|
const FnParam = fn_param.type orelse {
|
||||||
@compileError("generic params on Param.init fns are disallowed");
|
@compileError("generic params on Param.init fns are disallowed");
|
||||||
}
|
};
|
||||||
|
|
||||||
init_param.* = switch (@typeInfo(init_fn_param.type.?)) {
|
init_param.* = switch (@typeInfo(FnParam)) {
|
||||||
.pointer => |pointer| .{
|
.pointer => |pointer| .{
|
||||||
.type = pointer.child,
|
.type_id = .of(pointer.child),
|
||||||
.is_required = true,
|
.is_required = true,
|
||||||
.is_read_only = pointer.is_const,
|
.is_read_only = pointer.is_const,
|
||||||
.traits = if (@hasDecl(pointer.child, "traits") and @TypeOf(pointer.child.traits) == ona.Traits) pointer.child.traits else .{},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.optional => |optional| switch (@typeInfo(optional.child)) {
|
.optional => |optional| switch (@typeInfo(optional.child)) {
|
||||||
.pointer => |pointer| .{
|
.pointer => |pointer| .{
|
||||||
.type = pointer.child,
|
.type_id = .of(pointer.child),
|
||||||
.is_required = false,
|
.is_required = false,
|
||||||
.is_read_only = pointer.is_const,
|
.is_read_only = pointer.is_const,
|
||||||
},
|
},
|
||||||
@ -89,47 +57,50 @@ const Param = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var param = Param{
|
return &init_params.instance;
|
||||||
.init_params = &init_params.instance,
|
}
|
||||||
|
|
||||||
|
pub fn init(comptime fn_param: std.builtin.Type.Fn.Param) Param {
|
||||||
|
const FnParam = fn_param.type orelse {
|
||||||
|
@compileError("generic behavior params are not permitted");
|
||||||
};
|
};
|
||||||
|
|
||||||
if (has_fn(fn_param.type.?, "bind")) |bind_fn| {
|
const init_fn = hasFn(FnParam, "init", null) orelse {
|
||||||
const type_error_message = std.fmt.comptimePrint("{s}.bind must match the signature fn ({s}) !{s}", .{
|
@compileError(std.fmt.comptimePrint("{s} must contain a .init declaration", .{@typeName(FnParam)}));
|
||||||
@typeName(@TypeOf(fn_param.type.?.bind)),
|
|
||||||
@typeName(*ona.App),
|
|
||||||
@typeName(void),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (bind_fn.params.len != 1) {
|
|
||||||
@compileError(type_error_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bind_fn.params[0].type.? != *ona.App) {
|
|
||||||
@compileError(type_error_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
const binding = struct {
|
|
||||||
fn bind(app: *ona.App) void {
|
|
||||||
return switch (@typeInfo(bind_fn.return_type.?)) {
|
|
||||||
.error_union => coral.require(fn_param.type.?.bind, .{app}),
|
|
||||||
.void => fn_param.type.?.bind(app),
|
|
||||||
else => @compileError(type_error_message),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
param.has_bind = binding.bind;
|
comptime var uses_bind = false;
|
||||||
|
|
||||||
|
if (hasFn(FnParam, "bind", &.{})) |bind_fn| {
|
||||||
|
const State = switch (@typeInfo(bind_fn.return_type.?)) {
|
||||||
|
.error_union => |error_union| error_union.payload,
|
||||||
|
else => bind_fn.return_type.?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (init_fn.params.len == 0 or init_fn.params[0].type != *State) {
|
||||||
|
const fn_param_name = @typeName(FnParam);
|
||||||
|
|
||||||
|
@compileError(std.fmt.comptimePrint("The first parameter of fn {s}.init must be of type {s} while fn {s}.bind is present", .{
|
||||||
|
fn_param_name,
|
||||||
|
@typeName(*State),
|
||||||
|
fn_param_name,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (@hasDecl(fn_param.type.?, "traits") and @TypeOf(fn_param.type.?.traits) == ona.Traits) {
|
uses_bind = true;
|
||||||
param.traits = fn_param.type.?.traits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline for (param.init_params) |init_param| {
|
if (init_fn.return_type != FnParam) {
|
||||||
param.traits = param.traits.derived(init_param.traits);
|
const param_type_name = @typeName(FnParam);
|
||||||
|
|
||||||
|
@compileError(std.fmt.comptimePrint("Fn {s}.init must return {s}", .{ param_type_name, param_type_name }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return param;
|
return .{
|
||||||
|
.init_params = getInitParams(init_fn.params[@intFromBool(uses_bind)..]),
|
||||||
|
.is_blocking = usesTrait(FnParam, "blocking"),
|
||||||
|
.uses_bind = uses_bind,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -138,18 +109,18 @@ const Self = @This();
|
|||||||
pub fn after(comptime self: *const Self, comptime dependency: *const Self) *const Self {
|
pub fn after(comptime self: *const Self, comptime dependency: *const Self) *const Self {
|
||||||
const afters = struct {
|
const afters = struct {
|
||||||
fn on_insertion(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!void {
|
fn on_insertion(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!void {
|
||||||
try self.on_insertion(behavior, app, systems);
|
try self.onInsertion(behavior, app, systems);
|
||||||
try systems.depend_on_behavior(app, behavior, dependency);
|
try systems.depend_on_behavior(app, behavior, dependency);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_run(app: *ona.App) void {
|
fn on_run(app: *ona.App) void {
|
||||||
self.on_run(app);
|
self.onRun(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = Self{
|
const instance = Self{
|
||||||
.label = std.fmt.comptimePrint("({s} after {s})", .{ self.label, dependency.label }),
|
.label = std.fmt.comptimePrint("({s} after {s})", .{ self.label, dependency.label }),
|
||||||
.on_insertion = on_insertion,
|
.onInsertion = on_insertion,
|
||||||
.on_run = on_run,
|
.onRun = on_run,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -165,13 +136,61 @@ pub fn before(comptime self: *const Self, comptime dependant: *const Self) *cons
|
|||||||
|
|
||||||
const instance = Self{
|
const instance = Self{
|
||||||
.label = std.fmt.comptimePrint("({s} before {s})", .{ self.label, dependant.label }),
|
.label = std.fmt.comptimePrint("({s} before {s})", .{ self.label, dependant.label }),
|
||||||
.on_insertion = on_insertion,
|
.onInsertion = on_insertion,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return &afters.instance;
|
return &afters.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hasFn(comptime SystemParam: type, comptime name: []const u8, comptime has_expected_param_types: ?[]const type) ?std.builtin.Type.Fn {
|
||||||
|
switch (@typeInfo(SystemParam)) {
|
||||||
|
.@"struct", .@"opaque" => {},
|
||||||
|
|
||||||
|
else => @compileError(std.fmt.comptimePrint("{s} must be a struct or opaque types", .{
|
||||||
|
@typeName(SystemParam),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@hasDecl(SystemParam, name)) {
|
||||||
|
const named_fn = switch (@typeInfo(@TypeOf(@field(SystemParam, name)))) {
|
||||||
|
.@"fn" => |@"fn"| @"fn",
|
||||||
|
|
||||||
|
else => @compileError(std.fmt.comptimePrint("{s}.{s} declaration must be made an fn type", .{
|
||||||
|
@typeName(SystemParam),
|
||||||
|
name,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (has_expected_param_types) |expected_param_types| {
|
||||||
|
if (named_fn.params.len != expected_param_types.len) {
|
||||||
|
@compileError(std.fmt.comptimePrint("{s}.{s} fn must accept {d} parameters, not {d}", .{
|
||||||
|
@typeName(SystemParam),
|
||||||
|
name,
|
||||||
|
expected_param_types.len,
|
||||||
|
named_fn.params.len,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline for (named_fn.params, expected_param_types, 1..expected_param_types.len + 1) |fn_param, ExpectedParam, i| {
|
||||||
|
if (fn_param.type != ExpectedParam) {
|
||||||
|
@compileError(std.fmt.comptimePrint("Parameter {d} of {s}.{s} fn must be {s}, not {s}", .{
|
||||||
|
i,
|
||||||
|
@typeName(SystemParam),
|
||||||
|
name,
|
||||||
|
@typeName(ExpectedParam),
|
||||||
|
@typeName(fn_param.type.?),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return named_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn of(comptime call: anytype) *const Self {
|
pub fn of(comptime call: anytype) *const Self {
|
||||||
const Call = @TypeOf(call);
|
const Call = @TypeOf(call);
|
||||||
|
|
||||||
@ -181,41 +200,62 @@ pub fn of(comptime call: anytype) *const Self {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const behaviors = struct {
|
const behaviors = struct {
|
||||||
fn on_insertion(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!void {
|
fn onInsertion(behavior: *const Self, allocator: std.mem.Allocator, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error![]coral.Box {
|
||||||
|
var states: [call_fn.params.len]coral.Box = undefined;
|
||||||
|
comptime var states_written = 0;
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
inline for (states[states_written..]) |*state| {
|
||||||
|
state.deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline for (call_fn.params) |call_param| {
|
inline for (call_fn.params) |call_param| {
|
||||||
const behavior_param = Param.init(call_param);
|
const behavior_param = comptime Param.init(call_param);
|
||||||
|
|
||||||
inline for (behavior_param.init_params) |init_param| {
|
inline for (behavior_param.init_params) |init_param| {
|
||||||
try systems.dependOnType(app, behavior, .{
|
try systems.dependOnType(app, behavior, .{
|
||||||
.id = .of(init_param.type),
|
.id = init_param.type_id,
|
||||||
.is_read_only = init_param.is_read_only,
|
.is_read_only = init_param.is_read_only,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (behavior_param.has_bind) |bind| {
|
states[states_written] = try .init(allocator, if (behavior_param.uses_bind) call_param.type.?.bind() else {});
|
||||||
bind(app);
|
states_written += 1;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_run(app: *ona.App) void {
|
return allocator.dupe(coral.Box, &states);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn onRun(app: *ona.App, states: []const coral.Box) void {
|
||||||
var call_args: std.meta.ArgsTuple(Call) = undefined;
|
var call_args: std.meta.ArgsTuple(Call) = undefined;
|
||||||
|
|
||||||
inline for (&call_args, call_fn.params) |*call_arg, call_fn_param| {
|
std.debug.assert(states.len == call_args.len);
|
||||||
const call_param = Param.init(call_fn_param);
|
|
||||||
var init_args: std.meta.ArgsTuple(@TypeOf(call_fn_param.type.?.init)) = undefined;
|
|
||||||
|
|
||||||
inline for (&init_args, call_param.init_params) |*init_arg, init_param| {
|
inline for (0..call_args.len) |i| {
|
||||||
|
const fn_param = call_fn.params[i];
|
||||||
|
const param = comptime Param.init(fn_param);
|
||||||
|
var init_args: std.meta.ArgsTuple(@TypeOf(fn_param.type.?.init)) = undefined;
|
||||||
|
|
||||||
|
if (param.uses_bind) {
|
||||||
|
init_args[0] = states[i].has(@TypeOf(init_args[0].*)).?;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline for (@intFromBool(param.uses_bind)..init_args.len, param.init_params) |arg_index, init_param| {
|
||||||
if (init_param.is_required) {
|
if (init_param.is_required) {
|
||||||
init_arg.* = app.hasState(init_param.type) orelse {
|
const InitArg = @TypeOf(init_args[arg_index].*);
|
||||||
@panic(std.fmt.comptimePrint("{s} is a required state but not present in the App", .{@typeName(init_param.type)}));
|
|
||||||
|
init_args[arg_index] = app.hasState(InitArg) orelse {
|
||||||
|
@panic(std.fmt.comptimePrint("{s} is a required state but not present in the App", .{@typeName(InitArg)}));
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
init_arg.* = app.hasState(init_param.type);
|
const InitArg = @TypeOf(init_args[arg_index].*);
|
||||||
|
|
||||||
|
init_args[arg_index] = app.hasState(InitArg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
call_arg.* = @call(.auto, call_fn_param.type.?.init, init_args);
|
call_args[i] = @call(.auto, fn_param.type.?.init, init_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (@typeInfo(call_fn.return_type.?)) {
|
switch (@typeInfo(call_fn.return_type.?)) {
|
||||||
@ -230,20 +270,52 @@ pub fn of(comptime call: anytype) *const Self {
|
|||||||
|
|
||||||
const instance = Self{
|
const instance = Self{
|
||||||
.label = @typeName(Call),
|
.label = @typeName(Call),
|
||||||
.on_insertion = on_insertion,
|
.onInsertion = onInsertion,
|
||||||
.on_run = on_run,
|
.onRun = onRun,
|
||||||
|
|
||||||
.traits = derive: {
|
.is_blocking = check: {
|
||||||
var derived_traits = ona.Traits{};
|
for (call_fn.params) |fn_param| {
|
||||||
|
const param = Param.init(fn_param);
|
||||||
|
|
||||||
for (call_fn.params) |call_param| {
|
if (param.is_blocking) {
|
||||||
derived_traits = derived_traits.derived(Param.init(call_param).traits);
|
break :check true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break :derive derived_traits;
|
break :check false;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return &behaviors.instance;
|
return &behaviors.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn usesTrait(comptime SystemParam: type, name: []const u8) bool {
|
||||||
|
if (@hasDecl(SystemParam, "traits")) {
|
||||||
|
const Traits = @TypeOf(SystemParam.traits);
|
||||||
|
|
||||||
|
const traits_struct = switch (@typeInfo(Traits)) {
|
||||||
|
.@"struct" => |@"struct"| @"struct",
|
||||||
|
|
||||||
|
else => @compileError(std.fmt.comptimePrint("{s}.traits must be a struct type", .{
|
||||||
|
@typeName(SystemParam),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!traits_struct.is_tuple) {
|
||||||
|
@compileError(std.fmt.comptimePrint("{s}.traits must be a tuple", .{@typeName(SystemParam)}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (traits_struct.fields) |field| {
|
||||||
|
if (@typeInfo(field.type) != .enum_literal) {
|
||||||
|
@compileError("All members of tuple {s}.traits must be enum literals");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, @tagName(@field(SystemParam.traits, field.name)), name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -4,22 +4,49 @@ const ona = @import("../ona.zig");
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
dependants_edges: Map(BehaviorSet),
|
edges: Map(Edge),
|
||||||
processed: Map(void),
|
processed: Map(void),
|
||||||
state_readers: AccessMap,
|
state_readers: AccessMap,
|
||||||
state_writers: AccessMap,
|
state_writers: AccessMap,
|
||||||
blocking_work: BehaviorSet,
|
blocking_work: WorkSet,
|
||||||
parallel_work: BehaviorSet,
|
parallel_work: WorkSet,
|
||||||
parallel_work_ranges: coral.Stack(usize),
|
parallel_work_ranges: coral.stack.Sequential(usize),
|
||||||
|
|
||||||
const AccessMap = coral.tree.Binary(*const coral.TypeId, BehaviorSet, coral.tree.scalarTraits(*const coral.TypeId));
|
const AccessMap = coral.tree.Binary(*const coral.TypeId, BehaviorSet, coral.tree.scalarTraits(*const coral.TypeId));
|
||||||
|
|
||||||
const BehaviorSet = coral.Stack(*const ona.App.Behavior);
|
const BehaviorSet = coral.stack.Sequential(*const ona.App.Behavior);
|
||||||
|
|
||||||
|
const Edge = struct {
|
||||||
|
dependencies: BehaviorSet = .empty,
|
||||||
|
param_states: []coral.Box = &.{},
|
||||||
|
|
||||||
|
pub fn deinit(self: *Edge, allocator: std.mem.Allocator) void {
|
||||||
|
for (self.param_states) |*param_state| {
|
||||||
|
param_state.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator.free(self.param_states);
|
||||||
|
self.dependencies.deinit(allocator);
|
||||||
|
|
||||||
|
self.param_states = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fn Map(comptime Payload: type) type {
|
fn Map(comptime Payload: type) type {
|
||||||
return coral.tree.Binary(*const ona.App.Behavior, Payload, coral.tree.scalarTraits(*const ona.App.Behavior));
|
return coral.tree.Binary(*const ona.App.Behavior, Payload, coral.tree.scalarTraits(*const ona.App.Behavior));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Processed = struct {
|
||||||
|
is_blocking: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Work = struct {
|
||||||
|
behavior: *const ona.App.Behavior,
|
||||||
|
param_states: []const coral.Box = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
const WorkSet = coral.stack.Sequential(Work);
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub const RunError = error{
|
pub const RunError = error{
|
||||||
@ -33,15 +60,15 @@ pub const TypeDependency = struct {
|
|||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.processed.deinit(coral.heap.allocator);
|
self.processed.deinit(coral.heap.allocator);
|
||||||
self.parallel_work.deinit();
|
self.parallel_work.deinit(coral.heap.allocator);
|
||||||
self.parallel_work_ranges.deinit();
|
self.parallel_work_ranges.deinit(coral.heap.allocator);
|
||||||
self.blocking_work.deinit();
|
self.blocking_work.deinit(coral.heap.allocator);
|
||||||
|
|
||||||
inline for (.{ &self.dependants_edges, &self.state_readers, &self.state_writers }) |map| {
|
inline for (.{ &self.edges, &self.state_readers, &self.state_writers }) |map| {
|
||||||
var key_values = map.keyValues();
|
var key_values = map.keyValues();
|
||||||
|
|
||||||
while (key_values.nextValue()) |value| {
|
while (key_values.nextValue()) |value| {
|
||||||
value.deinit();
|
value.deinit(coral.heap.allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
map.deinit(coral.heap.allocator);
|
map.deinit(coral.heap.allocator);
|
||||||
@ -51,56 +78,56 @@ pub fn deinit(self: *Self) void {
|
|||||||
pub fn dependOnBehavior(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
pub fn dependOnBehavior(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||||
try self.insert(app, dependant);
|
try self.insert(app, dependant);
|
||||||
|
|
||||||
const edges = self.dependants_edges.get(dependant) orelse (try self.dependants_edges.insert(coral.heap.allocator, dependant, .empty)).?;
|
const edges = self.edges.get(dependant) orelse (try self.edges.insert(coral.heap.allocator, dependant, .{})).?;
|
||||||
|
|
||||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, edges.values, dependency) == null) {
|
if (std.mem.indexOfScalar(*const ona.App.Behavior, edges.dependencies.items.slice(), dependency) == null) {
|
||||||
try edges.pushGrow(dependency);
|
try edges.dependencies.pushGrow(coral.heap.allocator, dependency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dependOnType(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: TypeDependency) std.mem.Allocator.Error!void {
|
pub fn dependOnType(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: TypeDependency) std.mem.Allocator.Error!void {
|
||||||
const readers = self.state_readers.get(dependency.id) orelse (try self.state_readers.insert(coral.heap.allocator, dependency.id, .empty)).?;
|
const readers = self.state_readers.get(dependency.id) orelse (try self.state_readers.insert(coral.heap.allocator, dependency.id, .empty)).?;
|
||||||
|
|
||||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, readers.values, dependant)) |index| {
|
if (std.mem.indexOfScalar(*const ona.App.Behavior, readers.items.slice(), dependant)) |index| {
|
||||||
for (readers.values[0..index]) |reader| {
|
for (readers.items.slice()[0..index]) |reader| {
|
||||||
try self.dependOnBehavior(app, dependant, reader);
|
try self.dependOnBehavior(app, dependant, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (readers.values[index + 1 ..]) |reader| {
|
for (readers.items.slice()[index + 1 ..]) |reader| {
|
||||||
try self.dependOnBehavior(app, dependant, reader);
|
try self.dependOnBehavior(app, dependant, reader);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (readers.values) |reader| {
|
for (readers.items.slice()) |reader| {
|
||||||
try self.dependOnBehavior(app, dependant, reader);
|
try self.dependOnBehavior(app, dependant, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
try readers.pushGrow(dependant);
|
try readers.pushGrow(coral.heap.allocator, dependant);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dependency.is_read_only) {
|
if (!dependency.is_read_only) {
|
||||||
const writers = self.state_writers.get(dependency.id) orelse (try self.state_writers.insert(coral.heap.allocator, dependency.id, .empty)).?;
|
const writers = self.state_writers.get(dependency.id) orelse (try self.state_writers.insert(coral.heap.allocator, dependency.id, .empty)).?;
|
||||||
|
|
||||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, writers.values, dependant)) |index| {
|
if (std.mem.indexOfScalar(*const ona.App.Behavior, writers.items.slice(), dependant)) |index| {
|
||||||
for (writers.values[0..index]) |reader| {
|
for (writers.items.slice()[0..index]) |reader| {
|
||||||
try self.dependOnBehavior(app, dependant, reader);
|
try self.dependOnBehavior(app, dependant, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (writers.values[index..]) |reader| {
|
for (writers.items.slice()[index..]) |reader| {
|
||||||
try self.dependOnBehavior(app, dependant, reader);
|
try self.dependOnBehavior(app, dependant, reader);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (writers.values) |reader| {
|
for (writers.items.slice()) |reader| {
|
||||||
try self.dependOnBehavior(app, dependant, reader);
|
try self.dependOnBehavior(app, dependant, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
try writers.pushGrow(dependant);
|
try writers.pushGrow(coral.heap.allocator, dependant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const empty = Self{
|
pub const empty = Self{
|
||||||
|
.edges = .empty,
|
||||||
.processed = .empty,
|
.processed = .empty,
|
||||||
.dependants_edges = .empty,
|
|
||||||
.state_readers = .empty,
|
.state_readers = .empty,
|
||||||
.state_writers = .empty,
|
.state_writers = .empty,
|
||||||
.blocking_work = .empty,
|
.blocking_work = .empty,
|
||||||
@ -111,36 +138,43 @@ pub const empty = Self{
|
|||||||
pub fn insert(self: *Self, app: *ona.App, behavior: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
pub fn insert(self: *Self, app: *ona.App, behavior: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||||
self.processed.clear();
|
self.processed.clear();
|
||||||
|
|
||||||
if (try self.dependants_edges.insert(coral.heap.allocator, behavior, .empty) != null) {
|
if (try self.edges.insert(coral.heap.allocator, behavior, .{})) |edge| {
|
||||||
try behavior.on_insertion(behavior, app, self);
|
edge.param_states = try behavior.onInsertion(behavior, coral.heap.allocator, app, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parallelRun(app: *ona.App, behaviors: []const *const ona.App.Behavior) void {
|
fn parallelRun(app: *ona.App, works: []const Work) void {
|
||||||
for (behaviors) |behavior| {
|
for (works) |work| {
|
||||||
behavior.on_run(app);
|
work.behavior.onRun(app, work.param_states);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(self: *Self, behavior: *const ona.App.Behavior, dependencies: BehaviorSet) !ona.Traits {
|
fn process(self: *Self, behavior: *const ona.App.Behavior, edge: Edge) !Processed {
|
||||||
var inherited_traits = behavior.traits;
|
var processed = Processed{ .is_blocking = behavior.is_blocking };
|
||||||
|
|
||||||
for (dependencies.values) |dependency| {
|
for (edge.dependencies.items.slice()) |dependency| {
|
||||||
const traits = try self.process(dependency, (self.dependants_edges.get(dependency) orelse {
|
const processed_dependency = try self.process(dependency, (self.edges.get(dependency) orelse {
|
||||||
return error.MissingDependency;
|
return error.MissingDependency;
|
||||||
}).*);
|
}).*);
|
||||||
|
|
||||||
inherited_traits = inherited_traits.derived(traits);
|
processed.is_blocking = processed.is_blocking or processed_dependency.is_blocking;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try self.processed.insert(coral.heap.allocator, behavior, {}) != null) {
|
if (try self.processed.insert(coral.heap.allocator, behavior, {}) != null) {
|
||||||
try switch (inherited_traits.is_thread_unsafe) {
|
try switch (processed.is_blocking) {
|
||||||
true => self.blocking_work.pushGrow(behavior),
|
true => self.blocking_work.pushGrow(coral.heap.allocator, .{
|
||||||
false => self.parallel_work.pushGrow(behavior),
|
.behavior = behavior,
|
||||||
|
.param_states = edge.param_states,
|
||||||
|
}),
|
||||||
|
|
||||||
|
false => self.parallel_work.pushGrow(coral.heap.allocator, .{
|
||||||
|
.behavior = behavior,
|
||||||
|
.param_states = edge.param_states,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return inherited_traits;
|
return processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Allocator.Error || RunError)!void {
|
pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Allocator.Error || RunError)!void {
|
||||||
@ -153,19 +187,19 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
|||||||
self.parallel_work.clear();
|
self.parallel_work.clear();
|
||||||
self.parallel_work_ranges.clear();
|
self.parallel_work_ranges.clear();
|
||||||
|
|
||||||
var dependants_edges = self.dependants_edges.keyValues();
|
var dependants_edges = self.edges.keyValues();
|
||||||
|
|
||||||
while (dependants_edges.next()) |dependants_edge| {
|
while (dependants_edges.next()) |dependants_edge| {
|
||||||
const parallel_work_offset = self.parallel_work.values.len;
|
const parallel_work_offset = self.parallel_work.items.len;
|
||||||
const flags = try self.process(dependants_edge.key, dependants_edge.value.*);
|
const processed = try self.process(dependants_edge.key, dependants_edge.value.*);
|
||||||
|
|
||||||
if (flags.is_thread_unsafe) {
|
if (processed.is_blocking) {
|
||||||
std.debug.assert(parallel_work_offset == self.parallel_work.values.len);
|
std.debug.assert(parallel_work_offset == self.parallel_work.items.len);
|
||||||
} else {
|
} else {
|
||||||
const parallel_work_added = self.parallel_work.values.len - parallel_work_offset;
|
const parallel_work_added = self.parallel_work.items.len - parallel_work_offset;
|
||||||
|
|
||||||
if (parallel_work_added != 0) {
|
if (parallel_work_added != 0) {
|
||||||
try self.parallel_work_ranges.pushGrow(parallel_work_added);
|
try self.parallel_work_ranges.pushGrow(coral.heap.allocator, parallel_work_added);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,8 +207,8 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
|||||||
|
|
||||||
var parallel_work_offset: usize = 0;
|
var parallel_work_offset: usize = 0;
|
||||||
|
|
||||||
for (self.parallel_work_ranges.values) |parallel_work_range| {
|
for (self.parallel_work_ranges.items.slice()) |parallel_work_range| {
|
||||||
const work = self.parallel_work.values[parallel_work_offset .. parallel_work_offset + parallel_work_range];
|
const work = self.parallel_work.items.slice()[parallel_work_offset .. parallel_work_offset + parallel_work_range];
|
||||||
|
|
||||||
try tasks.execute(coral.asio.CallTask(parallelRun).init(.{ app, work }));
|
try tasks.execute(coral.asio.CallTask(parallelRun).init(.{ app, work }));
|
||||||
|
|
||||||
@ -183,7 +217,7 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
|||||||
|
|
||||||
tasks.finish();
|
tasks.finish();
|
||||||
|
|
||||||
for (self.blocking_work.values) |behavior| {
|
for (self.blocking_work.items.slice()) |work| {
|
||||||
behavior.on_run(app);
|
work.behavior.onRun(app, work.param_states);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
277
src/ona/gfx.zig
277
src/ona/gfx.zig
@ -2,26 +2,78 @@ const builtin = @import("builtin");
|
|||||||
|
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
const ext = @import("ext");
|
const ext = @cImport({
|
||||||
|
@cInclude("SDL3/SDL.h");
|
||||||
|
@cInclude("shaderc/shaderc.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
const glsl = @import("./gfx/glsl.zig");
|
||||||
|
|
||||||
const ona = @import("./ona.zig");
|
const ona = @import("./ona.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Display = struct {
|
|
||||||
width: u16,
|
|
||||||
height: u16,
|
|
||||||
is_hidden: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Context = struct {
|
const Context = struct {
|
||||||
window: *ext.SDL_Window,
|
window: *ext.SDL_Window,
|
||||||
|
frame_arena: std.heap.ArenaAllocator,
|
||||||
gpu_device: *ext.SDL_GPUDevice,
|
gpu_device: *ext.SDL_GPUDevice,
|
||||||
basic_shader: *ext.SDL_GPUShader = undefined,
|
shader_compiler: ext.shaderc_compiler_t,
|
||||||
|
spirv_options: ext.shaderc_compile_options_t,
|
||||||
|
|
||||||
|
pub const AssembleError = std.mem.Allocator.Error || error{BadSyntax};
|
||||||
|
|
||||||
|
pub const AssemblyKind = enum(c_int) {
|
||||||
|
vertex,
|
||||||
|
fragment,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn assembleShader(self: *Context, kind: AssemblyKind, name: [*:0]const u8, source: []const u8, injections: anytype) AssembleError![]const u8 {
|
||||||
|
const frame_allocator = self.frame_arena.allocator();
|
||||||
|
const injected_source = try glsl.inject(frame_allocator, source, 430, injections);
|
||||||
|
|
||||||
|
const result = ext.shaderc_compile_into_spv(self.shader_compiler, injected_source.ptr, injected_source.len, switch (kind) {
|
||||||
|
.fragment => ext.shaderc_glsl_fragment_shader,
|
||||||
|
.vertex => ext.shaderc_glsl_vertex_shader,
|
||||||
|
}, name, "main", self.spirv_options) orelse {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
};
|
||||||
|
|
||||||
|
defer {
|
||||||
|
ext.shaderc_result_release(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (ext.shaderc_result_get_compilation_status(result)) {
|
||||||
|
ext.shaderc_compilation_status_success => {
|
||||||
|
const compiled_len = ext.shaderc_result_get_length(result);
|
||||||
|
const compiled_ptr = ext.shaderc_result_get_bytes(result);
|
||||||
|
|
||||||
|
return frame_allocator.dupe(u8, compiled_ptr[0..compiled_len]);
|
||||||
|
},
|
||||||
|
|
||||||
|
ext.shaderc_compilation_status_compilation_error, ext.shaderc_compilation_status_invalid_stage => {
|
||||||
|
std.log.err("{s}", .{ext.shaderc_result_get_error_message(result)});
|
||||||
|
std.log.debug("problematic shader:\n{s}", .{injected_source});
|
||||||
|
|
||||||
|
return error.BadSyntax;
|
||||||
|
},
|
||||||
|
|
||||||
|
ext.shaderc_compilation_status_internal_error => @panic("shaderc internal compiler error"),
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assembleShaderEmbedded(self: *Context, kind: AssemblyKind, comptime path: [:0]const u8, injections: anytype) AssembleError![]const u8 {
|
||||||
|
return self.assembleShader(kind, path, @embedFile(path), injections);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Context) void {
|
pub fn deinit(self: *Context) void {
|
||||||
|
ext.shaderc_compile_options_release(self.spirv_options);
|
||||||
|
ext.shaderc_compiler_release(self.shader_compiler);
|
||||||
ext.SDL_DestroyGPUDevice(self.gpu_device);
|
ext.SDL_DestroyGPUDevice(self.gpu_device);
|
||||||
ext.SDL_DestroyWindow(self.window);
|
ext.SDL_DestroyWindow(self.window);
|
||||||
|
self.frame_arena.deinit();
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const traits = ona.Traits{
|
pub const traits = ona.Traits{
|
||||||
@ -29,124 +81,84 @@ const Context = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn compile_shaders(_: ona.Write(Context), assets: ona.Assets) !void {
|
pub const Display = struct {
|
||||||
var arena = std.heap.ArenaAllocator.init(coral.heap.allocator);
|
width: u16,
|
||||||
|
height: u16,
|
||||||
defer {
|
is_hidden: bool,
|
||||||
arena.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
const shader_path = "graphics_demo.eff";
|
|
||||||
var root = try coral.shaders.Root.init(&arena);
|
|
||||||
|
|
||||||
const Constants = extern struct {
|
|
||||||
screen_width: f32,
|
|
||||||
screen_height: f32,
|
|
||||||
time: f32,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try root.defineUniform(&arena, .{
|
pub const Effect = struct {
|
||||||
.identifier = "effect",
|
pipeline: *ext.SDL_GPUGraphicsPipeline,
|
||||||
.semantic = "Effect",
|
};
|
||||||
.binding = 0,
|
|
||||||
.has_field = .fieldsOf(Constants),
|
fn compile_shaders(context: ona.Exclusive(Context), _: ona.Assets(Effect)) !void {
|
||||||
|
const Camera = extern struct {
|
||||||
|
projection: [4]@Vector(4, f32),
|
||||||
|
};
|
||||||
|
|
||||||
|
const vertex_spirv = try context.ptr.assembleShaderEmbedded(.vertex, "./gfx/canvas.vert", .{
|
||||||
|
.model_xy = glsl.nativeInput(0, @Vector(2, f32)),
|
||||||
|
.model_uv = glsl.nativeInput(1, @Vector(2, f32)),
|
||||||
|
.model_rgba = glsl.nativeInput(2, @Vector(4, f32)),
|
||||||
|
.instance_uv_offset = glsl.nativeInput(3, @Vector(2, f32)),
|
||||||
|
.instance_uv_scale = glsl.nativeInput(4, @Vector(2, f32)),
|
||||||
|
.instance_xbasis = glsl.nativeInput(5, @Vector(2, f32)),
|
||||||
|
.instance_ybasis = glsl.nativeInput(6, @Vector(2, f32)),
|
||||||
|
.instance_origin = glsl.nativeInput(7, @Vector(2, f32)),
|
||||||
|
.instance_pivot = glsl.nativeInput(8, @Vector(2, f32)),
|
||||||
|
.instance_rgb = glsl.nativeInput(9, @Vector(3, f32)),
|
||||||
|
.instance_bits = glsl.nativeInput(10, u32),
|
||||||
|
.uv = glsl.nativeOutput(0, @Vector(2, f32)),
|
||||||
|
.rgba = glsl.nativeOutput(1, @Vector(4, f32)),
|
||||||
|
.camera = glsl.nativeUniformBlock(0, Camera),
|
||||||
});
|
});
|
||||||
|
|
||||||
try root.defineTexture(&arena, .{
|
const fragment_spirv = try context.ptr.assembleShaderEmbedded(.fragment, "./gfx/canvas.frag", .{
|
||||||
.identifier = "albedo",
|
.vertex_uv = glsl.nativeInput(0, @Vector(2, f32)),
|
||||||
.binding = 0,
|
.vertex_rgba = glsl.nativeInput(1, @Vector(4, f32)),
|
||||||
.layout = .dimensions_2,
|
.source_texture = glsl.Sampler{ .sampler_2d = 0 },
|
||||||
|
.color = glsl.nativeOutput(0, @Vector(4, f32)),
|
||||||
|
.camera = glsl.nativeUniformBlock(0, Camera),
|
||||||
});
|
});
|
||||||
|
|
||||||
try root.defineInput(&arena, .{
|
const effect_vertex = ext.SDL_CreateGPUShader(context.ptr.gpu_device, &.{
|
||||||
.identifier = "uv",
|
.code = vertex_spirv.ptr,
|
||||||
.type = .float2,
|
.code_size = vertex_spirv.len,
|
||||||
.location = 0,
|
.format = ext.SDL_GPU_SHADERFORMAT_SPIRV,
|
||||||
|
.stage = ext.SDL_GPU_SHADERSTAGE_VERTEX,
|
||||||
|
.entrypoint = "main",
|
||||||
});
|
});
|
||||||
|
|
||||||
try root.defineOutput(&arena, .{
|
|
||||||
.identifier = "color",
|
|
||||||
.type = .float4,
|
|
||||||
.location = 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (try root.parse(&arena, try assets.load(shader_path, arena.allocator()))) {
|
|
||||||
.ok => {
|
|
||||||
const spirv_module = try root.buildSpirvFragment(&arena, "frag");
|
|
||||||
var codes = coral.Stack(u8).empty;
|
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
codes.deinit();
|
ext.SDL_ReleaseGPUShader(context.ptr.gpu_device, effect_vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
try spirv_module.writeTo(coral.bytes.stackWriter(&codes));
|
const effect_fragment = ext.SDL_CreateGPUShader(context.ptr.gpu_device, &.{
|
||||||
|
.code = fragment_spirv.ptr,
|
||||||
std.log.info("{s}", .{codes.values});
|
.code_size = fragment_spirv.len,
|
||||||
},
|
.format = ext.SDL_GPU_SHADERFORMAT_SPIRV,
|
||||||
|
.stage = ext.SDL_GPU_SHADERSTAGE_FRAGMENT,
|
||||||
.failure => |failure| {
|
.entrypoint = "main",
|
||||||
std.log.err("failed to parse shader {s}: {s}", .{ shader_path, failure });
|
|
||||||
|
|
||||||
return error.ShaderParseFailure;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
@breakpoint();
|
|
||||||
|
|
||||||
// const basic_shader = ext.SDL_CreateGPUShader(gpu_device, &.{
|
|
||||||
// .code = spirv_code,
|
|
||||||
// .code_size = spirv_code.len,
|
|
||||||
// .stage = ext.SDL_GPU_SHADERSTAGE_FRAGMENT,
|
|
||||||
// .entrypoint = "frag_main",
|
|
||||||
// }) orelse {
|
|
||||||
// return sdl_failure("Failed to create basic shader");
|
|
||||||
// };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll(input_events: ona.Send(ona.hid.Event)) !void {
|
|
||||||
var event: ext.SDL_Event = undefined;
|
|
||||||
|
|
||||||
while (ext.SDL_PollEvent(&event)) {
|
|
||||||
try input_events.push(switch (event.type) {
|
|
||||||
ext.SDL_EVENT_QUIT => .quit,
|
|
||||||
ext.SDL_EVENT_KEY_UP => .{ .key_up = @enumFromInt(event.key.scancode) },
|
|
||||||
ext.SDL_EVENT_KEY_DOWN => .{ .key_down = @enumFromInt(event.key.scancode) },
|
|
||||||
|
|
||||||
ext.SDL_EVENT_MOUSE_BUTTON_UP => .{
|
|
||||||
.mouse_up = switch (event.button.button) {
|
|
||||||
ext.SDL_BUTTON_LEFT => .left,
|
|
||||||
ext.SDL_BUTTON_RIGHT => .right,
|
|
||||||
ext.SDL_BUTTON_MIDDLE => .middle,
|
|
||||||
else => continue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ext.SDL_EVENT_MOUSE_BUTTON_DOWN => .{
|
|
||||||
.mouse_down = switch (event.button.button) {
|
|
||||||
ext.SDL_BUTTON_LEFT => .left,
|
|
||||||
ext.SDL_BUTTON_RIGHT => .right,
|
|
||||||
ext.SDL_BUTTON_MIDDLE => .middle,
|
|
||||||
else => continue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ext.SDL_EVENT_MOUSE_MOTION => .{
|
|
||||||
.mouse_motion = .{
|
|
||||||
.relative_position = .{ event.motion.xrel, event.motion.yrel },
|
|
||||||
.absolute_position = .{ event.motion.x, event.motion.y },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
else => continue,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defer {
|
||||||
|
ext.SDL_ReleaseGPUShader(context.ptr.gpu_device, effect_fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _ = try effects.insert(.{
|
||||||
|
// .pipeline = ext.SDL_CreateGPUGraphicsPipeline(context.ptr.gpu_device, &.{
|
||||||
|
// .vertex_shader = effect_vertex,
|
||||||
|
// .fragment_shader = effect_fragment,
|
||||||
|
// .primitive_type = ext.SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
||||||
|
// }).?,
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare(display: ona.Write(Display)) void {
|
pub fn prepare(display: ona.Write(Display)) void {
|
||||||
display.ptr.is_hidden = false;
|
display.ptr.is_hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(context: ona.Read(Context)) !void {
|
pub fn render(context: ona.Exclusive(Context)) !void {
|
||||||
const commands = ext.SDL_AcquireGPUCommandBuffer(context.ptr.gpu_device) orelse {
|
const commands = ext.SDL_AcquireGPUCommandBuffer(context.ptr.gpu_device) orelse {
|
||||||
return error.SdlFailure;
|
return error.SdlFailure;
|
||||||
};
|
};
|
||||||
@ -155,16 +167,23 @@ pub fn render(context: ona.Read(Context)) !void {
|
|||||||
_ = !ext.SDL_CancelGPUCommandBuffer(commands);
|
_ = !ext.SDL_CancelGPUCommandBuffer(commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
var swapchain_texture: *ext.SDL_Texture = undefined;
|
var has_swapchain_texture: ?*ext.SDL_GPUTexture = null;
|
||||||
|
var sawpchain_width, var swapchain_height = [2]u32{ 0, 0 };
|
||||||
|
|
||||||
if (!ext.SDL_WaitAndAcquireGPUSwapchainTexture(commands, context.ptr.window, @ptrCast(&swapchain_texture), null, null)) {
|
if (!ext.SDL_WaitAndAcquireGPUSwapchainTexture(
|
||||||
|
commands,
|
||||||
|
context.ptr.window,
|
||||||
|
&has_swapchain_texture,
|
||||||
|
&sawpchain_width,
|
||||||
|
&swapchain_height,
|
||||||
|
)) {
|
||||||
return error.SdlFailure;
|
return error.SdlFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if (has_swapchain_texture) |swapchain_texture| {
|
||||||
const color_targets = [_]ext.SDL_GPUColorTargetInfo{
|
const color_targets = [_]ext.SDL_GPUColorTargetInfo{
|
||||||
.{
|
.{
|
||||||
.texture = @ptrCast(swapchain_texture),
|
.texture = swapchain_texture,
|
||||||
.load_op = ext.SDL_GPU_LOADOP_CLEAR,
|
.load_op = ext.SDL_GPU_LOADOP_CLEAR,
|
||||||
.clear_color = .{ .r = 0, .g = 0.2, .b = 0.4, .a = 1.0 },
|
.clear_color = .{ .r = 0, .g = 0.2, .b = 0.4, .a = 1.0 },
|
||||||
},
|
},
|
||||||
@ -215,19 +234,47 @@ pub fn setup(app: *ona.App) !void {
|
|||||||
return error.SdlFailure;
|
return error.SdlFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shader_compiler = ext.shaderc_compiler_initialize() orelse {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
};
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
ext.shaderc_compiler_release(shader_compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
const spirv_options = ext.shaderc_compile_options_initialize() orelse {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
};
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
ext.shaderc_compile_options_release(spirv_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.shaderc_compile_options_set_target_env(spirv_options, ext.shaderc_target_env_vulkan, ext.shaderc_env_version_vulkan_1_1);
|
||||||
|
|
||||||
|
ext.shaderc_compile_options_set_optimization_level(spirv_options, switch (builtin.mode) {
|
||||||
|
.Debug, .ReleaseSafe => ext.shaderc_optimization_level_zero,
|
||||||
|
.ReleaseSmall => ext.shaderc_optimization_level_size,
|
||||||
|
.ReleaseFast => ext.shaderc_optimization_level_performance,
|
||||||
|
});
|
||||||
|
|
||||||
|
try ona.registerAsset(app, Effect);
|
||||||
|
|
||||||
try app.setState(Context{
|
try app.setState(Context{
|
||||||
|
.frame_arena = .init(coral.heap.allocator),
|
||||||
|
.shader_compiler = shader_compiler,
|
||||||
|
.spirv_options = spirv_options,
|
||||||
.window = window,
|
.window = window,
|
||||||
.gpu_device = gpu_device,
|
.gpu_device = gpu_device,
|
||||||
});
|
});
|
||||||
|
|
||||||
try app.on(.load, .of(prepare));
|
try app.on(.load, .of(prepare));
|
||||||
try app.on(.load, .of(compile_shaders));
|
try app.on(.load, .of(compile_shaders));
|
||||||
try app.on(.post_update, .of(poll));
|
|
||||||
try app.on(.render, .of(render));
|
try app.on(.render, .of(render));
|
||||||
try app.on(.finish, .of(synchronize));
|
try app.on(.finish, .of(synchronize));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn synchronize(context: ona.Write(Context), display: ona.Read(Display)) !void {
|
pub fn synchronize(context: ona.Exclusive(Context), display: ona.Read(Display)) !void {
|
||||||
const window_flags = ext.SDL_GetWindowFlags(context.ptr.window);
|
const window_flags = ext.SDL_GetWindowFlags(context.ptr.window);
|
||||||
const is_window_hidden = (window_flags & ext.SDL_WINDOW_HIDDEN) != 0;
|
const is_window_hidden = (window_flags & ext.SDL_WINDOW_HIDDEN) != 0;
|
||||||
|
|
||||||
@ -241,4 +288,8 @@ pub fn synchronize(context: ona.Write(Context), display: ona.Read(Display)) !voi
|
|||||||
std.log.warn("failed to change window visibility", .{});
|
std.log.warn("failed to change window visibility", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!context.ptr.frame_arena.reset(.{ .retain_with_limit = 1024 * 1024 })) {
|
||||||
|
std.log.warn("failed to shrink frame arena during reset, freeing instead...", .{});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
4
src/ona/gfx/canvas.frag
Normal file
4
src/ona/gfx/canvas.frag
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
void main() {
|
||||||
|
color = vertex_rgba * texture(source_texture, vertex_uv);
|
||||||
|
}
|
12
src/ona/gfx/canvas.vert
Normal file
12
src/ona/gfx/canvas.vert
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
void main() {
|
||||||
|
const vec2 local_xy = model_xy + instance_pivot;
|
||||||
|
const vec2 world_position = instance_origin + (local_xy.x * instance_xbasis) + (local_xy.y * instance_ybasis);
|
||||||
|
const vec2 projected = (camera.projection * vec4(world_position, 0, 1)).xy;
|
||||||
|
const vec2 depth_alpha = unpackHalf2x16(instance_bits);
|
||||||
|
|
||||||
|
gl_Position = vec4(projected, depth_alpha.x, 1.0);
|
||||||
|
|
||||||
|
rgba = model_rgba * vec4(instance_rgb, depth_alpha.y);
|
||||||
|
uv = instance_uv_offset + (model_uv * instance_uv_scale);
|
||||||
|
}
|
197
src/ona/gfx/glsl.zig
Normal file
197
src/ona/gfx/glsl.zig
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
const ona = @import("../ona.zig");
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Field = struct {
|
||||||
|
name: [:0]const u8,
|
||||||
|
primitive: Primitive,
|
||||||
|
has_next: ?*const Field,
|
||||||
|
|
||||||
|
fn of(comptime uniform_fields: []const std.builtin.Type.StructField) *const Field {
|
||||||
|
if (uniform_fields.len == 0) {
|
||||||
|
@compileError("Struct contains no fields");
|
||||||
|
}
|
||||||
|
|
||||||
|
const first_uniform_field = uniform_fields[0];
|
||||||
|
|
||||||
|
const field = struct {
|
||||||
|
const instance = Field{
|
||||||
|
.primitive = nativePrimitive(first_uniform_field.type),
|
||||||
|
.name = first_uniform_field.name,
|
||||||
|
|
||||||
|
.has_next = switch (uniform_fields.len) {
|
||||||
|
1 => null,
|
||||||
|
else => .of(uniform_fields[1..]),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return &field.instance;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Input = struct {
|
||||||
|
location: usize,
|
||||||
|
primitive: Primitive,
|
||||||
|
|
||||||
|
pub fn serialize(self: Input, name: []const u8, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||||
|
return coral.bytes.writeFormatted(output, "layout (location = {location}) in {primitive} {name};\n", .{
|
||||||
|
.primitive = @tagName(self.primitive),
|
||||||
|
.location = coral.utf8.cDec(self.location),
|
||||||
|
.name = name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Output = struct {
|
||||||
|
location: usize,
|
||||||
|
primitive: Primitive,
|
||||||
|
|
||||||
|
pub fn serialize(self: Output, name: []const u8, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||||
|
return coral.bytes.writeFormatted(output, "layout (location = {location}) out {primitive} {name};\n", .{
|
||||||
|
.primitive = @tagName(self.primitive),
|
||||||
|
.location = coral.utf8.cDec(self.location),
|
||||||
|
.name = name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Primitive = enum {
|
||||||
|
float,
|
||||||
|
uint,
|
||||||
|
int,
|
||||||
|
vec2,
|
||||||
|
vec3,
|
||||||
|
vec4,
|
||||||
|
mat4,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Sampler = union(enum) {
|
||||||
|
sampler_2d: usize,
|
||||||
|
|
||||||
|
pub fn serialize(self: Sampler, name: []const u8, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||||
|
return switch (self) {
|
||||||
|
.sampler_2d => |binding| coral.bytes.writeFormatted(output, "layout (binding = {binding}) uniform sampler2D {name};\n", .{
|
||||||
|
.binding = coral.utf8.cDec(binding),
|
||||||
|
.name = name,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UniformBlock = struct {
|
||||||
|
name: [:0]const u8,
|
||||||
|
binding: usize,
|
||||||
|
field: *const Field,
|
||||||
|
|
||||||
|
pub fn serialize(self: UniformBlock, name: []const u8, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||||
|
var field = self.field;
|
||||||
|
|
||||||
|
try coral.bytes.writeFormatted(output, "layout (binding = {binding}) uniform {name} {{\n", .{
|
||||||
|
.binding = coral.utf8.cDec(self.binding),
|
||||||
|
.name = self.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
while (true) : (field = field.has_next orelse {
|
||||||
|
break;
|
||||||
|
}) {
|
||||||
|
try coral.bytes.writeFormatted(output, "\t{primitive} {name};\n", .{
|
||||||
|
.primitive = @tagName(field.primitive),
|
||||||
|
.name = field.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try coral.bytes.writeFormatted(output, "}} {namespace};\n", .{
|
||||||
|
.namespace = name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn nativeInput(location: usize, comptime Value: type) Input {
|
||||||
|
return .{
|
||||||
|
.primitive = nativePrimitive(Value),
|
||||||
|
.location = location,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nativePrimitive(comptime Value: type) Primitive {
|
||||||
|
return switch (Value) {
|
||||||
|
f32 => .float,
|
||||||
|
i32 => .int,
|
||||||
|
u32 => .uint,
|
||||||
|
@Vector(2, f32) => .vec2,
|
||||||
|
@Vector(3, f32) => .vec3,
|
||||||
|
@Vector(4, f32) => .vec4,
|
||||||
|
[4]@Vector(4, f32) => .mat4,
|
||||||
|
else => @compileError(std.fmt.comptimePrint("{s} is not a GLSL-compatible type", .{@typeName(Value)})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nativeOutput(location: usize, comptime Value: type) Output {
|
||||||
|
return .{
|
||||||
|
.primitive = nativePrimitive(Value),
|
||||||
|
.location = location,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nativeUniformBlock(binding: usize, comptime Struct: type) UniformBlock {
|
||||||
|
// TODO: Review how GLSL identifier-safe names are generated.
|
||||||
|
const struct_name = @typeName(Struct);
|
||||||
|
|
||||||
|
const struct_type = switch (@typeInfo(Struct)) {
|
||||||
|
.@"struct" => |@"struct"| @"struct",
|
||||||
|
else => @compileError("`Struct` must be a struct type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (struct_type.is_tuple) {
|
||||||
|
@compileError("`Struct` must be a non-tuple struct");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (struct_type.layout != .@"extern") {
|
||||||
|
@compileError("`Struct` must use an extern layout");
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.name = if (std.mem.lastIndexOfScalar(u8, struct_name, '.')) |index| struct_name[index + 1 ..] else struct_name,
|
||||||
|
.field = .of(struct_type.fields),
|
||||||
|
.binding = binding,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inject(allocator: std.mem.Allocator, source_base: []const u8, version: usize, interface: anytype) std.mem.Allocator.Error![:0]u8 {
|
||||||
|
const Interface = @TypeOf(interface);
|
||||||
|
|
||||||
|
const interface_info = switch (@typeInfo(Interface)) {
|
||||||
|
.@"struct" => |@"struct"| @"struct",
|
||||||
|
else => @compileError("`interface` must be a struct"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (interface_info.is_tuple) {
|
||||||
|
@compileError("`interface` cannot be a tuple");
|
||||||
|
}
|
||||||
|
|
||||||
|
const serialization = struct {
|
||||||
|
fn write(value: Interface, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||||
|
inline for (interface_info.fields) |field| {
|
||||||
|
const field_value = @field(value, field.name);
|
||||||
|
const FieldValue = @TypeOf(field_value);
|
||||||
|
|
||||||
|
if (!@hasDecl(FieldValue, "serialize")) {
|
||||||
|
@compileError(std.fmt.comptimePrint("`interface.{s}` is not a GLSL-serializable field", .{field.name}));
|
||||||
|
}
|
||||||
|
|
||||||
|
try field_value.serialize(field.name, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
try coral.bytes.writeAll(output, "#line 1\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return coral.bytes.allocFormatted(allocator, "#version {version}\n{injected_source}\n{original_source}\n", .{
|
||||||
|
.version = coral.utf8.cDec(version),
|
||||||
|
.injected_source = coral.bytes.altFormat(interface, serialization.write),
|
||||||
|
.original_source = source_base,
|
||||||
|
});
|
||||||
|
}
|
@ -1,5 +1,10 @@
|
|||||||
|
const ext = @cImport({
|
||||||
|
@cInclude("SDL3/SDL.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
const ona = @import("ona.zig");
|
||||||
|
|
||||||
pub const Event = union(enum) {
|
pub const Event = union(enum) {
|
||||||
quit,
|
|
||||||
key_up: KeyScancode,
|
key_up: KeyScancode,
|
||||||
key_down: KeyScancode,
|
key_down: KeyScancode,
|
||||||
mouse_up: MouseButton,
|
mouse_up: MouseButton,
|
||||||
@ -194,3 +199,58 @@ pub const KeyScancode = enum(u32) {
|
|||||||
right_gui = 0xE7,
|
right_gui = 0xE7,
|
||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn poll(exit: ona.Send(ona.App.Exit), hid_events: ona.Send(ona.hid.Event)) !void {
|
||||||
|
var event: ext.SDL_Event = undefined;
|
||||||
|
|
||||||
|
while (ext.SDL_PollEvent(&event)) {
|
||||||
|
try hid_events.push(switch (event.type) {
|
||||||
|
ext.SDL_EVENT_QUIT => {
|
||||||
|
return exit.push(.success);
|
||||||
|
},
|
||||||
|
|
||||||
|
ext.SDL_EVENT_KEY_UP => .{ .key_up = @enumFromInt(event.key.scancode) },
|
||||||
|
ext.SDL_EVENT_KEY_DOWN => .{ .key_down = @enumFromInt(event.key.scancode) },
|
||||||
|
|
||||||
|
ext.SDL_EVENT_MOUSE_BUTTON_UP => .{
|
||||||
|
.mouse_up = switch (event.button.button) {
|
||||||
|
ext.SDL_BUTTON_LEFT => .left,
|
||||||
|
ext.SDL_BUTTON_RIGHT => .right,
|
||||||
|
ext.SDL_BUTTON_MIDDLE => .middle,
|
||||||
|
|
||||||
|
else => {
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ext.SDL_EVENT_MOUSE_BUTTON_DOWN => .{
|
||||||
|
.mouse_down = switch (event.button.button) {
|
||||||
|
ext.SDL_BUTTON_LEFT => .left,
|
||||||
|
ext.SDL_BUTTON_RIGHT => .right,
|
||||||
|
ext.SDL_BUTTON_MIDDLE => .middle,
|
||||||
|
|
||||||
|
else => {
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ext.SDL_EVENT_MOUSE_MOTION => .{
|
||||||
|
.mouse_motion = .{
|
||||||
|
.relative_position = .{ event.motion.xrel, event.motion.yrel },
|
||||||
|
.absolute_position = .{ event.motion.x, event.motion.y },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(app: *ona.App) !void {
|
||||||
|
try app.on(.post_update, .of(poll));
|
||||||
|
try ona.registerChannel(app, Event);
|
||||||
|
}
|
||||||
|
211
src/ona/ona.zig
211
src/ona/ona.zig
@ -1,7 +1,5 @@
|
|||||||
pub const App = @import("./App.zig");
|
pub const App = @import("./App.zig");
|
||||||
|
|
||||||
pub const Assets = @import("./Assets.zig");
|
|
||||||
|
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
pub const gfx = @import("./gfx.zig");
|
pub const gfx = @import("./gfx.zig");
|
||||||
@ -10,24 +8,133 @@ pub const hid = @import("./hid.zig");
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const AssetPath = struct { u32 };
|
||||||
|
|
||||||
|
pub fn Assets(comptime Asset: type) type {
|
||||||
|
return struct {
|
||||||
|
queue: *Queue,
|
||||||
|
store: *Store,
|
||||||
|
|
||||||
|
pub const Handle = packed struct {
|
||||||
|
index: u32,
|
||||||
|
salt: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Queue = coral.stack.Sequential(struct {
|
||||||
|
asset_path: AssetPath,
|
||||||
|
reserved_handle: Handle,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
const State = struct {
|
||||||
|
queue: Queue,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Store = struct {
|
||||||
|
released_handles: coral.stack.Sequential(Handle) = .empty,
|
||||||
|
|
||||||
|
assets: coral.stack.Parallel(struct {
|
||||||
|
asset: Asset,
|
||||||
|
state: AssetState,
|
||||||
|
}) = .empty,
|
||||||
|
|
||||||
|
const AssetState = struct {
|
||||||
|
usage: enum(u32) { vacant, reserved, occupied },
|
||||||
|
salt: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn reserve(self: *Store) std.mem.Allocator.Error!Handle {
|
||||||
|
if (self.released_handles.pop()) |handle| {
|
||||||
|
const state = &self.assets.items.slice(.state)[handle.index];
|
||||||
|
|
||||||
|
std.debug.assert(state.usage == .vacant);
|
||||||
|
std.debug.assert(state.salt == handle.salt);
|
||||||
|
|
||||||
|
state.usage = .reserved;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handle = Handle{
|
||||||
|
.index = self.assets.items.len,
|
||||||
|
.salt = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
try self.assets.pushGrow(coral.heap.allocator, .{
|
||||||
|
.asset = undefined,
|
||||||
|
|
||||||
|
.state = .{
|
||||||
|
.usage = .reserved,
|
||||||
|
.salt = handle.salt,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(self: *Store, reserved_handle: Handle, asset: Asset) bool {
|
||||||
|
const state = self.assets.items.slice(.state)[reserved_handle.index];
|
||||||
|
|
||||||
|
if (state.usage != .reserved) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reserved_handle.salt != state.salt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assets.items.slice(.asset)[reserved_handle.index] = asset;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn bind() State {
|
||||||
|
return .{
|
||||||
|
.queue = .empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(state: *State, store: *Store) Self {
|
||||||
|
return .{
|
||||||
|
.store = store,
|
||||||
|
.queue = &state.queue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(self: Self, asset: Asset) std.mem.Allocator.Error!Handle {
|
||||||
|
const reserved_handle = try self.store.reserve();
|
||||||
|
|
||||||
|
std.debug.assert(self.store.resolve(reserved_handle, asset));
|
||||||
|
|
||||||
|
return reserved_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(self: Self, path: AssetPath) std.mem.Allocator.Error!Handle {
|
||||||
|
const reserved_handle = try self.store.reserve();
|
||||||
|
|
||||||
|
try self.queue.pushGrow(coral.heap.allocator, .{
|
||||||
|
.asset_path = path,
|
||||||
|
.reserved_handle = reserved_handle,
|
||||||
|
});
|
||||||
|
|
||||||
|
return reserved_handle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn Channel(comptime Message: type) type {
|
fn Channel(comptime Message: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
buffers: [2]coral.Stack(Message) = .{ .empty, .empty },
|
buffers: [2]coral.stack.Sequential(Message) = .{ .empty, .empty },
|
||||||
swap_index: u1 = 0,
|
swap_index: u1 = 0,
|
||||||
ticks: u1 = 0,
|
ticks: u1 = 0,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn bind(app: *App) std.mem.Allocator.Error!void {
|
|
||||||
if (app.hasState(Self) == null) {
|
|
||||||
try app.setState(Self{});
|
|
||||||
try app.on(.post_update, .of(swap));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
for (&self.buffers) |*buffer| {
|
for (&self.buffers) |*buffer| {
|
||||||
buffer.deinit();
|
buffer.deinit(coral.heap.allocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,15 +149,31 @@ fn Channel(comptime Message: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn messages(self: Self) []const Message {
|
fn messages(self: Self) []const Message {
|
||||||
return self.buffers[self.swap_index ^ 1].values;
|
return self.buffers[self.swap_index ^ 1].items.slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(self: *Self, message: Message) std.mem.Allocator.Error!void {
|
fn push(self: *Self, message: Message) std.mem.Allocator.Error!void {
|
||||||
try self.buffers[self.swap_index].pushGrow(message);
|
try self.buffers[self.swap_index].pushGrow(coral.heap.allocator, message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn Exclusive(comptime State: type) type {
|
||||||
|
return struct {
|
||||||
|
ptr: *State,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn init(state: *State) Self {
|
||||||
|
return .{
|
||||||
|
.ptr = state,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const traits = .{.blocking};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn Read(comptime State: type) type {
|
pub fn Read(comptime State: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
ptr: *const State,
|
ptr: *const State,
|
||||||
@ -66,38 +189,36 @@ pub fn Read(comptime State: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn Receive(comptime Message: type) type {
|
pub fn Receive(comptime Message: type) type {
|
||||||
const TypedChannel = Channel(Message);
|
const MessageChannel = Channel(Message);
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
channel: *const TypedChannel,
|
has_channel: ?*const MessageChannel,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub const bind = TypedChannel.bind;
|
pub fn init(channel: ?*const MessageChannel) Self {
|
||||||
|
|
||||||
pub fn init(channel: *const TypedChannel) Self {
|
|
||||||
return .{
|
return .{
|
||||||
.channel = channel,
|
.channel = channel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn messages(self: Self) []const Message {
|
pub fn messages(self: Self) []const Message {
|
||||||
return self.channel.messages();
|
if (self.has_channel) |channel| {
|
||||||
|
return channel.messages();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Send(comptime Message: type) type {
|
pub fn Send(comptime Message: type) type {
|
||||||
const TypedChannel = Channel(Message);
|
const MessageChannel = Channel(Message);
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
channel: *TypedChannel,
|
channel: *MessageChannel,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub const bind = TypedChannel.bind;
|
pub fn init(channel: *MessageChannel) Self {
|
||||||
|
|
||||||
pub fn init(channel: *TypedChannel) Self {
|
|
||||||
return .{
|
return .{
|
||||||
.channel = channel,
|
.channel = channel,
|
||||||
};
|
};
|
||||||
@ -109,16 +230,6 @@ pub fn Send(comptime Message: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Traits = packed struct {
|
|
||||||
is_thread_unsafe: bool = false,
|
|
||||||
|
|
||||||
pub fn derived(self: Traits, other: Traits) Traits {
|
|
||||||
return .{
|
|
||||||
.is_thread_unsafe = self.is_thread_unsafe or other.is_thread_unsafe,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn Write(comptime State: type) type {
|
pub fn Write(comptime State: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
ptr: *State,
|
ptr: *State,
|
||||||
@ -135,12 +246,26 @@ pub fn Write(comptime State: type) type {
|
|||||||
|
|
||||||
pub const realtime_app = App.Setup.init(run_realtime_loop);
|
pub const realtime_app = App.Setup.init(run_realtime_loop);
|
||||||
|
|
||||||
|
pub fn registerAsset(app: *App, comptime Asset: type) std.mem.Allocator.Error!void {
|
||||||
|
const AssetStore = Assets(Asset).Store;
|
||||||
|
|
||||||
|
try app.setState(AssetStore{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn registerChannel(app: *App, comptime Message: type) std.mem.Allocator.Error!void {
|
||||||
|
const MessageChannel = Channel(Message);
|
||||||
|
|
||||||
|
try app.on(.post_update, .of(MessageChannel.swap));
|
||||||
|
try app.setState(MessageChannel{});
|
||||||
|
}
|
||||||
|
|
||||||
fn run_realtime_loop(app: *App) !void {
|
fn run_realtime_loop(app: *App) !void {
|
||||||
// ext.SDL_SetLogPriorities(ext.SDL_LOG_PRIORITY_VERBOSE);
|
// ext.SDL_SetLogPriorities(ext.SDL_LOG_PRIORITY_VERBOSE);
|
||||||
// ext.SDL_SetLogOutputFunction(sdl_log, null);
|
// ext.SDL_SetLogOutputFunction(sdl_log, null);
|
||||||
const updates_per_frame = 60.0;
|
const updates_per_frame = 60.0;
|
||||||
const target_frame_time = 1.0 / updates_per_frame;
|
const target_frame_time = 1.0 / updates_per_frame;
|
||||||
const time = app.hasState(App.Time).?;
|
const time = app.hasState(App.Time).?;
|
||||||
|
const exit_channel = app.hasState(Channel(App.Exit)).?;
|
||||||
|
|
||||||
const virtual_thread_count = std.Thread.getCpuCount() catch 0;
|
const virtual_thread_count = std.Thread.getCpuCount() catch 0;
|
||||||
var tasks = try coral.asio.TaskQueue.init(virtual_thread_count / 2);
|
var tasks = try coral.asio.TaskQueue.init(virtual_thread_count / 2);
|
||||||
@ -155,7 +280,7 @@ fn run_realtime_loop(app: *App) !void {
|
|||||||
var ticks_previous = ticks_initial;
|
var ticks_previous = ticks_initial;
|
||||||
var accumulated_time = @as(f64, 0);
|
var accumulated_time = @as(f64, 0);
|
||||||
|
|
||||||
while (app.is_running) {
|
while (true) {
|
||||||
const ticks_current = std.time.milliTimestamp();
|
const ticks_current = std.time.milliTimestamp();
|
||||||
const milliseconds_per_second = 1000.0;
|
const milliseconds_per_second = 1000.0;
|
||||||
|
|
||||||
@ -172,7 +297,21 @@ fn run_realtime_loop(app: *App) !void {
|
|||||||
try app.run(&tasks, .post_update);
|
try app.run(&tasks, .post_update);
|
||||||
try app.run(&tasks, .render);
|
try app.run(&tasks, .render);
|
||||||
try app.run(&tasks, .finish);
|
try app.run(&tasks, .finish);
|
||||||
}
|
|
||||||
|
|
||||||
|
const exit_messages = exit_channel.messages();
|
||||||
|
|
||||||
|
if (exit_messages.len != 0) {
|
||||||
try app.run(&tasks, .exit);
|
try app.run(&tasks, .exit);
|
||||||
|
|
||||||
|
switch (exit_messages[exit_messages.len - 1]) {
|
||||||
|
.success => {
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
|
||||||
|
.failure => |failure| {
|
||||||
|
return failure;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
testing.spv
(Stored with Git LFS)
BIN
testing.spv
(Stored with Git LFS)
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user