Tidy up GLSL code injection interface
This commit is contained in:
parent
8dacc9b080
commit
e1d41ded4a
18
readme.md
18
readme.md
@ -4,12 +4,14 @@
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
1. [Goals](#goals)
|
||||
1. [Technical Details](#technical-details)
|
||||
1. [Requirements](#requirements)
|
||||
1. [Building](#building)
|
||||
1. [Packaging](#packaging)
|
||||
- [Ona](#ona)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Overview](#overview)
|
||||
- [Goals](#goals)
|
||||
- [Technical Details](#technical-details)
|
||||
- [Requirements](#requirements)
|
||||
- [Building](#building)
|
||||
- [Packaging](#packaging)
|
||||
|
||||
## Overview
|
||||
|
||||
@ -84,5 +86,5 @@ app.root_module.addImport("coral", ona_dependency.module("coral"));
|
||||
b.installArtifact(app);
|
||||
```
|
||||
|
||||
5. Create a `main.zig` containing a valid Ona app declaration.
|
||||
6. Run `zig build` to build your new game application.
|
||||
1. Create a `main.zig` containing a valid Ona app declaration.
|
||||
2. Run `zig build` to build your new game application.
|
||||
|
@ -101,6 +101,18 @@ pub fn allocFormatted(allocator: std.mem.Allocator, comptime format: []const u8,
|
||||
return @constCast(printFormatted(buffer, format, args) catch unreachable);
|
||||
}
|
||||
|
||||
pub fn altFormat(value: anytype, comptime format: fn (@TypeOf(value), Writable) ReadWriteError!void) struct {
|
||||
formattable: @TypeOf(value),
|
||||
|
||||
pub fn writeFormat(self: @This(), output: Writable) ReadWriteError!void {
|
||||
return format(self.formattable, output);
|
||||
}
|
||||
} {
|
||||
return .{
|
||||
.formattable = value,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn countFormatted(comptime format: []const u8, args: anytype) usize {
|
||||
var count = WriteCount{};
|
||||
|
||||
|
131
src/ona/gfx.zig
131
src/ona/gfx.zig
@ -10,14 +10,9 @@ const ona = @import("./ona.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Display = struct {
|
||||
width: u16,
|
||||
height: u16,
|
||||
is_hidden: bool,
|
||||
};
|
||||
|
||||
const Context = struct {
|
||||
window: *ext.SDL_Window,
|
||||
frame_arena: std.heap.ArenaAllocator,
|
||||
gpu_device: *ext.SDL_GPUDevice,
|
||||
shader_compiler: ext.shaderc_compiler_t,
|
||||
spirv_options: ext.shaderc_compile_options_t,
|
||||
@ -29,8 +24,11 @@ const Context = struct {
|
||||
fragment,
|
||||
};
|
||||
|
||||
pub fn assemble(self: *Context, allocator: std.mem.Allocator, kind: AssemblyKind, name: [*:0]const u8, source: []const u8) AssembleError![]u8 {
|
||||
const result = ext.shaderc_compile_into_spv(self.shader_compiler, source.ptr, source.len, switch (kind) {
|
||||
pub fn assembleShader(self: *Context, kind: AssemblyKind, name: [*:0]const u8, source: []const u8, injections: anytype) AssembleError![]const u8 {
|
||||
const frame_allocator = self.frame_arena.allocator();
|
||||
const injected_source = try glsl.inject(frame_allocator, source, 430, injections);
|
||||
|
||||
const result = ext.shaderc_compile_into_spv(self.shader_compiler, injected_source.ptr, injected_source.len, switch (kind) {
|
||||
.fragment => ext.shaderc_glsl_vertex_shader,
|
||||
.vertex => ext.shaderc_glsl_vertex_shader,
|
||||
}, name, "main", self.spirv_options) orelse {
|
||||
@ -46,12 +44,12 @@ const Context = struct {
|
||||
const compiled_len = ext.shaderc_result_get_length(result);
|
||||
const compiled_ptr = ext.shaderc_result_get_bytes(result);
|
||||
|
||||
return allocator.dupe(u8, compiled_ptr[0..compiled_len]);
|
||||
return frame_allocator.dupe(u8, compiled_ptr[0..compiled_len]);
|
||||
},
|
||||
|
||||
ext.shaderc_compilation_status_compilation_error, ext.shaderc_compilation_status_invalid_stage => {
|
||||
std.log.err("{s}", .{ext.shaderc_result_get_error_message(result)});
|
||||
std.log.debug("problematic shader:\n{s}", .{source});
|
||||
std.log.debug("problematic shader:\n{s}", .{injected_source});
|
||||
|
||||
return error.BadSyntax;
|
||||
},
|
||||
@ -61,11 +59,16 @@ const Context = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn assembleShaderEmbedded(self: *Context, kind: AssemblyKind, comptime path: [:0]const u8, injections: anytype) AssembleError![]const u8 {
|
||||
return self.assembleShader(kind, path, @embedFile(path), injections);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Context) void {
|
||||
ext.shaderc_compile_options_release(self.spirv_options);
|
||||
ext.shaderc_compiler_release(self.shader_compiler);
|
||||
ext.SDL_DestroyGPUDevice(self.gpu_device);
|
||||
ext.SDL_DestroyWindow(self.window);
|
||||
self.frame_arena.deinit();
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
@ -75,67 +78,44 @@ const Context = struct {
|
||||
};
|
||||
};
|
||||
|
||||
pub const Display = struct {
|
||||
width: u16,
|
||||
height: u16,
|
||||
is_hidden: bool,
|
||||
};
|
||||
|
||||
pub const Effect = struct {
|
||||
pipeline: *ext.SDL_GPUGraphicsPipeline,
|
||||
};
|
||||
|
||||
fn compile_shaders(context: ona.Write(Context)) !void {
|
||||
const Camera = extern struct {
|
||||
projection: [4]@Vector(4, f32),
|
||||
};
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(coral.heap.allocator);
|
||||
const vertex_spirv = try context.ptr.assembleShaderEmbedded(.vertex, "./gfx/canvas.vert", .{
|
||||
.model_xy = glsl.nativeInput(0, @Vector(2, f32)),
|
||||
.model_uv = glsl.nativeInput(1, @Vector(2, f32)),
|
||||
.model_rgba = glsl.nativeInput(2, @Vector(4, f32)),
|
||||
.instance_uv_offset = glsl.nativeInput(3, @Vector(2, f32)),
|
||||
.instance_uv_scale = glsl.nativeInput(4, @Vector(2, f32)),
|
||||
.instance_xbasis = glsl.nativeInput(5, @Vector(2, f32)),
|
||||
.instance_ybasis = glsl.nativeInput(6, @Vector(2, f32)),
|
||||
.instance_origin = glsl.nativeInput(7, @Vector(2, f32)),
|
||||
.instance_pivot = glsl.nativeInput(8, @Vector(2, f32)),
|
||||
.instance_rgb = glsl.nativeInput(9, @Vector(3, f32)),
|
||||
.instance_bits = glsl.nativeInput(10, u32),
|
||||
.uv = glsl.nativeOutput(0, @Vector(2, f32)),
|
||||
.rgba = glsl.nativeOutput(1, @Vector(4, f32)),
|
||||
.camera = glsl.nativeUniformBlock(0, Camera),
|
||||
});
|
||||
|
||||
defer {
|
||||
arena.deinit();
|
||||
}
|
||||
|
||||
const arena_allocator = arena.allocator();
|
||||
|
||||
const vertex_spirv = try context.ptr.assemble(arena_allocator, .vertex, "./gfx/effect.vert", try glsl.inject(arena_allocator, @embedFile("./gfx/effect.vert"), .{
|
||||
.inputs = &.{
|
||||
.{ "model_xy", .vec2 },
|
||||
.{ "model_uv", .vec2 },
|
||||
.{ "model_rgba", .vec4 },
|
||||
|
||||
.{ "instance_rect", .vec4 },
|
||||
.{ "instance_xbasis", .vec2 },
|
||||
.{ "instance_ybasis", .vec2 },
|
||||
.{ "instance_origin", .vec2 },
|
||||
.{ "instance_color", .vec4 },
|
||||
.{ "instance_depth", .float },
|
||||
},
|
||||
|
||||
.outputs = &.{
|
||||
.{ "color", .vec4 },
|
||||
.{ "uv", .vec2 },
|
||||
},
|
||||
|
||||
.uniforms = &.{
|
||||
.{ "camera", .init(0, Camera) },
|
||||
},
|
||||
}));
|
||||
|
||||
const fragment_spirv = try context.ptr.assemble(arena_allocator, .fragment, "./gfx/effect.frag", try glsl.inject(arena_allocator, @embedFile("./gfx/effect.frag"), .{
|
||||
.inputs = &.{
|
||||
.{ "vertex_color", .vec4 },
|
||||
.{ "vertex_uv", .vec2 },
|
||||
},
|
||||
|
||||
.samplers = &.{
|
||||
.{ "source_texture", .{ .sampler_2d = 0 } },
|
||||
},
|
||||
|
||||
.outputs = &.{
|
||||
.{ "color", .vec4 },
|
||||
},
|
||||
|
||||
.uniforms = &.{
|
||||
.{ "camera", .init(0, Camera) },
|
||||
},
|
||||
}));
|
||||
|
||||
const effect_fragment = ext.SDL_CreateGPUShader(context.ptr.gpu_device, &.{
|
||||
.code = fragment_spirv.ptr,
|
||||
.code_size = fragment_spirv.len,
|
||||
.format = ext.SDL_GPU_SHADERFORMAT_SPIRV,
|
||||
.entrypoint = "main",
|
||||
const fragment_spirv = try context.ptr.assembleShaderEmbedded(.fragment, "./gfx/canvas.frag", .{
|
||||
.vertex_uv = glsl.nativeInput(0, @Vector(2, f32)),
|
||||
.vertex_rgba = glsl.nativeInput(1, @Vector(4, f32)),
|
||||
.source_texture = glsl.Sampler{ .sampler_2d = 0 },
|
||||
.color = glsl.nativeOutput(0, @Vector(4, f32)),
|
||||
.camera = glsl.nativeUniformBlock(0, Camera),
|
||||
});
|
||||
|
||||
const effect_vertex = ext.SDL_CreateGPUShader(context.ptr.gpu_device, &.{
|
||||
@ -145,8 +125,20 @@ fn compile_shaders(context: ona.Write(Context)) !void {
|
||||
.entrypoint = "main",
|
||||
});
|
||||
|
||||
_ = effect_fragment;
|
||||
_ = effect_vertex;
|
||||
defer {
|
||||
ext.SDL_ReleaseGPUShader(context.ptr.gpu_device, effect_vertex);
|
||||
}
|
||||
|
||||
const effect_fragment = ext.SDL_CreateGPUShader(context.ptr.gpu_device, &.{
|
||||
.code = fragment_spirv.ptr,
|
||||
.code_size = fragment_spirv.len,
|
||||
.format = ext.SDL_GPU_SHADERFORMAT_SPIRV,
|
||||
.entrypoint = "main",
|
||||
});
|
||||
|
||||
defer {
|
||||
ext.SDL_ReleaseGPUShader(context.ptr.gpu_device, effect_fragment);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(exit: ona.Send(ona.App.Exit), hid_events: ona.Send(ona.hid.Event)) !void {
|
||||
@ -297,6 +289,7 @@ pub fn setup(app: *ona.App) !void {
|
||||
});
|
||||
|
||||
try app.setState(Context{
|
||||
.frame_arena = .init(coral.heap.allocator),
|
||||
.shader_compiler = shader_compiler,
|
||||
.spirv_options = spirv_options,
|
||||
.window = window,
|
||||
@ -324,4 +317,8 @@ pub fn synchronize(context: ona.Write(Context), display: ona.Read(Display)) !voi
|
||||
std.log.warn("failed to change window visibility", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (!context.ptr.frame_arena.reset(.{ .retain_with_limit = 1024 * 1024 })) {
|
||||
std.log.warn("failed to shrink frame arena during reset, freeing instead...", .{});
|
||||
}
|
||||
}
|
||||
|
4
src/ona/gfx/canvas.frag
Normal file
4
src/ona/gfx/canvas.frag
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
void main() {
|
||||
color = vertex_rgba * texture(source_texture, vertex_uv);
|
||||
}
|
12
src/ona/gfx/canvas.vert
Normal file
12
src/ona/gfx/canvas.vert
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
void main() {
|
||||
const vec2 local_xy = model_xy + instance_pivot;
|
||||
const vec2 world_position = instance_origin + (local_xy.x * instance_xbasis) + (local_xy.y * instance_ybasis);
|
||||
const vec2 projected = (camera.projection * vec4(world_position, 0, 1)).xy;
|
||||
const vec2 depth_alpha = unpackHalf2x16(instance_bits);
|
||||
|
||||
gl_Position = vec4(projected, depth_alpha.x, 1.0);
|
||||
|
||||
rgba = model_rgba * vec4(instance_rgb, depth_alpha.y);
|
||||
uv = instance_uv_offset + (model_uv * instance_uv_scale);
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
|
||||
void main() {
|
||||
color = vertex_color * texture(source_texture, vertex_uv);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
|
||||
void main() {
|
||||
const vec2 world_position = instance_origin + model_xy.x * instance_xbasis + model_xy.y * instance_ybasis;
|
||||
const vec2 projected_position = (camera.projection * vec4(world_position, 0, 1)).xy;
|
||||
const vec2 rect_size = instance_rect.zw - instance_rect.xy;
|
||||
const vec4 screen_position = vec4(projected_position, instance_depth, 1.0);
|
||||
|
||||
gl_Position = screen_position;
|
||||
color = model_rgba * instance_color;
|
||||
uv = instance_rect.xy + (model_uv * rect_size);
|
||||
}
|
@ -4,100 +4,6 @@ const ona = @import("../ona.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const InjectOptions = struct {
|
||||
version: u64 = 430,
|
||||
inputs: []const Entry(Primitive) = &.{},
|
||||
outputs: []const Entry(Primitive) = &.{},
|
||||
uniforms: []const Entry(Uniform) = &.{},
|
||||
samplers: []const Entry(Sampler) = &.{},
|
||||
|
||||
pub fn Entry(comptime Item: type) type {
|
||||
return struct { [:0]const u8, Item };
|
||||
}
|
||||
|
||||
pub fn writeFormat(self: InjectOptions, source: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||
try coral.bytes.writeFormatted(source, "#version {version}\n\n", .{ .version = coral.utf8.cDec(self.version) });
|
||||
|
||||
for (0..self.inputs.len, self.inputs) |location, input| {
|
||||
const name, const primitive = input;
|
||||
|
||||
try coral.bytes.writeFormatted(source, "layout (location = {location}) in {primitive} {name};\n", .{
|
||||
.location = coral.utf8.cDec(location),
|
||||
.primitive = @tagName(primitive),
|
||||
.name = name,
|
||||
});
|
||||
}
|
||||
|
||||
try coral.bytes.writeAll(source, "\n");
|
||||
|
||||
for (0..self.outputs.len, self.outputs) |location, output| {
|
||||
const name, const primitive = output;
|
||||
|
||||
try coral.bytes.writeFormatted(source, "layout (location = {location}) out {primitive} {name};\n", .{
|
||||
.location = coral.utf8.cDec(location),
|
||||
.primitive = @tagName(primitive),
|
||||
.name = name,
|
||||
});
|
||||
}
|
||||
|
||||
try coral.bytes.writeAll(source, "\n");
|
||||
|
||||
for (self.samplers) |named_sampler| {
|
||||
const name, const sampler = named_sampler;
|
||||
|
||||
switch (sampler) {
|
||||
.sampler_2d => |binding| {
|
||||
try coral.bytes.writeFormatted(source, "layout (binding = {binding}) uniform sampler2D {name};\n\n", .{
|
||||
.binding = coral.utf8.cDec(binding),
|
||||
.name = name,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for (self.uniforms) |named_uniform| {
|
||||
const name, const uniform = named_uniform;
|
||||
|
||||
try coral.bytes.writeFormatted(source, "layout (binding = {binding}) uniform {type_name} {{\n", .{
|
||||
.binding = coral.utf8.cDec(uniform.binding),
|
||||
.type_name = uniform.name,
|
||||
});
|
||||
|
||||
var field = uniform.field;
|
||||
|
||||
while (true) : (field = field.has_next orelse {
|
||||
break;
|
||||
}) {
|
||||
try coral.bytes.writeFormatted(source, "\t{primitive} {name};\n", .{
|
||||
.primitive = @tagName(field.primitive),
|
||||
.name = field.name,
|
||||
});
|
||||
}
|
||||
|
||||
try coral.bytes.writeFormatted(source, "}} {name};\n\n", .{ .name = name });
|
||||
}
|
||||
|
||||
try coral.bytes.writeAll(source, "#line 1\n\n");
|
||||
}
|
||||
};
|
||||
|
||||
pub const Primitive = enum {
|
||||
float,
|
||||
vec2,
|
||||
vec3,
|
||||
vec4,
|
||||
mat4,
|
||||
};
|
||||
|
||||
pub const Sampler = union(enum) {
|
||||
sampler_2d: usize,
|
||||
};
|
||||
|
||||
pub const Uniform = struct {
|
||||
name: [:0]const u8,
|
||||
binding: usize,
|
||||
field: *const Field,
|
||||
|
||||
pub const Field = struct {
|
||||
name: [:0]const u8,
|
||||
primitive: Primitive,
|
||||
@ -108,15 +14,12 @@ pub const Uniform = struct {
|
||||
@compileError("Struct contains no fields");
|
||||
}
|
||||
|
||||
const first_uniform_field = uniform_fields[0];
|
||||
|
||||
const field = struct {
|
||||
const instance = Field{
|
||||
.name = uniform_fields[0].name,
|
||||
|
||||
.primitive = switch (uniform_fields[0].type) {
|
||||
@Vector(4, f32) => .vec4,
|
||||
[4]@Vector(4, f32) => .mat4,
|
||||
else => @compileError("Unsupported uniform type"),
|
||||
},
|
||||
.primitive = nativePrimitive(first_uniform_field.type),
|
||||
.name = first_uniform_field.name,
|
||||
|
||||
.has_next = switch (uniform_fields.len) {
|
||||
1 => null,
|
||||
@ -129,7 +32,111 @@ pub const Uniform = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(binding: usize, comptime Struct: type) Uniform {
|
||||
pub const Input = struct {
|
||||
location: usize,
|
||||
primitive: Primitive,
|
||||
|
||||
pub fn serialize(self: Input, name: []const u8, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||
return coral.bytes.writeFormatted(output, "layout (location = {location}) in {primitive} {name};\n", .{
|
||||
.primitive = @tagName(self.primitive),
|
||||
.location = coral.utf8.cDec(self.location),
|
||||
.name = name,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pub const Output = struct {
|
||||
location: usize,
|
||||
primitive: Primitive,
|
||||
|
||||
pub fn serialize(self: Output, name: []const u8, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||
return coral.bytes.writeFormatted(output, "layout (location = {location}) out {primitive} {name};\n", .{
|
||||
.primitive = @tagName(self.primitive),
|
||||
.location = coral.utf8.cDec(self.location),
|
||||
.name = name,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pub const Primitive = enum {
|
||||
float,
|
||||
uint,
|
||||
int,
|
||||
vec2,
|
||||
vec3,
|
||||
vec4,
|
||||
mat4,
|
||||
};
|
||||
|
||||
pub const Sampler = union(enum) {
|
||||
sampler_2d: usize,
|
||||
|
||||
pub fn serialize(self: Sampler, name: []const u8, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||
return switch (self) {
|
||||
.sampler_2d => |binding| coral.bytes.writeFormatted(output, "layout (binding = {binding}) uniform sampler2D {name};\n", .{
|
||||
.binding = coral.utf8.cDec(binding),
|
||||
.name = name,
|
||||
}),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const UniformBlock = struct {
|
||||
name: [:0]const u8,
|
||||
binding: usize,
|
||||
field: *const Field,
|
||||
|
||||
pub fn serialize(self: UniformBlock, name: []const u8, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||
var field = self.field;
|
||||
|
||||
try coral.bytes.writeFormatted(output, "layout (binding = {binding}) uniform {name} {{\n", .{
|
||||
.binding = coral.utf8.cDec(self.binding),
|
||||
.name = self.name,
|
||||
});
|
||||
|
||||
while (true) : (field = field.has_next orelse {
|
||||
break;
|
||||
}) {
|
||||
try coral.bytes.writeFormatted(output, "\t{primitive} {name};\n", .{
|
||||
.primitive = @tagName(field.primitive),
|
||||
.name = field.name,
|
||||
});
|
||||
}
|
||||
|
||||
try coral.bytes.writeFormatted(output, "}} {namespace};\n", .{
|
||||
.namespace = name,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pub fn nativeInput(location: usize, comptime Value: type) Input {
|
||||
return .{
|
||||
.primitive = nativePrimitive(Value),
|
||||
.location = location,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn nativePrimitive(comptime Value: type) Primitive {
|
||||
return switch (Value) {
|
||||
f32 => .float,
|
||||
i32 => .int,
|
||||
u32 => .uint,
|
||||
@Vector(2, f32) => .vec2,
|
||||
@Vector(3, f32) => .vec3,
|
||||
@Vector(4, f32) => .vec4,
|
||||
[4]@Vector(4, f32) => .mat4,
|
||||
else => @compileError(std.fmt.comptimePrint("{s} is not a GLSL-compatible type", .{@typeName(Value)})),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn nativeOutput(location: usize, comptime Value: type) Output {
|
||||
return .{
|
||||
.primitive = nativePrimitive(Value),
|
||||
.location = location,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn nativeUniformBlock(binding: usize, comptime Struct: type) UniformBlock {
|
||||
// TODO: Review how GLSL identifier-safe names are generated.
|
||||
const struct_name = @typeName(Struct);
|
||||
|
||||
@ -152,11 +159,39 @@ pub const Uniform = struct {
|
||||
.binding = binding,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn inject(allocator: std.mem.Allocator, source_base: []const u8, version: usize, interface: anytype) std.mem.Allocator.Error![:0]u8 {
|
||||
const Interface = @TypeOf(interface);
|
||||
|
||||
const interface_info = switch (@typeInfo(Interface)) {
|
||||
.@"struct" => |@"struct"| @"struct",
|
||||
else => @compileError("`interface` must be a struct"),
|
||||
};
|
||||
|
||||
pub fn inject(allocator: std.mem.Allocator, source_base: []const u8, options: InjectOptions) std.mem.Allocator.Error![:0]u8 {
|
||||
return coral.bytes.allocFormatted(allocator, "{injected_source}\n{original_source}\n", .{
|
||||
.injected_source = options,
|
||||
if (interface_info.is_tuple) {
|
||||
@compileError("`interface` cannot be a tuple");
|
||||
}
|
||||
|
||||
const serialization = struct {
|
||||
fn write(value: Interface, output: coral.bytes.Writable) coral.bytes.ReadWriteError!void {
|
||||
inline for (interface_info.fields) |field| {
|
||||
const field_value = @field(value, field.name);
|
||||
const FieldValue = @TypeOf(field_value);
|
||||
|
||||
if (!@hasDecl(FieldValue, "serialize")) {
|
||||
@compileError(std.fmt.comptimePrint("`interface.{s}` is not a GLSL-serializable field", .{field.name}));
|
||||
}
|
||||
|
||||
try field_value.serialize(field.name, output);
|
||||
}
|
||||
|
||||
try coral.bytes.writeAll(output, "#line 1\n");
|
||||
}
|
||||
};
|
||||
|
||||
return coral.bytes.allocFormatted(allocator, "#version {version}\n{injected_source}\n{original_source}\n", .{
|
||||
.version = coral.utf8.cDec(version),
|
||||
.injected_source = coral.bytes.altFormat(interface, serialization.write),
|
||||
.original_source = source_base,
|
||||
});
|
||||
}
|
||||
|
BIN
testing.spv
(Stored with Git LFS)
BIN
testing.spv
(Stored with Git LFS)
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user