Add runtime shader reflection support
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
2ba249f4bf
commit
842dbd56cf
|
@ -7,7 +7,7 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"target": "${workspaceRoot}/zig-out/bin/main",
|
"target": "${workspaceRoot}/zig-out/bin/main",
|
||||||
"cwd": "${workspaceRoot}/debug/",
|
"cwd": "${workspaceRoot}/debug/",
|
||||||
"valuesFormatting": "parseText",
|
"valuesFormatting": "prettyPrinters",
|
||||||
"preLaunchTask": "Build All"
|
"preLaunchTask": "Build All"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -115,7 +115,7 @@ pub const DecimalFormat = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(self: DecimalFormat, writer: io.Writer, value: anytype) io.PrintError!void {
|
pub fn format(self: DecimalFormat, writer: io.Writer, value: anytype) io.PrintError!void {
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
return io.print(writer, switch (self.positive_prefix) {
|
return io.print(writer, switch (self.positive_prefix) {
|
||||||
.none => "0",
|
.none => "0",
|
||||||
|
@ -178,13 +178,13 @@ pub const HexadecimalFormat = struct {
|
||||||
positive_prefix: enum {none, plus, space} = .none,
|
positive_prefix: enum {none, plus, space} = .none,
|
||||||
casing: enum {lower, upper} = .lower,
|
casing: enum {lower, upper} = .lower,
|
||||||
|
|
||||||
const default = HexadecimalFormat{
|
pub const default = HexadecimalFormat{
|
||||||
.delimiter = "",
|
.delimiter = "",
|
||||||
.positive_prefix = .none,
|
.positive_prefix = .none,
|
||||||
.casing = .lower,
|
.casing = .lower,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn print(self: HexadecimalFormat, writer: io.Writer, value: anytype) ?usize {
|
pub fn format(self: HexadecimalFormat, writer: io.Writer, value: anytype) io.Error!void {
|
||||||
// TODO: Implement.
|
// TODO: Implement.
|
||||||
_ = self;
|
_ = self;
|
||||||
_ = writer;
|
_ = writer;
|
||||||
|
|
|
@ -108,13 +108,23 @@ pub fn Generator(comptime Output: type, comptime input_types: []const type) type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const PrintError = Error || error {
|
pub const NullWritable = struct {
|
||||||
IncompleteWrite,
|
written: usize = 0,
|
||||||
|
|
||||||
|
pub fn write(self: *NullWritable, buffer: []const Byte) Error!usize {
|
||||||
|
self.written += buffer.len;
|
||||||
|
|
||||||
|
return buffer.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writer(self: *NullWritable) Writer {
|
||||||
|
return Writer.bind(NullWritable, self, write);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Reader = Generator(Error!usize, &.{[]coral.Byte});
|
pub const Reader = Generator(Error!usize, &.{[]Byte});
|
||||||
|
|
||||||
pub const Writer = Generator(Error!usize, &.{[]const coral.Byte});
|
pub const Writer = Generator(Error!usize, &.{[]const Byte});
|
||||||
|
|
||||||
pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.Byte {
|
pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.Byte {
|
||||||
const buffer = coral.Stack(coral.Byte){.allocator = allocator};
|
const buffer = coral.Stack(coral.Byte){.allocator = allocator};
|
||||||
|
@ -138,12 +148,6 @@ pub fn bytes_of(value: anytype) []const Byte {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(writer: Writer, utf8: []const u8) PrintError!void {
|
|
||||||
if (try writer.yield(.{utf8}) != utf8.len) {
|
|
||||||
return error.IncompleteWrite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn skip_n(input: Reader, distance: u64) Error!void {
|
pub fn skip_n(input: Reader, distance: u64) Error!void {
|
||||||
var buffer = @as([512]coral.Byte, undefined);
|
var buffer = @as([512]coral.Byte, undefined);
|
||||||
var remaining = distance;
|
var remaining = distance;
|
||||||
|
@ -159,7 +163,7 @@ pub fn skip_n(input: Reader, distance: u64) Error!void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slice_sentineled(comptime sen: anytype, ptr: [*:sen]const @TypeOf(sen)) [:sen]const @TypeOf(sen) {
|
pub fn slice_sentineled(comptime Sentinel: type, comptime sen: Sentinel, ptr: [*:sen]const Sentinel) [:sen]const Sentinel {
|
||||||
var len = @as(usize, 0);
|
var len = @as(usize, 0);
|
||||||
|
|
||||||
while (ptr[len] != sen) {
|
while (ptr[len] != sen) {
|
||||||
|
@ -206,3 +210,9 @@ pub fn stream_n(input: Reader, output: Writer, limit: usize) Error!usize {
|
||||||
remaining -= read;
|
remaining -= read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_all(writer: Writer, utf8: []const u8) Error!void {
|
||||||
|
if (try writer.yield(.{utf8}) != utf8.len) {
|
||||||
|
return error.UnavailableResource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,15 +19,54 @@ pub fn alloc_formatted(allocator: std.mem.Allocator, comptime format: []const u8
|
||||||
return buffer.to_allocation(formatted_len, 0);
|
return buffer.to_allocation(formatted_len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_formatted(comptime format: []const u8, args: anytype) usize {
|
pub fn count_formatted(comptime format: []const u8, args: anytype) usize {
|
||||||
var count = io.defaultWritable{};
|
var count = io.NullWritable{};
|
||||||
|
|
||||||
print_formatted(count.writer(), format, args) catch unreachable;
|
write_formatted(count.writer(), format, args) catch unreachable;
|
||||||
|
|
||||||
return count.written;
|
return count.written;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_formatted(writer: io.Writer, comptime format: []const u8, args: anytype) io.PrintError!void {
|
pub fn print_formatted(buffer: [:0]coral.io.Byte, comptime format: []const u8, args: anytype) io.Error![:0]u8 {
|
||||||
|
const Seekable = struct {
|
||||||
|
buffer: []coral.io.Byte,
|
||||||
|
cursor: usize,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
fn write(self: *Self, input: []const coral.io.Byte) io.Error!usize {
|
||||||
|
const range = @min(input.len, self.buffer.len - self.cursor);
|
||||||
|
const tail = self.cursor + range;
|
||||||
|
|
||||||
|
@memcpy(self.buffer[self.cursor .. tail], input);
|
||||||
|
|
||||||
|
self.cursor = tail;
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const len = count_formatted(format, args);
|
||||||
|
|
||||||
|
if (len > buffer.len) {
|
||||||
|
return error.UnavailableResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seekable = Seekable{
|
||||||
|
.buffer = buffer,
|
||||||
|
.cursor = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
try write_formatted(coral.io.Writer.bind(Seekable, &seekable, Seekable.write), format, args);
|
||||||
|
|
||||||
|
if (buffer.len < len) {
|
||||||
|
buffer[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer[0 .. len:0];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_formatted(writer: io.Writer, comptime format: []const u8, args: anytype) io.Error!void {
|
||||||
switch (@typeInfo(@TypeOf(args))) {
|
switch (@typeInfo(@TypeOf(args))) {
|
||||||
.Struct => |arguments_struct| {
|
.Struct => |arguments_struct| {
|
||||||
comptime var arg_index = 0;
|
comptime var arg_index = 0;
|
||||||
|
@ -67,7 +106,7 @@ pub fn print_formatted(writer: io.Writer, comptime format: []const u8, args: any
|
||||||
@compileError("format specifiers cannot be named when using a tuple struct");
|
@compileError("format specifiers cannot be named when using a tuple struct");
|
||||||
}
|
}
|
||||||
|
|
||||||
try io.print(writer, format[head .. (tail - 1)]);
|
try io.write_all(writer, format[head .. (tail - 1)]);
|
||||||
|
|
||||||
head = tail;
|
head = tail;
|
||||||
tail += 1;
|
tail += 1;
|
||||||
|
@ -93,14 +132,14 @@ pub fn print_formatted(writer: io.Writer, comptime format: []const u8, args: any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try io.print(writer, format[head .. ]);
|
try io.write_all(writer, format[head .. ]);
|
||||||
},
|
},
|
||||||
|
|
||||||
else => @compileError("`arguments` must be a struct type"),
|
else => @compileError("`arguments` must be a struct type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noinline fn print_formatted_value(writer: io.Writer, value: anytype) io.PrintError!void {
|
noinline fn print_formatted_value(writer: io.Writer, value: anytype) io.Error!void {
|
||||||
const Value = @TypeOf(value);
|
const Value = @TypeOf(value);
|
||||||
|
|
||||||
return switch (@typeInfo(Value)) {
|
return switch (@typeInfo(Value)) {
|
||||||
|
@ -109,9 +148,9 @@ noinline fn print_formatted_value(writer: io.Writer, value: anytype) io.PrintErr
|
||||||
.Enum => io.print(writer, @tagName(value)),
|
.Enum => io.print(writer, @tagName(value)),
|
||||||
|
|
||||||
.Pointer => |pointer| switch (pointer.size) {
|
.Pointer => |pointer| switch (pointer.size) {
|
||||||
.Many, .C => ascii.HexadecimalFormat.default.print(writer, @intFromPtr(value)),
|
.Many, .C => ascii.HexadecimalFormat.default.format(writer, @intFromPtr(value)),
|
||||||
.One => if (pointer.child == []const u8) io.print(writer, *value) else ascii.HexadecimalFormat.default.print(writer, @intFromPtr(value)),
|
.One => if (pointer.child == []const u8) io.write_all(writer, *value) else ascii.HexadecimalFormat.default.print(writer, @intFromPtr(value)),
|
||||||
.Slice => if (pointer.child == u8) io.print(writer, value) else @compileError(unformattableMessage(Value)),
|
.Slice => if (pointer.child == u8) io.write_all(writer, value) else @compileError(unformattableMessage(Value)),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => @compileError(unformattableMessage(Value)),
|
else => @compileError(unformattableMessage(Value)),
|
||||||
|
|
|
@ -380,7 +380,7 @@ const Loop = struct {
|
||||||
ext.SDL_GL_DeleteContext(context);
|
ext.SDL_GL_DeleteContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rendering_2d = try Rendering2D.init(coral.heap.allocator);
|
var rendering_2d = try Rendering2D.init();
|
||||||
|
|
||||||
defer rendering_2d.deinit();
|
defer rendering_2d.deinit();
|
||||||
|
|
||||||
|
@ -535,6 +535,12 @@ const Rendering2D = struct {
|
||||||
|
|
||||||
const mesh_2d = resource_cast(frame.mesh.?).payload.mesh_2d;
|
const mesh_2d = resource_cast(frame.mesh.?).payload.mesh_2d;
|
||||||
|
|
||||||
|
const texture_image, const texture_sampler = switch (resource_cast(frame.texture.?).payload) {
|
||||||
|
.texture => |texture| .{texture.image, texture.sampler},
|
||||||
|
.render_target => |render_target| .{render_target.color_image, render_target.sampler},
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
var bindings = sokol.gfx.Bindings{
|
var bindings = sokol.gfx.Bindings{
|
||||||
.vertex_buffers = get: {
|
.vertex_buffers = get: {
|
||||||
var buffers = [_]sokol.gfx.Buffer{.{}} ** 8;
|
var buffers = [_]sokol.gfx.Buffer{.{}} ** 8;
|
||||||
|
@ -546,12 +552,11 @@ const Rendering2D = struct {
|
||||||
|
|
||||||
.index_buffer = mesh_2d.index_buffer,
|
.index_buffer = mesh_2d.index_buffer,
|
||||||
|
|
||||||
.fs = switch (resource_cast(frame.texture.?).payload) {
|
.fs = .{
|
||||||
.texture => |texture| .{
|
|
||||||
.images = get: {
|
.images = get: {
|
||||||
var images = [_]sokol.gfx.Image{.{}} ** 12;
|
var images = [_]sokol.gfx.Image{.{}} ** 12;
|
||||||
|
|
||||||
images[0] = texture.image;
|
images[0] = texture_image;
|
||||||
|
|
||||||
break: get images;
|
break: get images;
|
||||||
},
|
},
|
||||||
|
@ -559,17 +564,17 @@ const Rendering2D = struct {
|
||||||
.samplers = get: {
|
.samplers = get: {
|
||||||
var samplers = [_]sokol.gfx.Sampler{.{}} ** 8;
|
var samplers = [_]sokol.gfx.Sampler{.{}} ** 8;
|
||||||
|
|
||||||
samplers[0] = texture.sampler;
|
samplers[0] = texture_sampler;
|
||||||
|
|
||||||
break: get samplers;
|
break: get samplers;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
.render_target => |render_target| .{
|
.vs = .{
|
||||||
.images = get: {
|
.images = get: {
|
||||||
var images = [_]sokol.gfx.Image{.{}} ** 12;
|
var images = [_]sokol.gfx.Image{.{}} ** 12;
|
||||||
|
|
||||||
images[0] = render_target.color_image;
|
images[0] = texture_image;
|
||||||
|
|
||||||
break: get images;
|
break: get images;
|
||||||
},
|
},
|
||||||
|
@ -577,14 +582,11 @@ const Rendering2D = struct {
|
||||||
.samplers = get: {
|
.samplers = get: {
|
||||||
var samplers = [_]sokol.gfx.Sampler{.{}} ** 8;
|
var samplers = [_]sokol.gfx.Sampler{.{}} ** 8;
|
||||||
|
|
||||||
samplers[0] = render_target.sampler;
|
samplers[0] = texture_sampler;
|
||||||
|
|
||||||
break: get samplers;
|
break: get samplers;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
while (frame.flushed_batch_count < frame.pushed_batch_count) {
|
while (frame.flushed_batch_count < frame.pushed_batch_count) {
|
||||||
|
@ -602,7 +604,7 @@ const Rendering2D = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(allocator: std.mem.Allocator) spirv.CompileError!Rendering2D {
|
fn init() spirv.Error!Rendering2D {
|
||||||
sokol.gfx.setup(.{
|
sokol.gfx.setup(.{
|
||||||
.environment = .{
|
.environment = .{
|
||||||
.defaults = .{
|
.defaults = .{
|
||||||
|
@ -617,17 +619,15 @@ const Rendering2D = struct {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
var spirv_unit = try spirv.Unit.init();
|
||||||
|
|
||||||
defer arena.deinit();
|
defer spirv_unit.deinit();
|
||||||
|
|
||||||
const shader_desc = try spirv.compile(&arena, .{
|
try spirv_unit.compile(shader_spirv[0 ..], .vertex);
|
||||||
.fragment_spirv = shader_spirv[0 ..],
|
try spirv_unit.compile(shader_spirv[0 ..], .fragment);
|
||||||
.vertex_spirv = shader_spirv[0 ..],
|
|
||||||
});
|
|
||||||
|
|
||||||
std.log.info("{s}\n", .{shader_desc.fs.source});
|
std.log.info("{s}\n", .{spirv_unit.shader_desc.fs.source});
|
||||||
std.log.info("{s}", .{shader_desc.vs.source});
|
std.log.info("{s}", .{spirv_unit.shader_desc.vs.source});
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.batching_pipeline = sokol.gfx.makePipeline(.{
|
.batching_pipeline = sokol.gfx.makePipeline(.{
|
||||||
|
@ -689,7 +689,7 @@ const Rendering2D = struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
.shader = sokol.gfx.makeShader(shader_desc),
|
.shader = sokol.gfx.makeShader(spirv_unit.shader_desc),
|
||||||
.index_type = .UINT16,
|
.index_type = .UINT16,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
|
@ -8,123 +8,285 @@ const sokol = @import("sokol");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const CompileError = std.mem.Allocator.Error || error {
|
pub const Error = std.mem.Allocator.Error || error {
|
||||||
UnsupportedTarget,
|
UnsupportedTarget,
|
||||||
InvalidSpirV,
|
InvalidSPIRV,
|
||||||
UnsupportedSpirv,
|
UnsupportedSPIRV,
|
||||||
UnknownFailure,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Sources = struct {
|
pub const Unit = struct {
|
||||||
vertex_spirv: []const u32,
|
context: ext.spvc_context,
|
||||||
fragment_spirv: []const u32,
|
shader_desc: sokol.gfx.ShaderDesc,
|
||||||
};
|
attrs_used: u32 = 0,
|
||||||
|
|
||||||
pub fn compile(arena: *std.heap.ArenaAllocator, sources: Sources) CompileError!sokol.gfx.ShaderDesc {
|
pub fn compile(self: *Unit, spirv: []const u32, stage: Stage) Error!void {
|
||||||
var context = @as(ext.spvc_context, null);
|
const execution_model, const stage_desc = switch (stage) {
|
||||||
|
.vertex => .{ext.SpvExecutionModelVertex, &self.shader_desc.vs},
|
||||||
try switch (ext.spvc_context_create(&context)) {
|
.fragment => .{ext.SpvExecutionModelFragment, &self.shader_desc.fs},
|
||||||
ext.SPVC_ERROR_OUT_OF_MEMORY => error.OutOfMemory,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ext.spvc_context_set_error_callback(context, log_errors, null);
|
const Backend = struct {
|
||||||
|
target: ext.spvc_backend,
|
||||||
|
option_values: []const struct {ext.spvc_compiler_option, c_uint},
|
||||||
|
};
|
||||||
|
|
||||||
defer ext.spvc_context_destroy(context);
|
const backend: Backend = switch (sokol.gfx.queryBackend()) {
|
||||||
|
.GLCORE => .{
|
||||||
|
.target = ext.SPVC_BACKEND_GLSL,
|
||||||
|
|
||||||
const arena_allocator = arena.allocator();
|
.option_values = &.{
|
||||||
const shader_source_sentinel = @as(u8, 0);
|
.{ext.SPVC_COMPILER_OPTION_GLSL_VERSION, 430},
|
||||||
|
.{ext.SPVC_COMPILER_OPTION_GLSL_ES, @intFromBool(false)},
|
||||||
|
.{ext.SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, @intFromBool(false)},
|
||||||
|
.{ext.SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS, @intFromBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
else => @panic("Unimplemented"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const compiler = parse_and_configure: {
|
||||||
|
var parsed_ir: ext.spvc_parsed_ir = null;
|
||||||
|
|
||||||
|
try to_error(ext.spvc_context_parse_spirv(self.context, spirv.ptr, spirv.len, &parsed_ir));
|
||||||
|
|
||||||
|
var compiler: ext.spvc_compiler = null;
|
||||||
|
|
||||||
|
try to_error(ext.spvc_context_create_compiler(self.context, backend.target, parsed_ir, ext.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler));
|
||||||
|
try to_error(ext.spvc_compiler_build_combined_image_samplers(compiler));
|
||||||
|
|
||||||
|
var combined_image_samplers: []const ext.spvc_combined_image_sampler = &.{};
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_get_combined_image_samplers(compiler, @ptrCast(&combined_image_samplers.ptr), &combined_image_samplers.len));
|
||||||
|
|
||||||
|
var binding: u32 = 0;
|
||||||
|
|
||||||
|
for (combined_image_samplers) |combined_image_sampler| {
|
||||||
|
var name_buffer = [_:0]u8{0} ** 255;
|
||||||
|
|
||||||
|
const name = coral.utf8.print_formatted(&name_buffer, "{image_name}_{sampler_name}", .{
|
||||||
|
.image_name = coral.io.slice_sentineled(u8, 0, ext.spvc_compiler_get_name(compiler, combined_image_sampler.image_id)),
|
||||||
|
.sampler_name = coral.io.slice_sentineled(u8, 0, ext.spvc_compiler_get_name(compiler, combined_image_sampler.sampler_id)),
|
||||||
|
}) catch {
|
||||||
|
return error.InvalidSPIRV;
|
||||||
|
};
|
||||||
|
|
||||||
|
ext.spvc_compiler_set_name(compiler, combined_image_sampler.combined_id, name);
|
||||||
|
ext.spvc_compiler_set_decoration(compiler, combined_image_sampler.combined_id, ext.SpvDecorationBinding, binding);
|
||||||
|
|
||||||
|
binding += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break: parse_and_configure compiler;
|
||||||
|
};
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_set_entry_point(compiler, stage_desc.entry, @intCast(execution_model)));
|
||||||
|
|
||||||
|
const resources = create: {
|
||||||
|
var resources: ext.spvc_resources = null;
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_create_shader_resources(compiler, &resources));
|
||||||
|
|
||||||
|
break: create resources;
|
||||||
|
};
|
||||||
|
|
||||||
|
try reflect_uniform_blocks(compiler, resources, stage_desc);
|
||||||
|
try reflect_image_samplers(compiler, resources, stage_desc);
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_install_compiler_options(compiler, create: {
|
||||||
|
var options: ext.spvc_compiler_options = null;
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_create_compiler_options(compiler, &options));
|
||||||
|
|
||||||
|
for (backend.option_values) |option_value| {
|
||||||
|
const entry, const value = option_value;
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_options_set_uint(options, entry, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
break: create options;
|
||||||
|
}));
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_compile(compiler, @ptrCast(&stage_desc.source)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Unit) void {
|
||||||
|
ext.spvc_context_destroy(self.context);
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() std.mem.Allocator.Error!Unit {
|
||||||
|
var context: ext.spvc_context = null;
|
||||||
|
|
||||||
|
if (ext.spvc_context_create(&context) != ext.SPVC_SUCCESS) {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
errdefer ext.spvc_context_destroy(context);
|
||||||
|
|
||||||
|
ext.spvc_context_set_error_callback(context, log_context_errors, null);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.vs = .{
|
.context = context,
|
||||||
.source = try arena_allocator.dupeZ(u8, coral.io.slice_sentineled(
|
|
||||||
shader_source_sentinel,
|
|
||||||
@ptrCast(try compile_shader(context, ext.SpvExecutionModelVertex, sources.vertex_spirv)))),
|
|
||||||
},
|
|
||||||
|
|
||||||
.fs = .{
|
.shader_desc = .{
|
||||||
.source = try arena_allocator.dupeZ(u8, coral.io.slice_sentineled(
|
.vs = .{.entry = "main"},
|
||||||
shader_source_sentinel,
|
.fs = .{.entry = "main"},
|
||||||
@ptrCast(try compile_shader(context, ext.SpvExecutionModelFragment, sources.fragment_spirv)))),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fn compile_shader(context: ext.spvc_context, model: ext.SpvExecutionModel, spirv: []const u32) CompileError![*]const u8 {
|
pub const Stage = enum {
|
||||||
var parsed_ir = @as(ext.spvc_parsed_ir, null);
|
fragment,
|
||||||
|
vertex,
|
||||||
|
};
|
||||||
|
|
||||||
try switch (ext.spvc_context_parse_spirv(context, spirv.ptr, spirv.len, &parsed_ir)) {
|
fn log_context_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callconv(.C) void {
|
||||||
ext.SPVC_ERROR_OUT_OF_MEMORY => error.OutOfMemory,
|
|
||||||
ext.SPVC_ERROR_INVALID_SPIRV => error.InvalidSpirV,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
var compiler = @as(ext.spvc_compiler, null);
|
|
||||||
|
|
||||||
try switch (ext.spvc_context_create_compiler(context, ext.SPVC_BACKEND_GLSL, parsed_ir, ext.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler)) {
|
|
||||||
ext.SPVC_ERROR_OUT_OF_MEMORY => error.OutOfMemory,
|
|
||||||
ext.SPVC_ERROR_INVALID_ARGUMENT => error.UnsupportedTarget,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
try switch (ext.spvc_compiler_build_combined_image_samplers(compiler)) {
|
|
||||||
ext.SPVC_ERROR_UNSUPPORTED_SPIRV => error.UnsupportedSpirv,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
try switch (ext.spvc_compiler_set_entry_point(compiler, "main", model)) {
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
var options = @as(ext.spvc_compiler_options, null);
|
|
||||||
|
|
||||||
try switch (ext.spvc_compiler_create_compiler_options(compiler, &options)) {
|
|
||||||
ext.SPVC_ERROR_OUT_OF_MEMORY => error.OutOfMemory,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
try switch (ext.spvc_compiler_options_set_uint(options, ext.SPVC_COMPILER_OPTION_GLSL_VERSION, 430)) {
|
|
||||||
ext.SPVC_ERROR_INVALID_ARGUMENT => error.InvalidSpirV,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
try switch (ext.spvc_compiler_options_set_bool(options, ext.SPVC_COMPILER_OPTION_GLSL_ES, @intFromBool(false))) {
|
|
||||||
ext.SPVC_ERROR_INVALID_ARGUMENT => error.InvalidSpirV,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
try switch (ext.spvc_compiler_options_set_bool(options, ext.SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, @intFromBool(false))) {
|
|
||||||
ext.SPVC_ERROR_INVALID_ARGUMENT => error.InvalidSpirV,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
try switch (ext.spvc_compiler_install_compiler_options(compiler, options)) {
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
var source = @as([*]const u8, undefined);
|
|
||||||
|
|
||||||
try switch (ext.spvc_compiler_compile(compiler, @ptrCast(&source))) {
|
|
||||||
ext.SPVC_ERROR_OUT_OF_MEMORY => error.OutOfMemory,
|
|
||||||
ext.SPVC_ERROR_UNSUPPORTED_SPIRV => error.UnsupportedSpirv,
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
else => error.UnknownFailure,
|
|
||||||
};
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn log_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callconv(.C) void {
|
|
||||||
std.debug.assert(userdata == null);
|
std.debug.assert(userdata == null);
|
||||||
std.log.err("{s}", .{error_message});
|
std.log.err("{s}", .{error_message});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reflect_image_samplers(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage: *sokol.gfx.ShaderStageDesc) Error!void {
|
||||||
|
var reflected_sampled_images: []const ext.spvc_reflected_resource = &.{};
|
||||||
|
|
||||||
|
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
||||||
|
resources,
|
||||||
|
ext.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
|
||||||
|
@ptrCast(&reflected_sampled_images.ptr),
|
||||||
|
&reflected_sampled_images.len,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (reflected_sampled_images.len > stage.image_sampler_pairs.len) {
|
||||||
|
return error.UnsupportedSPIRV;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0 .. reflected_sampled_images.len, reflected_sampled_images) |i, reflected_sampled_image| {
|
||||||
|
const sampled_image_type = ext.spvc_compiler_get_type_handle(compiler, reflected_sampled_image.type_id);
|
||||||
|
|
||||||
|
if (ext.spvc_type_get_basetype(sampled_image_type) != ext.SPVC_BASETYPE_SAMPLED_IMAGE) {
|
||||||
|
return error.InvalidSPIRV;
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.images[i] = .{
|
||||||
|
.multisampled = ext.spvc_type_get_image_multisampled(sampled_image_type) != 0,
|
||||||
|
|
||||||
|
.image_type = try switch (ext.spvc_type_get_image_dimension(sampled_image_type)) {
|
||||||
|
ext.SpvDim2D => sokol.gfx.ImageType._2D,
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
.sample_type = try switch (ext.spvc_type_get_basetype(ext.spvc_compiler_get_type_handle(compiler, ext.spvc_type_get_image_sampled_type(sampled_image_type)))) {
|
||||||
|
ext.SPVC_BASETYPE_FP32 => sokol.gfx.ImageSampleType.FLOAT,
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
.used = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
stage.samplers[i] = .{
|
||||||
|
.sampler_type = .DEFAULT,
|
||||||
|
.used = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
stage.image_sampler_pairs[i] = .{
|
||||||
|
.glsl_name = ext.spvc_compiler_get_name(compiler, reflected_sampled_image.id),
|
||||||
|
.image_slot = @intCast(i),
|
||||||
|
.sampler_slot = @intCast(i),
|
||||||
|
.used = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflect_uniform_blocks(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage: *sokol.gfx.ShaderStageDesc) Error!void {
|
||||||
|
var reflected_uniform_buffers: []const ext.spvc_reflected_resource = &.{};
|
||||||
|
|
||||||
|
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
||||||
|
resources,
|
||||||
|
ext.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
|
||||||
|
@ptrCast(&reflected_uniform_buffers.ptr),
|
||||||
|
&reflected_uniform_buffers.len,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (reflected_uniform_buffers.len > stage.uniform_blocks.len) {
|
||||||
|
return error.UnsupportedSPIRV;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (stage.uniform_blocks[0 .. reflected_uniform_buffers.len], reflected_uniform_buffers) |*uniform_block, reflected_uniform_buffer| {
|
||||||
|
const uniform_buffer_type = ext.spvc_compiler_get_type_handle(compiler, reflected_uniform_buffer.type_id);
|
||||||
|
|
||||||
|
if (ext.spvc_type_get_basetype(uniform_buffer_type) != ext.SPVC_BASETYPE_STRUCT) {
|
||||||
|
return error.InvalidSPIRV;
|
||||||
|
}
|
||||||
|
|
||||||
|
const member_count = ext.spvc_type_get_num_member_types(uniform_buffer_type);
|
||||||
|
|
||||||
|
if (member_count > uniform_block.uniforms.len) {
|
||||||
|
return error.UnsupportedSPIRV;
|
||||||
|
}
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_get_declared_struct_size(compiler, uniform_buffer_type, &uniform_block.size));
|
||||||
|
|
||||||
|
var uniform_blocks_used: u32 = 0;
|
||||||
|
|
||||||
|
while (uniform_blocks_used < member_count) : (uniform_blocks_used += 1) {
|
||||||
|
const member_type_id = ext.spvc_type_get_member_type(uniform_buffer_type, uniform_blocks_used);
|
||||||
|
const member_type = ext.spvc_compiler_get_type_handle(compiler, member_type_id);
|
||||||
|
|
||||||
|
uniform_block.uniforms[uniform_blocks_used] = .{
|
||||||
|
// .name = ext.spvc_compiler_get_member_name(compiler, ext.spvc_type_get_base_type_id(uniform_buffer_type), uniform_blocks_used),
|
||||||
|
|
||||||
|
.array_count = @intCast(try switch (ext.spvc_type_get_num_array_dimensions(member_type)) {
|
||||||
|
0 => 0,
|
||||||
|
|
||||||
|
1 => switch (ext.spvc_type_array_dimension_is_literal(member_type, 1) != 0) {
|
||||||
|
true => ext.spvc_type_get_array_dimension(member_type, 1),
|
||||||
|
false => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.type = try switch (ext.spvc_type_get_basetype(member_type)) {
|
||||||
|
ext.SPVC_BASETYPE_INT32 => switch (ext.spvc_type_get_vector_size(member_type)) {
|
||||||
|
1 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.INT else error.InvalidSPIRV,
|
||||||
|
2 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.INT2 else error.InvalidSPIRV,
|
||||||
|
3 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.INT3 else error.InvalidSPIRV,
|
||||||
|
4 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.INT4 else error.InvalidSPIRV,
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
ext.SPVC_BASETYPE_FP32 => switch (ext.spvc_type_get_vector_size(member_type)) {
|
||||||
|
1 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.FLOAT else error.InvalidSPIRV,
|
||||||
|
2 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.FLOAT2 else error.InvalidSPIRV,
|
||||||
|
3 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.FLOAT3 else error.InvalidSPIRV,
|
||||||
|
|
||||||
|
4 => switch (ext.spvc_type_get_columns(member_type)) {
|
||||||
|
1 => sokol.gfx.UniformType.FLOAT4,
|
||||||
|
4 => sokol.gfx.UniformType.MAT4,
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// uniform_block.uniforms[uniform_blocks_used].name = ext.spvc_compiler_get_member_name(compiler, ext.spvc_type_get_base_type_id(uniform_buffer_type), uniform_blocks_used);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_error(result: ext.spvc_result) Error!void {
|
||||||
|
return switch (result) {
|
||||||
|
ext.SPVC_SUCCESS => {},
|
||||||
|
ext.SPVC_ERROR_INVALID_SPIRV => error.InvalidSPIRV,
|
||||||
|
ext.SPVC_ERROR_UNSUPPORTED_SPIRV => error.UnsupportedSPIRV,
|
||||||
|
ext.SPVC_ERROR_OUT_OF_MEMORY => error.OutOfMemory,
|
||||||
|
ext.SPVC_ERROR_INVALID_ARGUMENT, ext.SPVC_ERROR_INT_MAX => unreachable,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue