Add initial SPIR-V shader backend
This commit is contained in:
parent
c87606d425
commit
d5a61d3642
@ -38,6 +38,17 @@ pub const ReadWriteSpan = struct {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn put(self: *ReadWriteSpan, byte: u8) bool {
|
||||
if (self.write_cursor >= self.bytes.len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.bytes[self.write_cursor] = byte;
|
||||
self.write_cursor += 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn write(self: *ReadWriteSpan, buffer: []const u8) usize {
|
||||
const written = @min(buffer.len, self.bytes.len - self.write_cursor);
|
||||
|
||||
@ -60,6 +71,20 @@ pub fn Span(comptime Ptr: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub const WriteCount = struct {
|
||||
written: usize = 0,
|
||||
|
||||
fn write(self: *WriteCount, input: []const u8) usize {
|
||||
self.written += input.len;
|
||||
|
||||
return input.len;
|
||||
}
|
||||
|
||||
fn writable(self: *WriteCount) Writable {
|
||||
return .initRef(self, WriteCount.write);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Writable = coral.Callable(usize, &.{[]const u8});
|
||||
|
||||
pub const null_writer = Writable.initFn(writeNull);
|
||||
@ -77,21 +102,9 @@ pub fn allocFormatted(allocator: std.mem.Allocator, comptime format: []const u8,
|
||||
}
|
||||
|
||||
pub fn countFormatted(comptime format: []const u8, args: anytype) usize {
|
||||
const Counter = struct {
|
||||
written: usize,
|
||||
var count = WriteCount{};
|
||||
|
||||
const Self = @This();
|
||||
|
||||
fn write(self: *Self, input: []const u8) usize {
|
||||
self.written += input.len;
|
||||
|
||||
return input.len;
|
||||
}
|
||||
};
|
||||
|
||||
var count = Counter{ .written = 0 };
|
||||
|
||||
writeFormatted(.initRef(&count, Counter.write), format, args) catch unreachable;
|
||||
writeFormatted(count.writable(), format, args) catch unreachable;
|
||||
|
||||
return count.written;
|
||||
}
|
||||
@ -192,6 +205,53 @@ pub fn writeN(output: Writable, data: []const u8, count: usize) ReadWriteError!v
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeFormatable(output: Writable, formatable: anytype) ReadWriteError!void {
|
||||
const Formatable = @TypeOf(formatable);
|
||||
|
||||
switch (@typeInfo(Formatable)) {
|
||||
.pointer => |pointer| {
|
||||
const error_message = std.fmt.comptimePrint("{s} is not a string-like type", .{@typeName(Formatable)});
|
||||
|
||||
switch (pointer.size) {
|
||||
.one => switch (@typeInfo(pointer.child)) {
|
||||
.array => |array| switch (array.child == u8) {
|
||||
true => try coral.bytes.writeAll(output, formatable.*[0..]),
|
||||
false => @compileError(error_message),
|
||||
},
|
||||
|
||||
.@"struct", .@"union", .@"enum" => {
|
||||
try formatable.writeFormat(output);
|
||||
},
|
||||
|
||||
else => @compileError(error_message),
|
||||
},
|
||||
|
||||
.slice => switch (pointer.child == u8) {
|
||||
true => try coral.bytes.writeAll(output, formatable),
|
||||
false => @compileError(error_message),
|
||||
},
|
||||
|
||||
.many => switch ((pointer.sentinel() != null) and (pointer.child == u8)) {
|
||||
true => try coral.bytes.writeAll(output, std.mem.span(formatable)),
|
||||
false => @compileError(error_message),
|
||||
},
|
||||
|
||||
else => false,
|
||||
}
|
||||
},
|
||||
|
||||
.@"struct", .@"union", .@"enum" => {
|
||||
try formatable.writeFormat(output);
|
||||
},
|
||||
|
||||
else => {
|
||||
@compileError(std.fmt.comptimePrint("`{s}` is not a valid placeholder type", .{
|
||||
@typeName(Formatable),
|
||||
}));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeFormatted(output: Writable, comptime format: []const u8, args: anytype) ReadWriteError!void {
|
||||
comptime {
|
||||
if (!std.unicode.utf8ValidateSlice(format)) {
|
||||
@ -199,6 +259,13 @@ pub fn writeFormatted(output: Writable, comptime format: []const u8, args: anyty
|
||||
}
|
||||
}
|
||||
|
||||
const Args = @TypeOf(args);
|
||||
|
||||
const args_struct = switch (@typeInfo(Args)) {
|
||||
.@"struct" => |@"struct"| @"struct",
|
||||
else => @compileError(std.fmt.comptimePrint("`args` must be a struct type, not {s}", .{@typeName(Args)})),
|
||||
};
|
||||
|
||||
comptime var tokens = formatting.TokenStream.init(format);
|
||||
|
||||
inline while (comptime tokens.next()) |token| {
|
||||
@ -216,8 +283,24 @@ pub fn writeFormatted(output: Writable, comptime format: []const u8, args: anyty
|
||||
},
|
||||
|
||||
.placeholder => |placeholder| {
|
||||
const Args = @TypeOf(args);
|
||||
if (args_struct.is_tuple) {
|
||||
const index = comptime coral.utf8.DecFormat.c.parse(usize, placeholder) orelse {
|
||||
@compileError(std.fmt.comptimePrint("{s} in format string `{s} is not a valid tuple index", .{
|
||||
placeholder,
|
||||
format,
|
||||
}));
|
||||
};
|
||||
|
||||
if (index >= args.len) {
|
||||
@compileError(std.fmt.comptimePrint("format string `{s}` uses index `{s}` not present in {s}", .{
|
||||
format,
|
||||
placeholder,
|
||||
@typeName(Args),
|
||||
}));
|
||||
}
|
||||
|
||||
try writeFormatable(output, args[index]);
|
||||
} else {
|
||||
if (!@hasField(Args, placeholder)) {
|
||||
@compileError(std.fmt.comptimePrint("format string `{s}` uses field `{s}` not present in {s}", .{
|
||||
format,
|
||||
@ -226,48 +309,7 @@ pub fn writeFormatted(output: Writable, comptime format: []const u8, args: anyty
|
||||
}));
|
||||
}
|
||||
|
||||
const field = @field(args, placeholder);
|
||||
const Field = @TypeOf(field);
|
||||
|
||||
switch (@typeInfo(Field)) {
|
||||
.pointer => |pointer| {
|
||||
const error_message = std.fmt.comptimePrint("{s} is not a string-like type", .{@typeName(Field)});
|
||||
|
||||
switch (pointer.size) {
|
||||
.one => switch (@typeInfo(pointer.child)) {
|
||||
.array => |array| switch (array.child == u8) {
|
||||
true => try coral.bytes.writeAll(output, field.*[0..]),
|
||||
false => @compileError(error_message),
|
||||
},
|
||||
|
||||
.@"struct", .@"union", .@"enum" => {
|
||||
try field.writeFormat(output);
|
||||
},
|
||||
|
||||
else => @compileError(error_message),
|
||||
},
|
||||
|
||||
.slice => switch (pointer.child == u8) {
|
||||
true => try coral.bytes.writeAll(output, field),
|
||||
false => @compileError(error_message),
|
||||
},
|
||||
|
||||
.many => switch ((pointer.sentinel() != null) and (pointer.child == u8)) {
|
||||
true => try coral.bytes.writeAll(output, std.mem.span(field)),
|
||||
false => @compileError(error_message),
|
||||
},
|
||||
|
||||
else => false,
|
||||
}
|
||||
},
|
||||
|
||||
.@"struct", .@"union", .@"enum" => {
|
||||
try field.writeFormat(output);
|
||||
},
|
||||
|
||||
else => {
|
||||
@compileError("Unsupported placeholder type");
|
||||
},
|
||||
try writeFormatable(output, @field(args, placeholder));
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -289,11 +331,15 @@ pub fn writeLittle(output: Writable, value: anytype) ReadWriteError!void {
|
||||
|
||||
std.mem.byteSwapAllFields(Value, ©);
|
||||
|
||||
try writeAll(output, std.mem.asBytes(&value));
|
||||
try writeAll(output, std.mem.asBytes(©));
|
||||
},
|
||||
|
||||
.int, .float, .bool => {
|
||||
try writeAll(@byteSwap(value));
|
||||
try writeAll(output, std.mem.asBytes(&@byteSwap(value)));
|
||||
},
|
||||
|
||||
.@"enum" => {
|
||||
try writeLittle(output, @intFromEnum(value));
|
||||
},
|
||||
|
||||
else => {
|
||||
@ -308,7 +354,7 @@ fn writeNull(buffer: []const u8) usize {
|
||||
return buffer.len;
|
||||
}
|
||||
|
||||
pub fn writeSentineled(output: Writable, data: []const u8, sentinel: u8) void {
|
||||
pub fn writeSentineled(output: Writable, data: []const u8, sentinel: u8) coral.bytes.ReadWriteError!void {
|
||||
try writeAll(output, data);
|
||||
try writeAll((&sentinel)[0..1]);
|
||||
try writeAll(output, (&sentinel)[0..1]);
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ pub const hashes = @import("./hashes.zig");
|
||||
|
||||
pub const heap = @import("./heap.zig");
|
||||
|
||||
pub const list = @import("./list.zig");
|
||||
|
||||
pub const map = @import("./map.zig");
|
||||
|
||||
pub const scalars = @import("./scalars.zig");
|
||||
@ -18,6 +20,8 @@ pub const shaders = @import("./shaders.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const tree = @import("./tree.zig");
|
||||
|
||||
pub const utf8 = @import("./utf8.zig");
|
||||
|
||||
pub fn Callable(comptime Output: type, comptime input_types: []const type) type {
|
||||
@ -98,6 +102,19 @@ pub fn Callable(comptime Output: type, comptime input_types: []const type) type
|
||||
};
|
||||
}
|
||||
|
||||
pub fn KeyValuePair(comptime Key: type, comptime Value: type) type {
|
||||
return struct {
|
||||
key: Key,
|
||||
value: Value,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn tuple(self: Self) struct { Key, Value } {
|
||||
return .{ self.key, self.value };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const ShortString = extern struct {
|
||||
buffer: [max]u8,
|
||||
remaining: u8,
|
||||
@ -242,15 +259,11 @@ pub fn Stack(comptime Value: type) type {
|
||||
}
|
||||
|
||||
pub fn get(self: Self) ?*Value {
|
||||
if (self.values.len == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return &self.values[self.values.len - 1];
|
||||
return if (self.isEmpty()) null else &self.values[self.values.len - 1];
|
||||
}
|
||||
|
||||
pub fn pop(self: *Self) ?Value {
|
||||
if (self.values.len == 0) {
|
||||
if (self.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
const std = @import("std");
|
||||
|
||||
///
|
||||
/// Non-cryptographic, hashing function suitable for scenarios where a quick and lightweight hash is needed.
|
||||
///
|
||||
/// Unlike Jenkins hashing, djb2 is not optimized for a specific sequence size.
|
||||
///
|
||||
pub fn djb2(comptime Int: type, bytes: []const u8) Int {
|
||||
var hash = @as(Int, @intCast(5381));
|
||||
|
||||
@ -10,6 +15,12 @@ pub fn djb2(comptime Int: type, bytes: []const u8) Int {
|
||||
return hash;
|
||||
}
|
||||
|
||||
///
|
||||
/// Non-cryptographic hashing function that is designed to be fast over secure.
|
||||
///
|
||||
/// It is particularly effective for small to medium-sized keys, providing low collision rates and provides good
|
||||
/// distribution properties.
|
||||
///
|
||||
pub fn jenkins(comptime Int: type, bytes: []const u8) Int {
|
||||
var hash = @as(Int, 0);
|
||||
|
||||
|
125
src/coral/list.zig
Normal file
125
src/coral/list.zig
Normal file
@ -0,0 +1,125 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
@ -10,7 +10,7 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
entry_map: []?Entry,
|
||||
len: usize,
|
||||
|
||||
pub const Entry = struct {
|
||||
const Entry = struct {
|
||||
key: Key,
|
||||
value: Value,
|
||||
|
||||
@ -33,7 +33,7 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
return &table.entry_map[hashed_key].?;
|
||||
});
|
||||
|
||||
if (traits.are_equal(table_entry.key, self.key)) {
|
||||
if (traits.areEqual(table_entry.key, self.key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -42,16 +42,18 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
}
|
||||
};
|
||||
|
||||
pub const Entries = struct {
|
||||
table: *const Self,
|
||||
pub const Keys = struct {
|
||||
context: *const Self,
|
||||
iterations: usize,
|
||||
|
||||
pub fn next(self: *Entries) ?*Entry {
|
||||
while (self.iterations < self.table.entry_map.len) {
|
||||
defer self.iterations += 1;
|
||||
pub fn next(self: *Keys) ?Key {
|
||||
while (self.iterations < self.context.entry_map.len) {
|
||||
defer {
|
||||
self.iterations += 1;
|
||||
}
|
||||
|
||||
if (self.table.entry_map[self.iterations]) |*entry| {
|
||||
return entry;
|
||||
if (self.context.entry_map[self.iterations]) |entry| {
|
||||
return entry.key;
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,78 +63,25 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const empty = Self{
|
||||
.entry_map = &.{},
|
||||
.len = 0,
|
||||
};
|
||||
pub const Values = struct {
|
||||
context: *const Self,
|
||||
iterations: usize,
|
||||
|
||||
pub fn entries(self: *const Self) Entries {
|
||||
return .{
|
||||
.table = self,
|
||||
.iterations = 0,
|
||||
};
|
||||
pub fn next(self: *Values) ?*Value {
|
||||
while (self.iterations < self.context.entry_map.len) {
|
||||
defer {
|
||||
self.iterations += 1;
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: Self) bool {
|
||||
return self.len == 0;
|
||||
}
|
||||
|
||||
pub fn remove(self: *Self, key: Key) ?Entry {
|
||||
const hash_max = @min(max_int, self.entry_map.len);
|
||||
var hashed_key = key.hash() % hash_max;
|
||||
|
||||
while (true) {
|
||||
const entry = &(self.entry_map[hashed_key] orelse continue);
|
||||
|
||||
if (self.keys_equal(entry.key, key)) {
|
||||
const original_entry = entry.*;
|
||||
|
||||
self.entry_map[hashed_key] = null;
|
||||
|
||||
return original_entry;
|
||||
}
|
||||
|
||||
hashed_key = (hashed_key +% 1) % hash_max;
|
||||
if (self.context.entry_map[self.iterations]) |*entry| {
|
||||
return &entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace(self: *Self, key: Key, value: Value) std.mem.Allocator.Error!?Entry {
|
||||
try self.rehash(load_max);
|
||||
|
||||
std.debug.assert(self.entry_map.len > self.len);
|
||||
|
||||
{
|
||||
const hash_max = @min(max_int, self.entry_map.len);
|
||||
var hashed_key = traits.hash(key) % hash_max;
|
||||
|
||||
while (true) {
|
||||
const entry = &(self.entry_map[hashed_key] orelse {
|
||||
self.entry_map[hashed_key] = .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
};
|
||||
|
||||
self.len += 1;
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
if (traits.are_equal(key, entry.key)) {
|
||||
const original_entry = entry.*;
|
||||
|
||||
entry.* = .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
}
|
||||
};
|
||||
|
||||
return original_entry;
|
||||
}
|
||||
|
||||
hashed_key = (hashed_key +% 1) % hash_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(self: *Self) void {
|
||||
for (self.entry_map) |*entry| {
|
||||
entry.* = null;
|
||||
@ -142,41 +91,19 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.entry_map.len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.entry_map.len != 0) {
|
||||
coral.heap.allocator.free(self.entry_map);
|
||||
}
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn get(self: Self, key: Key) ?*Value {
|
||||
if (self.len == 0) {
|
||||
return null;
|
||||
}
|
||||
pub const empty = Self{
|
||||
.entry_map = &.{},
|
||||
.len = 0,
|
||||
};
|
||||
|
||||
const hash_max = @min(max_int, self.entry_map.len);
|
||||
var hashed_key = traits.hash(key) % hash_max;
|
||||
var iterations = @as(usize, 0);
|
||||
|
||||
while (iterations < self.len) : (iterations += 1) {
|
||||
const entry = &(self.entry_map[hashed_key] orelse return null);
|
||||
|
||||
if (traits.are_equal(entry.key, key)) {
|
||||
return &entry.value;
|
||||
}
|
||||
|
||||
hashed_key = (hashed_key +% 1) % hash_max;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn emplace(self: *Self, key: Key, value: Value) std.mem.Allocator.Error!switch (Value) {
|
||||
void => bool,
|
||||
else => ?*Value,
|
||||
} {
|
||||
pub fn insert(self: *Self, key: Key, value: Value) std.mem.Allocator.Error!bool {
|
||||
try self.rehash(load_max);
|
||||
|
||||
std.debug.assert(self.entry_map.len > self.len);
|
||||
@ -186,16 +113,39 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
.value = value,
|
||||
};
|
||||
|
||||
if (entry.writeInto(self)) |written_entry| {
|
||||
return switch (Value) {
|
||||
void => true,
|
||||
else => &written_entry.value,
|
||||
};
|
||||
return entry.writeInto(self) != null;
|
||||
}
|
||||
|
||||
return switch (Value) {
|
||||
void => false,
|
||||
else => null,
|
||||
pub fn get(self: Self, key: Key) ?*Value {
|
||||
if (!self.isEmpty()) {
|
||||
const hash_max = @min(max_int, self.entry_map.len);
|
||||
var hashed_key = traits.hash(key) % hash_max;
|
||||
var iterations = @as(usize, 0);
|
||||
|
||||
while (iterations < self.len) : (iterations += 1) {
|
||||
const entry = &(self.entry_map[hashed_key] orelse {
|
||||
return null;
|
||||
});
|
||||
|
||||
if (traits.areEqual(entry.key, key)) {
|
||||
return &entry.value;
|
||||
}
|
||||
|
||||
hashed_key = (hashed_key +% 1) % hash_max;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: Self) bool {
|
||||
return self.len == 0;
|
||||
}
|
||||
|
||||
pub fn keys(self: *const Self) Keys {
|
||||
return .{
|
||||
.context = self,
|
||||
.iterations = 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -210,7 +160,9 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
|
||||
var table = empty;
|
||||
|
||||
errdefer table.deinit();
|
||||
errdefer {
|
||||
table.deinit();
|
||||
}
|
||||
|
||||
table.entry_map = allocate: {
|
||||
const min_len = @max(1, self.len);
|
||||
@ -238,59 +190,139 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
|
||||
self.* = table;
|
||||
}
|
||||
|
||||
pub fn remove(self: *Self, key: Key) ?coral.KeyValuePair(Key, Value) {
|
||||
const hash_max = @min(max_int, self.entry_map.len);
|
||||
var hashed_key = traits.hash(key) % hash_max;
|
||||
|
||||
while (true) {
|
||||
const entry = &(self.entry_map[hashed_key] orelse continue);
|
||||
|
||||
if (traits.areEqual(entry.key, key)) {
|
||||
const original_entry = entry.*;
|
||||
|
||||
self.entry_map[hashed_key] = null;
|
||||
|
||||
return .{
|
||||
.key = original_entry.key,
|
||||
.value = original_entry.value,
|
||||
};
|
||||
}
|
||||
|
||||
hashed_key = (hashed_key +% 1) % hash_max;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace(self: *Self, key: Key, value: Value) std.mem.Allocator.Error!?coral.KeyValuePair(Key, Value) {
|
||||
try self.rehash(load_max);
|
||||
|
||||
std.debug.assert(self.entry_map.len > self.len);
|
||||
|
||||
{
|
||||
const hash_max = @min(max_int, self.entry_map.len);
|
||||
var hashed_key = traits.hash(key) % hash_max;
|
||||
|
||||
while (true) {
|
||||
const entry = &(self.entry_map[hashed_key] orelse {
|
||||
self.entry_map[hashed_key] = .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
};
|
||||
|
||||
self.len += 1;
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
if (traits.areEqual(key, entry.key)) {
|
||||
const original_entry = entry.*;
|
||||
|
||||
entry.* = .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
};
|
||||
|
||||
return .{
|
||||
.key = original_entry.key,
|
||||
.value = original_entry.value,
|
||||
};
|
||||
}
|
||||
|
||||
hashed_key = (hashed_key +% 1) % hash_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn values(self: *const Self) Values {
|
||||
return .{
|
||||
.context = self,
|
||||
.iterations = 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Traits(comptime Key: type) type {
|
||||
return struct {
|
||||
are_equal: fn (Key, Key) bool,
|
||||
areEqual: fn (Key, Key) bool,
|
||||
hash: fn (Key) usize,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn enumTraits(comptime Enum: type) Traits(Enum) {
|
||||
const enums = struct {
|
||||
fn are_equal(a: Enum, b: Enum) bool {
|
||||
pub fn scalarTraits(comptime Scalar: type) Traits(Scalar) {
|
||||
const traits = switch (@typeInfo(Scalar)) {
|
||||
.@"enum" => |@"enum"| struct {
|
||||
fn areEqual(a: Scalar, b: Scalar) bool {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
fn hash(value: Enum) usize {
|
||||
return @intFromEnum(value) % std.math.maxInt(usize);
|
||||
fn hash(value: Scalar) usize {
|
||||
return switch (@sizeOf(@"enum".tag_type) > @sizeOf(usize)) {
|
||||
true => @intFromEnum(value) % std.math.maxInt(usize),
|
||||
false => @intFromEnum(value),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
.pointer => struct {
|
||||
fn areEqual(a: Scalar, b: Scalar) bool {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
fn hash(value: Scalar) usize {
|
||||
return @intFromPtr(value);
|
||||
}
|
||||
},
|
||||
|
||||
.int => |int| struct {
|
||||
fn areEqual(a: Scalar, b: Scalar) bool {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
fn hash(value: Scalar) Scalar {
|
||||
return switch (int.bits > @bitSizeOf(usize)) {
|
||||
true => value % std.math.maxInt(usize),
|
||||
false => value,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
else => {
|
||||
@compileError(std.fmt.comptimePrint("parameter `Scalar` must be a scalar type, not {s}", .{
|
||||
@typeName(Scalar),
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
return .{
|
||||
.are_equal = enums.are_equal,
|
||||
.hash = enums.hash,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn ptrTraits(comptime Ptr: type) Traits(Ptr) {
|
||||
const pointers = struct {
|
||||
fn are_equal(a: Ptr, b: Ptr) bool {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
fn hash(value: Ptr) usize {
|
||||
return @intFromPtr(value);
|
||||
}
|
||||
};
|
||||
|
||||
return switch (@typeInfo(Ptr)) {
|
||||
.pointer => .{
|
||||
.are_equal = pointers.are_equal,
|
||||
.hash = pointers.hash,
|
||||
},
|
||||
|
||||
else => @compileError(std.fmt.comptimePrint("parameter `Ptr` must be a pointer type, not {s}", .{
|
||||
@typeName(Ptr),
|
||||
})),
|
||||
.areEqual = traits.areEqual,
|
||||
.hash = traits.hash,
|
||||
};
|
||||
}
|
||||
|
||||
pub const string_traits = init: {
|
||||
const strings = struct {
|
||||
fn are_equal(a: []const u8, b: []const u8) bool {
|
||||
fn areEqual(a: []const u8, b: []const u8) bool {
|
||||
return std.mem.eql(u8, a, b);
|
||||
}
|
||||
|
||||
@ -300,24 +332,7 @@ pub const string_traits = init: {
|
||||
};
|
||||
|
||||
break :init Traits([]const u8){
|
||||
.are_equal = strings.are_equal,
|
||||
.areEqual = strings.areEqual,
|
||||
.hash = strings.hash,
|
||||
};
|
||||
};
|
||||
|
||||
pub const usize_traits = init: {
|
||||
const usizes = struct {
|
||||
fn are_equal(a: usize, b: usize) bool {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
fn hash(value: usize) usize {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
break :init Traits(usize){
|
||||
.are_equal = usizes.are_equal,
|
||||
.hash = usizes.hash,
|
||||
};
|
||||
};
|
||||
|
@ -8,8 +8,6 @@ pub const Type = @import("./shaders/Type.zig");
|
||||
|
||||
const coral = @import("./coral.zig");
|
||||
|
||||
const glsl = @import("./shaders/glsl.zig");
|
||||
|
||||
const spirv = @import("./shaders/spirv.zig");
|
||||
|
||||
const std = @import("std");
|
||||
@ -24,7 +22,33 @@ pub const Argument = struct {
|
||||
pub const Block = struct {
|
||||
scope: *const Scope,
|
||||
depth: usize,
|
||||
has_statement: ?*const Statement = null,
|
||||
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{
|
||||
@ -33,8 +57,7 @@ pub const DefinitionError = std.mem.Allocator.Error || error{
|
||||
};
|
||||
|
||||
pub const Expression = union(enum) {
|
||||
float: Float,
|
||||
int: Int,
|
||||
constant: [:0]const u8,
|
||||
group_expression: *const Expression,
|
||||
add: BinaryOperation,
|
||||
subtract: BinaryOperation,
|
||||
@ -65,17 +88,7 @@ pub const Expression = union(enum) {
|
||||
pub const BinaryOperation = struct {
|
||||
rhs_expression: *const Expression,
|
||||
lhs_expression: *const Expression,
|
||||
|
||||
pub fn inferType(self: BinaryOperation) TypeError!*const Type {
|
||||
const lhs_type = try self.lhs_expression.inferType();
|
||||
const rhs_type = try self.rhs_expression.inferType();
|
||||
|
||||
if (lhs_type != rhs_type) {
|
||||
return error.IncompatibleTypes;
|
||||
}
|
||||
|
||||
return lhs_type;
|
||||
}
|
||||
type: *const Type,
|
||||
};
|
||||
|
||||
pub const Builtin = struct {
|
||||
@ -118,17 +131,14 @@ pub const Expression = union(enum) {
|
||||
parameter_types: []const *const Type,
|
||||
};
|
||||
|
||||
pub const Float = struct {
|
||||
whole: [:0]const u8,
|
||||
decimal: [:0]const u8,
|
||||
};
|
||||
|
||||
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) {
|
||||
@ -136,11 +146,11 @@ pub const Expression = union(enum) {
|
||||
}
|
||||
|
||||
var has_next_argument = self.first_argument.has_next;
|
||||
var arguments_remaining = self.expected_parameter_count;
|
||||
var arguments_remaining = self.expected_parameter_count - 1;
|
||||
|
||||
while (has_next_argument) |next_argument| : ({
|
||||
has_next_argument = next_argument.has_next;
|
||||
arguments_remaining += 1;
|
||||
arguments_remaining -= 1;
|
||||
}) {
|
||||
if (arguments_remaining == 0) {
|
||||
return error.IncompatibleArguments;
|
||||
@ -171,20 +181,21 @@ pub const Expression = union(enum) {
|
||||
|
||||
pub const Invocation = struct {
|
||||
function: *const Function,
|
||||
has_argument: ?*const Argument = null,
|
||||
arguments: ?*const Argument = null,
|
||||
argument_count: usize = 0,
|
||||
|
||||
pub fn inferType(self: Invocation) TypeError!*const Type {
|
||||
var has_parameter = self.function.has_parameter;
|
||||
var has_argument = self.has_argument;
|
||||
var parameters = self.function.parameters;
|
||||
var arguments = self.arguments;
|
||||
|
||||
while (has_parameter) |parameter| {
|
||||
const argument = has_argument orelse {
|
||||
while (parameters) |parameter| {
|
||||
const argument = arguments orelse {
|
||||
return error.IncompatibleArguments;
|
||||
};
|
||||
|
||||
defer {
|
||||
has_parameter = parameter.has_next;
|
||||
has_argument = argument.has_next;
|
||||
parameters = parameter.has_next;
|
||||
arguments = argument.has_next;
|
||||
}
|
||||
|
||||
if (parameter.type != try argument.expression.inferType()) {
|
||||
@ -192,7 +203,7 @@ pub const Expression = union(enum) {
|
||||
}
|
||||
}
|
||||
|
||||
if (has_argument != null) {
|
||||
if (arguments != null) {
|
||||
return error.IncompatibleArguments;
|
||||
}
|
||||
|
||||
@ -200,10 +211,6 @@ pub const Expression = union(enum) {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Int = struct {
|
||||
literal: [:0]const u8,
|
||||
};
|
||||
|
||||
pub const OutputMutation = struct {
|
||||
output: *const Output,
|
||||
expression: *const Expression,
|
||||
@ -216,8 +223,11 @@ pub const Expression = union(enum) {
|
||||
|
||||
pub fn inferType(self: Expression) TypeError!*const Type {
|
||||
return switch (self) {
|
||||
.float => .float,
|
||||
.int => .int,
|
||||
.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,
|
||||
@ -245,28 +255,30 @@ pub const Expression = union(enum) {
|
||||
.greater_equal,
|
||||
.lesser_than,
|
||||
.lesser_equal,
|
||||
=> |binary_op| binary_op.inferType(),
|
||||
=> |binary_op| binary_op.type,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Function = struct {
|
||||
identifier: [:0]const u8,
|
||||
signature: [:0]const u8,
|
||||
has_return_type: ?*const Type = null,
|
||||
has_parameter: ?*const Parameter = null,
|
||||
parameters: ?*const Parameter = null,
|
||||
parameter_count: usize = 0,
|
||||
block: *const Block,
|
||||
};
|
||||
|
||||
pub const GenerationError = coral.bytes.ReadWriteError || error{
|
||||
UnsupportedFeature,
|
||||
};
|
||||
|
||||
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,
|
||||
@ -279,9 +291,10 @@ pub const Parameter = struct {
|
||||
has_next: ?*const Parameter = null,
|
||||
};
|
||||
|
||||
pub const ParsingError = std.mem.Allocator.Error || tokens.ExpectationError || DefinitionError || TypeError || error{
|
||||
pub const ParsingError = std.mem.Allocator.Error || tokens.ExpectationError || DefinitionError || TypeError || InternError || error{
|
||||
ImmutableStorage,
|
||||
UndefinedIdentifier,
|
||||
MissingReturn,
|
||||
};
|
||||
|
||||
pub const Texture = struct {
|
||||
@ -298,7 +311,7 @@ pub const Statement = union(enum) {
|
||||
declare_local: LocalDeclaration,
|
||||
mutate_local: LocalMutation,
|
||||
mutate_output: OutputMutation,
|
||||
return_expression: ?*const Expression,
|
||||
return_expression: *const Expression,
|
||||
|
||||
pub const LocalDeclaration = struct {
|
||||
local: *const Local,
|
||||
@ -337,6 +350,13 @@ pub const Uniform = struct {
|
||||
has_field: ?*const Field = null,
|
||||
};
|
||||
|
||||
pub const generateGlsl = glsl.generate;
|
||||
pub fn compileFragmentSpirv(arena: *std.heap.ArenaAllocator, function: *const coral.shaders.Function) spirv.BuildError!spirv.Module {
|
||||
var module = spirv.Module{
|
||||
.capabilities = &.{.shader},
|
||||
.memory_model = .{ .logical, .glsl450 },
|
||||
};
|
||||
|
||||
pub const generateSpirv = spirv.generate;
|
||||
_ = try module.shaderEntryPoint(arena, .fragment, function);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ 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, 255) = .{},
|
||||
functions: std.BoundedArray(*const coral.shaders.Function, max_functions) = .{},
|
||||
|
||||
pub const ParseResult = union(enum) {
|
||||
ok,
|
||||
@ -28,9 +28,7 @@ pub fn clear(self: *Self) void {
|
||||
}
|
||||
|
||||
pub fn defineFunction(self: *Self, arena: *std.heap.ArenaAllocator, function: coral.shaders.Function) coral.shaders.DefinitionError!void {
|
||||
const defined = try self.scope.define(arena, function);
|
||||
|
||||
self.functions.append(defined) catch |append_error| {
|
||||
self.functions.append(try self.scope.define(arena, function)) catch |append_error| {
|
||||
return switch (append_error) {
|
||||
error.Overflow => error.TooManySymbols,
|
||||
};
|
||||
@ -38,9 +36,7 @@ pub fn defineFunction(self: *Self, arena: *std.heap.ArenaAllocator, function: co
|
||||
}
|
||||
|
||||
pub fn defineInput(self: *Self, arena: *std.heap.ArenaAllocator, input: coral.shaders.Input) coral.shaders.DefinitionError!void {
|
||||
const defined = try self.scope.define(arena, input);
|
||||
|
||||
self.inputs.append(defined) catch |append_error| {
|
||||
self.inputs.append(try self.scope.define(arena, input)) catch |append_error| {
|
||||
return switch (append_error) {
|
||||
error.Overflow => error.TooManySymbols,
|
||||
};
|
||||
@ -48,35 +44,49 @@ pub fn defineInput(self: *Self, arena: *std.heap.ArenaAllocator, input: coral.sh
|
||||
}
|
||||
|
||||
pub fn defineOutput(self: *Self, arena: *std.heap.ArenaAllocator, output: coral.shaders.Output) coral.shaders.DefinitionError!void {
|
||||
const defined = try self.scope.define(arena, output);
|
||||
|
||||
self.outputs.append(defined) catch |append_error| {
|
||||
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, input: coral.shaders.Texture) coral.shaders.DefinitionError!void {
|
||||
const defined = try self.scope.define(arena, input);
|
||||
|
||||
self.textures.append(defined) catch |append_error| {
|
||||
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, input: coral.shaders.Uniform) coral.shaders.DefinitionError!void {
|
||||
const defined = try self.scope.define(arena, input);
|
||||
|
||||
self.uniforms.append(defined) catch |append_error| {
|
||||
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);
|
||||
|
||||
@ -85,80 +95,26 @@ pub fn init(arena: *std.heap.ArenaAllocator) std.mem.Allocator.Error!Self {
|
||||
return .{ .scope = scope };
|
||||
}
|
||||
|
||||
pub fn parse(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!void {
|
||||
errdefer {
|
||||
self.clear();
|
||||
}
|
||||
const max_functions = 255;
|
||||
|
||||
while (source.skip(.newline) != .end) {
|
||||
const symbol = try self.scope.parse(arena, source);
|
||||
|
||||
if (symbol.has(coral.shaders.Input)) |input| {
|
||||
self.inputs.append(input) catch |append_error| {
|
||||
return switch (append_error) {
|
||||
error.Overflow => error.TooManySymbols,
|
||||
};
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbol.has(coral.shaders.Output)) |output| {
|
||||
self.outputs.append(output) catch |append_error| {
|
||||
return switch (append_error) {
|
||||
error.Overflow => error.TooManySymbols,
|
||||
};
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbol.has(coral.shaders.Uniform)) |uniform| {
|
||||
self.uniforms.append(uniform) catch |append_error| {
|
||||
return switch (append_error) {
|
||||
error.Overflow => error.TooManySymbols,
|
||||
};
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbol.has(coral.shaders.Texture)) |texture| {
|
||||
self.textures.append(texture) catch |append_error| {
|
||||
return switch (append_error) {
|
||||
error.Overflow => error.TooManySymbols,
|
||||
};
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbol.has(coral.shaders.Function)) |function| {
|
||||
self.functions.append(function) catch |append_error| {
|
||||
return switch (append_error) {
|
||||
error.Overflow => error.TooManySymbols,
|
||||
};
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parseText(self: *Self, arena: *std.heap.ArenaAllocator, source_text: []const u8) std.mem.Allocator.Error!ParseResult {
|
||||
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.parse(arena, &source) catch |parse_error| {
|
||||
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,
|
||||
}),
|
||||
@ -190,9 +146,102 @@ pub fn parseText(self: *Self, arena: *std.heap.ArenaAllocator, source_text: []co
|
||||
.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);
|
||||
}
|
||||
|
@ -4,15 +4,20 @@ const std = @import("std");
|
||||
|
||||
const tokens = @import("./tokens.zig");
|
||||
|
||||
identifiers: [max][:0]const u8 = undefined,
|
||||
symbols: [max]coral.Box = undefined,
|
||||
len: usize = 0,
|
||||
has_enclosing: ?*const Self = null,
|
||||
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.len = 0;
|
||||
self.defined = 0;
|
||||
|
||||
if (self.has_enclosing) |enclosing| {
|
||||
enclosing.clear();
|
||||
}
|
||||
}
|
||||
|
||||
fn create(arena: *std.heap.ArenaAllocator, node: anytype) std.mem.Allocator.Error!*@TypeOf(node) {
|
||||
@ -23,7 +28,7 @@ fn create(arena: *std.heap.ArenaAllocator, node: anytype) std.mem.Allocator.Erro
|
||||
return allocation;
|
||||
}
|
||||
|
||||
pub fn createScope(self: *const Self, arena: *std.heap.ArenaAllocator) std.mem.Allocator.Error!*Self {
|
||||
pub fn createScope(self: *Self, arena: *std.heap.ArenaAllocator) std.mem.Allocator.Error!*Self {
|
||||
return create(arena, Self{
|
||||
.has_enclosing = self,
|
||||
});
|
||||
@ -51,32 +56,32 @@ pub fn define(self: *Self, arena: *std.heap.ArenaAllocator, symbol: anytype) cor
|
||||
})),
|
||||
};
|
||||
|
||||
if (self.exists(identifier)) {
|
||||
if (self.definitionExists(identifier)) {
|
||||
return error.DuplicateIdentifier;
|
||||
}
|
||||
|
||||
if (self.len >= max) {
|
||||
if (self.defined >= max_definitions) {
|
||||
return error.TooManySymbols;
|
||||
}
|
||||
|
||||
const stored_symbol = &self.symbols[self.len];
|
||||
const stored_symbol = &self.symbols[self.defined];
|
||||
|
||||
self.identifiers[self.len] = identifier;
|
||||
self.identifiers[self.defined] = identifier;
|
||||
stored_symbol.* = try .initWithAllocator(arena.allocator(), symbol);
|
||||
self.len += 1;
|
||||
self.defined += 1;
|
||||
|
||||
return stored_symbol.has(Symbol).?;
|
||||
}
|
||||
|
||||
pub fn exists(self: Self, identifier: []const u8) bool {
|
||||
for (self.identifiers) |existing_identifier| {
|
||||
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.exists(identifier);
|
||||
return enclosing.definitionExists(identifier);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -95,7 +100,7 @@ pub fn hasGlobal(self: *const Self, comptime Symbol: type, identifier: []const u
|
||||
}
|
||||
|
||||
pub fn hasLocal(self: *const Self, comptime Symbol: type, identifier: []const u8) ?*const Symbol {
|
||||
for (0..self.len) |i| {
|
||||
for (0..self.defined) |i| {
|
||||
if (std.mem.eql(u8, self.identifiers[i], identifier)) {
|
||||
if (self.symbols[i].has(Symbol)) |symbol| {
|
||||
return symbol;
|
||||
@ -106,48 +111,22 @@ pub fn hasLocal(self: *const Self, comptime Symbol: type, identifier: []const u8
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const max = 256;
|
||||
pub fn intern(self: *Self, arena: *std.heap.ArenaAllocator, value: [:0]const u8) std.mem.Allocator.Error![:0]const u8 {
|
||||
var scope: ?*Self = self;
|
||||
|
||||
pub fn parse(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!coral.Box {
|
||||
const index = self.len;
|
||||
|
||||
switch (try source.current.expectAny(&.{.keyword_function})) {
|
||||
.keyword_function => {
|
||||
const function = try self.define(arena, coral.shaders.Function{
|
||||
.identifier = try arena.allocator().dupeZ(u8, try source.skip(.newline).expectIdentifier()),
|
||||
|
||||
.block = &.{
|
||||
.scope = &.{},
|
||||
.depth = 0,
|
||||
},
|
||||
});
|
||||
|
||||
try source.skip(.newline).expect(.symbol_paren_left);
|
||||
|
||||
const inner_scope = try self.createScope(arena);
|
||||
|
||||
function.has_parameter = switch (try source.skip(.newline).expectAny(&.{ .symbol_paren_right, .identifier })) {
|
||||
.identifier => try inner_scope.parseParameter(arena, source),
|
||||
.symbol_paren_right => null,
|
||||
};
|
||||
|
||||
try source.skip(.newline).expect(.symbol_arrow);
|
||||
|
||||
var peeking = source.*;
|
||||
|
||||
if (peeking.skip(.newline) != .symbol_brace_left) {
|
||||
function.has_return_type = try self.parseType(source);
|
||||
while (scope) |current| : (scope = current.has_enclosing) {
|
||||
if (current.interned.getKey(value)) |existing| {
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
|
||||
function.block = try inner_scope.parseBlock(arena, source);
|
||||
},
|
||||
}
|
||||
std.debug.assert(try self.interned.insert(arena.allocator(), value, {}) != null);
|
||||
|
||||
std.debug.assert((index + 1) == self.len);
|
||||
|
||||
return self.symbols[index];
|
||||
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);
|
||||
|
||||
@ -173,14 +152,14 @@ fn parseArgument(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.S
|
||||
};
|
||||
}
|
||||
|
||||
fn parseBlock(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Block {
|
||||
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,
|
||||
|
||||
.has_statement = switch (source.skip(.newline)) {
|
||||
.statements = switch (source.skip(.newline)) {
|
||||
.symbol_brace_right => null,
|
||||
else => try self.parseBlockStatement(arena, source),
|
||||
},
|
||||
@ -253,7 +232,7 @@ fn parseBlockStatement(self: *Self, arena: *std.heap.ArenaAllocator, source: *to
|
||||
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 = &.{ .int = .{ .literal = "0" } },
|
||||
.expression = &.{ .constant = try self.intern(arena, "0") },
|
||||
.type = .int,
|
||||
});
|
||||
|
||||
@ -300,84 +279,140 @@ fn parseExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens
|
||||
}
|
||||
|
||||
fn parseAdditiveExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
||||
const expression = try self.parseEqualityExpression(arena, source);
|
||||
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 = try self.parseEqualityExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.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 = try self.parseEqualityExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.rhs_expression = rhs_expression,
|
||||
.lhs_expression = lhs_expression,
|
||||
.type = lhs_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return expression;
|
||||
return lhs_expression;
|
||||
}
|
||||
|
||||
fn parseComparativeExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
||||
const expression = try self.parseTermExpression(arena, source);
|
||||
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 = try self.parseTermExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.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 = try self.parseTermExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.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 = try self.parseTermExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.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 = try self.parseTermExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.rhs_expression = rhs_expression,
|
||||
.lhs_expression = lhs_expression,
|
||||
.type = lhs_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return expression;
|
||||
return lhs_expression;
|
||||
}
|
||||
|
||||
fn parseEqualityExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
||||
const expression = try self.parseComparativeExpression(arena, source);
|
||||
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 = try self.parseComparativeExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.rhs_expression = rhs_expression,
|
||||
.lhs_expression = lhs_expression,
|
||||
.type = lhs_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return expression;
|
||||
return lhs_expression;
|
||||
}
|
||||
|
||||
fn parseFactorExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
||||
@ -421,22 +456,8 @@ fn parseOperandExpression(self: *Self, arena: *std.heap.ArenaAllocator, source:
|
||||
},
|
||||
|
||||
.scalar => {
|
||||
const arena_allocator = arena.allocator();
|
||||
const scalar = try source.current.expectScalar();
|
||||
|
||||
if (std.mem.indexOfScalar(u8, scalar, '.')) |index| {
|
||||
return create(arena, coral.shaders.Expression{
|
||||
.float = .{
|
||||
.whole = try arena_allocator.dupeZ(u8, scalar[0..index]),
|
||||
.decimal = try arena_allocator.dupeZ(u8, scalar[index + 1 ..]),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return create(arena, coral.shaders.Expression{
|
||||
.int = .{
|
||||
.literal = try arena_allocator.dupeZ(u8, scalar),
|
||||
},
|
||||
return try create(arena, coral.shaders.Expression{
|
||||
.constant = try arena.allocator().dupeZ(u8, try source.current.expectScalar()),
|
||||
});
|
||||
},
|
||||
|
||||
@ -458,10 +479,22 @@ fn parseOperandExpression(self: *Self, arena: *std.heap.ArenaAllocator, source:
|
||||
},
|
||||
|
||||
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,
|
||||
.has_argument = try self.parseArgument(arena, source),
|
||||
.argument_count = argument_count,
|
||||
.arguments = arguments,
|
||||
},
|
||||
});
|
||||
},
|
||||
@ -643,30 +676,46 @@ fn parseOperandExpression(self: *Self, arena: *std.heap.ArenaAllocator, source:
|
||||
}
|
||||
|
||||
fn parseTermExpression(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Expression {
|
||||
const expression = try self.parseFactorExpression(arena, source);
|
||||
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 = try self.parseFactorExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.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 = try self.parseFactorExpression(arena, source),
|
||||
.lhs_expression = expression,
|
||||
.rhs_expression = rhs_expression,
|
||||
.lhs_expression = lhs_expression,
|
||||
.type = lhs_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return expression;
|
||||
return lhs_expression;
|
||||
}
|
||||
|
||||
fn parseParameter(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.Stream) coral.shaders.ParsingError!?*const coral.shaders.Parameter {
|
||||
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,
|
||||
@ -684,7 +733,7 @@ fn parseParameter(self: *Self, arena: *std.heap.ArenaAllocator, source: *tokens.
|
||||
return parameter;
|
||||
}
|
||||
|
||||
fn parseType(self: *const Self, source: *tokens.Stream) coral.shaders.ParsingError!*const coral.shaders.Type {
|
||||
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,
|
||||
|
@ -1,65 +1,117 @@
|
||||
const coral = @import("../coral.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
identifier: [:0]const u8,
|
||||
layout: Layout,
|
||||
|
||||
pub const Layout = union(enum) {
|
||||
handle,
|
||||
scalar,
|
||||
vector: VectorWidth,
|
||||
matrix: VectorWidth,
|
||||
record_fields: ?*const coral.shaders.Field,
|
||||
float: Scalar,
|
||||
signed: Scalar,
|
||||
unsigned: Scalar,
|
||||
vector: Vector,
|
||||
matrix: Matrix,
|
||||
texture: Texture,
|
||||
record: Record,
|
||||
};
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const VectorWidth = enum(u2) {
|
||||
@"1",
|
||||
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 = .scalar,
|
||||
.layout = .{ .float = .{ .bits = 32 } },
|
||||
};
|
||||
|
||||
pub const float2 = &Self{
|
||||
.identifier = "float2",
|
||||
.layout = .{ .vector = .@"2" },
|
||||
|
||||
.layout = .{
|
||||
.vector = .{
|
||||
.element = .float,
|
||||
.dimensions = .@"2",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub const float3 = &Self{
|
||||
.identifier = "float3",
|
||||
.layout = .{ .vector = .@"3" },
|
||||
|
||||
.layout = .{
|
||||
.vector = .{
|
||||
.element = .float,
|
||||
.dimensions = .@"3",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub const float4 = &Self{
|
||||
.identifier = "float4",
|
||||
.layout = .{ .vector = .@"4" },
|
||||
|
||||
.layout = .{
|
||||
.vector = .{
|
||||
.element = .float,
|
||||
.dimensions = .@"4",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub const float4x4 = &Self{
|
||||
.identifier = "float4x4",
|
||||
.layout = .{ .matrix = .@"4" },
|
||||
|
||||
.layout = .{
|
||||
.matrix = .{
|
||||
.element = .float,
|
||||
.dimensions = .@"2",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub fn hasField(self: Self, field_identifier: []const u8) ?*const coral.shaders.Field {
|
||||
return switch (self.layout) {
|
||||
.scalar, .matrix, .handle => null,
|
||||
.record_fields => |has_field| if (has_field) |field| field.has(field_identifier) else null,
|
||||
|
||||
.vector => |vector_width| switch (vector_width) {
|
||||
.@"1" => switch (field_identifier.len) {
|
||||
1 => switch (field_identifier[0]) {
|
||||
'x', 'r' => .vector_x,
|
||||
else => null,
|
||||
},
|
||||
|
||||
else => null,
|
||||
},
|
||||
.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,
|
||||
@ -915,10 +967,18 @@ pub fn hasField(self: Self, field_identifier: []const u8) ?*const coral.shaders.
|
||||
|
||||
pub const int = &Self{
|
||||
.identifier = "int",
|
||||
.layout = .scalar,
|
||||
.layout = .{ .signed = .{ .bits = 32 } },
|
||||
};
|
||||
|
||||
pub const texture2 = &Self{
|
||||
.identifier = "texture2",
|
||||
.layout = .handle,
|
||||
|
||||
.layout = .{
|
||||
.texture = .{
|
||||
.dimensions = .@"2",
|
||||
.is_arary = false,
|
||||
.is_depth = false,
|
||||
.is_multi_sampled = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -2,73 +2,523 @@ 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 Document = struct {
|
||||
ids_assigned: u32 = 0,
|
||||
ops: coral.Stack(u32) = .empty,
|
||||
pub const Dim = enum(u32) {
|
||||
@"1d" = 0,
|
||||
@"2d" = 1,
|
||||
@"3d" = 2,
|
||||
cube = 4,
|
||||
};
|
||||
|
||||
fn nextId(self: *Document) u32 {
|
||||
self.ids_assigned += 1;
|
||||
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");
|
||||
};
|
||||
},
|
||||
|
||||
.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 op(self: *Document, code: u32) std.mem.Allocator.Error!void {
|
||||
try self.ops.pushGrow(std.mem.nativeToLittle(u32, code));
|
||||
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;
|
||||
}
|
||||
|
||||
pub fn opCapability(self: *Document, capability: Capability) std.mem.Allocator.Error!void {
|
||||
try self.op(17);
|
||||
try self.op(@intFromEnum(capability));
|
||||
}
|
||||
const arena_allocator = arena.allocator();
|
||||
const data = try arena_allocator.alloc(u32, 1);
|
||||
|
||||
pub fn opEntryPoint(self: *Document, name: []const u8, interfaces: []const u32) std.mem.Allocator.Error!void {
|
||||
try self.op(14);
|
||||
try self.string(name);
|
||||
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");
|
||||
}));
|
||||
|
||||
for (interfaces) |interface| {
|
||||
self.op(interface);
|
||||
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 opMemoryModel(self: *Document, addressing: AddressingModel, memory: MemoryModel) std.mem.Allocator.Error!void {
|
||||
try self.op(14);
|
||||
try self.op(@intFromEnum(addressing));
|
||||
try self.op(@intFromEnum(memory));
|
||||
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 opExtInstImport(self: *Document, extension: []const u8) std.mem.Allocator.Error!u32 {
|
||||
const id = self.nextId();
|
||||
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();
|
||||
|
||||
try self.op(11);
|
||||
try self.op(id);
|
||||
try self.string(extension);
|
||||
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() },
|
||||
})).?;
|
||||
|
||||
return id;
|
||||
{
|
||||
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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string(self: *Document, data: []const u8) std.mem.Allocator.Error!void {
|
||||
const data_len_with_sentinel = data.len + 1;
|
||||
const word_count = (data_len_with_sentinel + 4) / 4;
|
||||
{
|
||||
var statements = shader_function.block.statements;
|
||||
|
||||
try self.ops.grow(word_count);
|
||||
while (statements) |statement| {
|
||||
switch (statement.*) {
|
||||
.declare_local => |local_declaration| {
|
||||
_ = try function.block.shaderVariable(self, function, arena, local_declaration.local);
|
||||
statements = local_declaration.has_next;
|
||||
},
|
||||
|
||||
std.debug.assert(self.ops.pushMany(word_count, 0));
|
||||
.mutate_local => {},
|
||||
.mutate_output => {},
|
||||
|
||||
const buffer = std.mem.sliceAsBytes(self.ops.values[(self.ops.values.len - word_count)..]);
|
||||
|
||||
@memcpy(buffer[0..data.len], data);
|
||||
.return_expression => |_| {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeTo(self: Document, spirv: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||
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)
|
||||
@ -77,6 +527,12 @@ pub const Document = struct {
|
||||
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,
|
||||
@ -84,24 +540,81 @@ pub const Document = struct {
|
||||
.id_upper_bound = self.ids_assigned,
|
||||
});
|
||||
|
||||
return coral.bytes.writeAll(spirv, std.mem.sliceAsBytes(self.ops.values));
|
||||
{
|
||||
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 MemoryModel = enum(u32) {
|
||||
glsl450 = 1,
|
||||
pub const Value = struct {
|
||||
id: u32,
|
||||
type: *const Type,
|
||||
};
|
||||
|
||||
pub fn generate(spirv: coral.bytes.Writable, root: coral.shaders.Root) (coral.shaders.GenerationError || std.mem.Allocator.Error)!void {
|
||||
// TODO: Finish and testing coverage.
|
||||
var arena = std.heap.ArenaAllocator.init(coral.heap.allocator);
|
||||
pub const SampledImageType = struct {
|
||||
id: u32,
|
||||
image_type: *const Type,
|
||||
};
|
||||
|
||||
var module = Module{
|
||||
.capabilities = &.{.shader},
|
||||
.memory_model = .{ .logical, .glsl450 },
|
||||
};
|
||||
pub const StructType = struct {
|
||||
id: u32,
|
||||
member_types: []const *const Type,
|
||||
};
|
||||
|
||||
const glsl_std_450 = module.importExtension(&arena, "GLSL.std.450");
|
||||
// TODO: specify frag entry point
|
||||
const frag = module.entryPoint(.fragment, "main", .{});
|
||||
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);
|
||||
}
|
||||
|
337
src/coral/tree.zig
Normal file
337
src/coral/tree.zig
Normal file
@ -0,0 +1,337 @@
|
||||
const coral = @import("./coral.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(Key)) type {
|
||||
return struct {
|
||||
free_nodes: ?*Node = null,
|
||||
active_nodes: ?*Node = null,
|
||||
|
||||
const Node = struct {
|
||||
key: Key,
|
||||
value: Value,
|
||||
has_left: ?*Node = null,
|
||||
has_right: ?*Node = null,
|
||||
has_parent: ?*Node = null,
|
||||
|
||||
fn deinit(self: *Node, allocator: std.mem.Allocator) void {
|
||||
self.has_parent = undefined;
|
||||
|
||||
if (self.has_left) |left| {
|
||||
left.deinit(allocator);
|
||||
allocator.destroy(left);
|
||||
}
|
||||
|
||||
self.has_left = undefined;
|
||||
|
||||
if (self.has_right) |right| {
|
||||
right.deinit(allocator);
|
||||
allocator.destroy(right);
|
||||
}
|
||||
|
||||
self.has_right = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
pub const KeyValues = struct {
|
||||
nodes: ?*Node,
|
||||
|
||||
pub fn next(self: *KeyValues) ?coral.KeyValuePair(Key, *Value) {
|
||||
var nodes = self.nodes;
|
||||
|
||||
while (nodes) |node| {
|
||||
const left = node.has_left orelse {
|
||||
self.nodes = node.has_right;
|
||||
|
||||
return .{
|
||||
.key = node.key,
|
||||
.value = &node.value,
|
||||
};
|
||||
};
|
||||
|
||||
// Find the rightmost node in left subtree or link back to current
|
||||
var pred = left;
|
||||
while (pred.has_right != null and pred.has_right != node) {
|
||||
pred = pred.has_right.?;
|
||||
}
|
||||
|
||||
if (pred.has_right != null) {
|
||||
pred.has_right = null;
|
||||
self.nodes = node.has_right;
|
||||
|
||||
return .{
|
||||
.key = node.key,
|
||||
.value = &node.value,
|
||||
};
|
||||
}
|
||||
|
||||
pred.has_right = node;
|
||||
self.nodes = node.has_left;
|
||||
nodes = self.nodes;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn nextKey(self: *KeyValues) ?Key {
|
||||
return if (self.next()) |key_value| key_value.key else null;
|
||||
}
|
||||
|
||||
pub fn nextValue(self: *KeyValues) ?*Value {
|
||||
return if (self.next()) |key_value| key_value.value else null;
|
||||
}
|
||||
};
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn clear(self: *Self) void {
|
||||
var free_nodes: ?*Node = null;
|
||||
|
||||
if (self.active_nodes) |root| {
|
||||
// Push root onto stack
|
||||
root.has_parent = free_nodes;
|
||||
free_nodes = root;
|
||||
self.active_nodes = null;
|
||||
}
|
||||
|
||||
while (free_nodes) |node| {
|
||||
// Pop node from stack
|
||||
free_nodes = node.has_parent;
|
||||
|
||||
// Push children onto stack
|
||||
if (node.has_left) |left| {
|
||||
left.has_parent = free_nodes;
|
||||
free_nodes = left;
|
||||
}
|
||||
|
||||
if (node.has_right) |right| {
|
||||
right.has_parent = free_nodes;
|
||||
free_nodes = right;
|
||||
}
|
||||
|
||||
// Add node to free list
|
||||
node.has_left = null;
|
||||
node.has_right = null;
|
||||
node.has_parent = null;
|
||||
node.key = undefined;
|
||||
node.value = undefined;
|
||||
node.has_right = self.free_nodes;
|
||||
self.free_nodes = node;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn createNode(self: *Self, allocator: std.mem.Allocator, node: Node) std.mem.Allocator.Error!*Node {
|
||||
if (self.free_nodes) |free_node| {
|
||||
self.free_nodes = free_node.has_parent;
|
||||
|
||||
free_node.* = node;
|
||||
|
||||
return free_node;
|
||||
}
|
||||
|
||||
const created_node = try allocator.create(Node);
|
||||
|
||||
created_node.* = node;
|
||||
|
||||
return created_node;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
if (self.active_nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
allocator.destroy(node);
|
||||
}
|
||||
|
||||
self.active_nodes = undefined;
|
||||
}
|
||||
|
||||
pub fn insert(self: *Self, allocator: std.mem.Allocator, key: Key, value: Value) std.mem.Allocator.Error!?*Value {
|
||||
var node = self.active_nodes orelse {
|
||||
self.active_nodes = try self.createNode(allocator, .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
});
|
||||
|
||||
return &self.active_nodes.?.value;
|
||||
};
|
||||
|
||||
while (true) {
|
||||
switch (traits.compare(key, node.key)) {
|
||||
.equal => {
|
||||
return null;
|
||||
},
|
||||
|
||||
.lesser => {
|
||||
node = node.has_left orelse {
|
||||
node.has_left = try self.createNode(allocator, .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
.has_parent = node,
|
||||
});
|
||||
|
||||
return &node.has_left.?.value;
|
||||
};
|
||||
},
|
||||
|
||||
.greater => {
|
||||
node = node.has_right orelse {
|
||||
node.has_right = try self.createNode(allocator, .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
.has_parent = node,
|
||||
});
|
||||
|
||||
return &node.has_right.?.value;
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const empty = Self{
|
||||
.active_nodes = null,
|
||||
};
|
||||
|
||||
pub fn get(self: Self, key: Key) ?*Value {
|
||||
var nodes = self.active_nodes;
|
||||
|
||||
while (nodes) |node| {
|
||||
nodes = switch (traits.compare(key, node.key)) {
|
||||
.lesser => node.has_left,
|
||||
.greater => node.has_right,
|
||||
|
||||
.equal => {
|
||||
return &node.value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getKey(self: Self, key: Key) ?Key {
|
||||
var nodes = self.active_nodes;
|
||||
|
||||
while (nodes) |node| {
|
||||
nodes = switch (traits.compare(key, node.key)) {
|
||||
.lesser => node.has_left,
|
||||
.greater => node.has_right,
|
||||
|
||||
.equal => {
|
||||
return node.key;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
return self.active_nodes == null;
|
||||
}
|
||||
|
||||
pub fn keyValues(self: *const Self) KeyValues {
|
||||
return .{ .nodes = self.active_nodes };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Comparison = enum(i2) {
|
||||
lesser = -1,
|
||||
equal = 0,
|
||||
greater = 1,
|
||||
};
|
||||
|
||||
pub fn Traits(comptime Key: type) type {
|
||||
return struct {
|
||||
compare: fn (Key, Key) Comparison,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scalarTraits(comptime Scalar: type) Traits(Scalar) {
|
||||
const traits = switch (@typeInfo(Scalar)) {
|
||||
.@"enum" => struct {
|
||||
fn compare(a: Scalar, b: Scalar) Comparison {
|
||||
const a_int = @intFromEnum(a);
|
||||
const b_int = @intFromEnum(b);
|
||||
|
||||
if (a_int < b_int) {
|
||||
return .lesser;
|
||||
}
|
||||
|
||||
if (a_int > b_int) {
|
||||
return .greater;
|
||||
}
|
||||
|
||||
return .equal;
|
||||
}
|
||||
},
|
||||
|
||||
.pointer => struct {
|
||||
fn compare(a: Scalar, b: Scalar) Comparison {
|
||||
const a_int = @intFromPtr(a);
|
||||
const b_int = @intFromPtr(b);
|
||||
|
||||
if (a_int < b_int) {
|
||||
return .lesser;
|
||||
}
|
||||
|
||||
if (a_int > b_int) {
|
||||
return .greater;
|
||||
}
|
||||
|
||||
return .equal;
|
||||
}
|
||||
},
|
||||
|
||||
.int => struct {
|
||||
fn compare(a: Scalar, b: Scalar) Comparison {
|
||||
if (a < b) {
|
||||
return .lesser;
|
||||
}
|
||||
|
||||
if (a > b) {
|
||||
return .greater;
|
||||
}
|
||||
|
||||
return .equal;
|
||||
}
|
||||
},
|
||||
|
||||
else => {
|
||||
@compileError(std.fmt.comptimePrint("parameter `Scalar` must be a scalar type, not {s}", .{
|
||||
@typeName(Scalar),
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
return .{
|
||||
.compare = traits.compare,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sliceTraits(comptime Slice: type) Traits(Slice) {
|
||||
const slice_pointer = switch (@typeInfo(Slice)) {
|
||||
.pointer => |pointer| pointer,
|
||||
|
||||
else => {
|
||||
@compileError(std.fmt.comptimePrint("parameter `Slice` must be a slice type, not {s}", .{
|
||||
@typeName(Slice),
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
const traits = struct {
|
||||
fn compare(a: Slice, b: Slice) Comparison {
|
||||
return switch (std.mem.order(slice_pointer.child, a, b)) {
|
||||
.lt => .lesser,
|
||||
.gt => .greater,
|
||||
.eq => .equal,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return .{
|
||||
.compare = traits.compare,
|
||||
};
|
||||
}
|
@ -44,6 +44,8 @@ pub fn Dec(comptime Value: type) type {
|
||||
}
|
||||
|
||||
pub fn writeFormat(self: Self, writer: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||
switch (@typeInfo(Value)) {
|
||||
.int => |int| {
|
||||
if (self.value == 0) {
|
||||
return coral.bytes.writeAll(writer, switch (self.format.positive_prefix) {
|
||||
.none => "0",
|
||||
@ -52,8 +54,6 @@ pub fn Dec(comptime Value: type) type {
|
||||
});
|
||||
}
|
||||
|
||||
switch (@typeInfo(Value)) {
|
||||
.int => |int| {
|
||||
const radix = 10;
|
||||
var buffer = [_]u8{0} ** (1 + @max(int.bits, 1));
|
||||
var buffer_start = buffer.len - 1;
|
||||
@ -81,6 +81,14 @@ pub fn Dec(comptime Value: type) type {
|
||||
},
|
||||
|
||||
.float => |float| {
|
||||
if (self.value == 0) {
|
||||
return coral.bytes.writeAll(writer, switch (self.format.positive_prefix) {
|
||||
.none => "0",
|
||||
.plus => "+0",
|
||||
.space => " 0",
|
||||
});
|
||||
}
|
||||
|
||||
if (self.value < 0) {
|
||||
try coral.bytes.writeAll(writer, "-");
|
||||
}
|
||||
@ -102,7 +110,18 @@ pub fn Dec(comptime Value: type) type {
|
||||
try repacked.writeFormat(writer);
|
||||
},
|
||||
|
||||
else => @compileError("`" ++ @typeName(Value) ++ "` cannot be formatted to a decimal string"),
|
||||
.@"enum" => |@"enum"| {
|
||||
const repacked = Dec(@"enum".tag_type){
|
||||
.value = @intFromEnum(self.value),
|
||||
.format = self.format,
|
||||
};
|
||||
|
||||
try repacked.writeFormat(writer);
|
||||
},
|
||||
|
||||
else => {
|
||||
@compileError("`" ++ @typeName(Value) ++ "` is not a valid decimal value");
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -112,13 +131,18 @@ pub const DecFormat = struct {
|
||||
delimiter: []const u8,
|
||||
positive_prefix: enum { none, plus, space },
|
||||
|
||||
pub fn parse(self: DecFormat, utf8: []const u8, comptime Value: type) ?Value {
|
||||
pub const c = &DecFormat{
|
||||
.delimiter = "",
|
||||
.positive_prefix = .none,
|
||||
};
|
||||
|
||||
pub fn parse(self: DecFormat, comptime Value: type, utf8: []const u8) ?Value {
|
||||
if (utf8.len == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (@typeInfo(Value)) {
|
||||
.Int => |int| {
|
||||
.int => |int| {
|
||||
const has_sign = switch (utf8[0]) {
|
||||
'-', '+', ' ' => true,
|
||||
else => false,
|
||||
@ -168,7 +192,7 @@ pub const DecFormat = struct {
|
||||
}
|
||||
},
|
||||
|
||||
.Float => {
|
||||
.float => {
|
||||
const has_sign = switch (utf8[0]) {
|
||||
'-', '+', ' ' => true,
|
||||
else => false,
|
||||
@ -221,21 +245,12 @@ pub const DecFormat = struct {
|
||||
};
|
||||
|
||||
test "decimal parsing" {
|
||||
const format = DecFormat{
|
||||
.delimiter = "",
|
||||
.positive_prefix = .none,
|
||||
};
|
||||
|
||||
try std.testing.expectEqual(format.parse("69", i64), 69);
|
||||
try std.testing.expectEqual(DecFormat.c.parse("69", i64), 69);
|
||||
}
|
||||
|
||||
pub fn cDec(value: anytype) Dec(@TypeOf(value)) {
|
||||
return .{
|
||||
.value = value,
|
||||
|
||||
.format = &.{
|
||||
.delimiter = "",
|
||||
.positive_prefix = .none,
|
||||
},
|
||||
.format = .c,
|
||||
};
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ const ona = @import("./ona.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
initialized_states: coral.map.Hashed(*const coral.TypeId, coral.Box, coral.map.ptrTraits(*const coral.TypeId)),
|
||||
named_systems: coral.map.Hashed([]const u8, SystemGraph, coral.map.string_traits),
|
||||
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)),
|
||||
is_running: bool,
|
||||
|
||||
pub const RunError = std.mem.Allocator.Error || error{
|
||||
@ -26,26 +26,33 @@ pub const Time = struct {
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
{
|
||||
var entries = self.named_systems.entries();
|
||||
var key_values = self.named_systems.keyValues();
|
||||
|
||||
while (entries.next()) |entry| {
|
||||
entry.value.deinit();
|
||||
while (key_values.nextValue()) |system| {
|
||||
system.deinit();
|
||||
}
|
||||
|
||||
self.named_systems.deinit();
|
||||
self.named_systems.deinit(coral.heap.allocator);
|
||||
}
|
||||
|
||||
{
|
||||
var entries = self.initialized_states.entries();
|
||||
var states = self.initialized_states.values();
|
||||
|
||||
while (entries.next()) |entry| {
|
||||
entry.value.deinit();
|
||||
while (states.next()) |state| {
|
||||
state.deinit();
|
||||
}
|
||||
|
||||
self.initialized_states.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
fn scheduleName(comptime schedule: anytype) [:0]const u8 {
|
||||
return switch (@typeInfo(@TypeOf(schedule))) {
|
||||
.enum_literal => @tagName(schedule),
|
||||
else => @compileError("paramater `schedule` must be an enum literal type"),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasState(self: *Self, comptime State: type) ?*State {
|
||||
if (self.initialized_states.get(.of(State))) |boxed_state| {
|
||||
return boxed_state.has(State).?;
|
||||
@ -69,7 +76,10 @@ pub fn init() std.mem.Allocator.Error!Self {
|
||||
}
|
||||
|
||||
pub fn on(self: *Self, comptime schedule: anytype, behavior: *const Behavior) std.mem.Allocator.Error!void {
|
||||
try (try self.systemNamed(schedule)).insert(self, behavior);
|
||||
const schedule_name = scheduleName(schedule);
|
||||
const systems = self.named_systems.get(schedule_name) orelse (try self.named_systems.insert(coral.heap.allocator, schedule_name, .empty)).?;
|
||||
|
||||
try systems.insert(self, behavior);
|
||||
}
|
||||
|
||||
pub fn putState(self: *Self, state: anytype) std.mem.Allocator.Error!*@TypeOf(state) {
|
||||
@ -79,7 +89,10 @@ pub fn putState(self: *Self, state: anytype) std.mem.Allocator.Error!*@TypeOf(st
|
||||
}
|
||||
|
||||
pub fn run(self: *Self, tasks: *coral.asio.TaskQueue, comptime schedule: anytype) RunError!void {
|
||||
try (try self.systemNamed(schedule)).run(self, tasks);
|
||||
const schedule_name = scheduleName(schedule);
|
||||
const systems = self.named_systems.get(schedule_name) orelse (try self.named_systems.insert(coral.heap.allocator, schedule_name, .empty)).?;
|
||||
|
||||
try systems.run(self, tasks);
|
||||
}
|
||||
|
||||
pub fn setState(self: *Self, state: anytype) std.mem.Allocator.Error!void {
|
||||
@ -95,12 +108,3 @@ pub fn setState(self: *Self, state: anytype) std.mem.Allocator.Error!void {
|
||||
entry.value.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
fn systemNamed(self: *Self, comptime schedule: anytype) std.mem.Allocator.Error!*SystemGraph {
|
||||
const schedule_name = switch (@typeInfo(@TypeOf(schedule))) {
|
||||
.enum_literal => @tagName(schedule),
|
||||
else => @compileError("paramater `schedule` must be an enum literal type"),
|
||||
};
|
||||
|
||||
return self.named_systems.get(schedule_name) orelse (try self.named_systems.emplace(schedule_name, .empty)).?;
|
||||
}
|
||||
|
@ -12,12 +12,12 @@ blocking_work: BehaviorSet,
|
||||
parallel_work: BehaviorSet,
|
||||
parallel_work_ranges: coral.Stack(usize),
|
||||
|
||||
const AccessMap = coral.map.Hashed(*const coral.TypeId, BehaviorSet, coral.map.ptrTraits(*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);
|
||||
|
||||
fn Map(comptime Payload: type) type {
|
||||
return coral.map.Hashed(*const ona.App.Behavior, Payload, coral.map.ptrTraits(*const ona.App.Behavior));
|
||||
return coral.tree.Binary(*const ona.App.Behavior, Payload, coral.tree.scalarTraits(*const ona.App.Behavior));
|
||||
}
|
||||
|
||||
const Self = @This();
|
||||
@ -32,26 +32,26 @@ pub const TypeDependency = struct {
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.processed.deinit();
|
||||
self.processed.deinit(coral.heap.allocator);
|
||||
self.parallel_work.deinit();
|
||||
self.parallel_work_ranges.deinit();
|
||||
self.blocking_work.deinit();
|
||||
|
||||
inline for (.{ &self.dependants_edges, &self.state_readers, &self.state_writers }) |map| {
|
||||
var entries = map.entries();
|
||||
var key_values = map.keyValues();
|
||||
|
||||
while (entries.next()) |entry| {
|
||||
entry.value.deinit();
|
||||
while (key_values.nextValue()) |value| {
|
||||
value.deinit();
|
||||
}
|
||||
|
||||
map.deinit();
|
||||
map.deinit(coral.heap.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const edges = self.dependants_edges.get(dependant).?;
|
||||
const edges = self.dependants_edges.get(dependant) orelse (try self.dependants_edges.insert(coral.heap.allocator, dependant, .empty)).?;
|
||||
|
||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, edges.values, dependency) == null) {
|
||||
try edges.pushGrow(dependency);
|
||||
@ -59,7 +59,7 @@ pub fn dependOnBehavior(self: *Self, app: *ona.App, dependant: *const ona.App.Be
|
||||
}
|
||||
|
||||
pub fn dependOnType(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: TypeDependency) std.mem.Allocator.Error!void {
|
||||
var readers = self.state_readers.get(dependency.id) orelse (try self.state_readers.emplace(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| {
|
||||
for (readers.values[0..index]) |reader| {
|
||||
@ -78,7 +78,7 @@ pub fn dependOnType(self: *Self, app: *ona.App, dependant: *const ona.App.Behavi
|
||||
}
|
||||
|
||||
if (!dependency.is_read_only) {
|
||||
const writers = self.state_writers.get(dependency.id) orelse (try self.state_writers.emplace(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| {
|
||||
for (writers.values[0..index]) |reader| {
|
||||
@ -111,7 +111,7 @@ pub const empty = Self{
|
||||
pub fn insert(self: *Self, app: *ona.App, behavior: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||
self.processed.clear();
|
||||
|
||||
if ((try self.dependants_edges.emplace(behavior, .empty)) != null) {
|
||||
if (try self.dependants_edges.insert(coral.heap.allocator, behavior, .empty) != null) {
|
||||
try behavior.on_insertion(behavior, app, self);
|
||||
}
|
||||
}
|
||||
@ -133,7 +133,7 @@ fn process(self: *Self, behavior: *const ona.App.Behavior, dependencies: Behavio
|
||||
inherited_traits = inherited_traits.derived(traits);
|
||||
}
|
||||
|
||||
if (try self.processed.emplace(behavior, {})) {
|
||||
if (try self.processed.insert(coral.heap.allocator, behavior, {}) != null) {
|
||||
try switch (inherited_traits.is_thread_unsafe) {
|
||||
true => self.blocking_work.pushGrow(behavior),
|
||||
false => self.parallel_work.pushGrow(behavior),
|
||||
@ -153,11 +153,11 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
||||
self.parallel_work.clear();
|
||||
self.parallel_work_ranges.clear();
|
||||
|
||||
var dependents_edges = self.dependants_edges.entries();
|
||||
var dependants_edges = self.dependants_edges.keyValues();
|
||||
|
||||
while (dependents_edges.next()) |entry| {
|
||||
while (dependants_edges.next()) |dependants_edge| {
|
||||
const parallel_work_offset = self.parallel_work.values.len;
|
||||
const flags = try self.process(entry.key, entry.value);
|
||||
const flags = try self.process(dependants_edge.key, dependants_edge.value.*);
|
||||
|
||||
if (flags.is_thread_unsafe) {
|
||||
std.debug.assert(parallel_work_offset == self.parallel_work.values.len);
|
||||
|
@ -70,11 +70,21 @@ fn compile_shaders(_: ona.Write(Context), assets: ona.Assets) !void {
|
||||
.location = 0,
|
||||
});
|
||||
|
||||
switch (try root.parseText(&arena, try assets.load(shader_path, arena.allocator()))) {
|
||||
switch (try root.parse(&arena, try assets.load(shader_path, arena.allocator()))) {
|
||||
.ok => {
|
||||
const spirv_module = try coral.shaders.compileFragmentSpirv(&arena, root.hasFunction("frag") orelse {
|
||||
std.log.err("effect shader {s} requires an entry-point named `frag`", .{shader_path});
|
||||
|
||||
return error.ShaderParseFailure;
|
||||
});
|
||||
|
||||
var codes = coral.Stack(u8).empty;
|
||||
|
||||
try coral.shaders.generateSpirv(coral.bytes.stackWriter(&codes), root);
|
||||
defer {
|
||||
codes.deinit();
|
||||
}
|
||||
|
||||
try spirv_module.writeTo(coral.bytes.stackWriter(&codes));
|
||||
|
||||
std.log.info("{s}", .{codes.values});
|
||||
},
|
||||
@ -86,6 +96,8 @@ fn compile_shaders(_: ona.Write(Context), assets: ona.Assets) !void {
|
||||
},
|
||||
}
|
||||
|
||||
@breakpoint();
|
||||
|
||||
// const basic_shader = ext.SDL_CreateGPUShader(gpu_device, &.{
|
||||
// .code = spirv_code,
|
||||
// .code_size = spirv_code.len,
|
||||
|
Loading…
x
Reference in New Issue
Block a user