renderer-mvp/post-processing #56
14
build.zig
14
build.zig
|
@ -194,17 +194,7 @@ pub fn build(b: *std.Build) !void {
|
|||
const vertex_binary_sub_path_utf8 = vertex_binary_sub_path.utf8();
|
||||
const fragment_binary_sub_path_utf8 = fragment_binary_sub_path.utf8();
|
||||
|
||||
const link_command = b.addSystemCommand(&.{
|
||||
"spirv-link",
|
||||
vertex_binary_sub_path_utf8,
|
||||
fragment_binary_sub_path_utf8,
|
||||
"-o",
|
||||
pending_shader.binary_sub_path.utf8(),
|
||||
});
|
||||
|
||||
exe.step.dependOn(&link_command.step);
|
||||
|
||||
link_command.step.dependOn(compile_vertex: {
|
||||
exe.step.dependOn(compile_vertex: {
|
||||
const compile_command = b.addSystemCommand(&.{
|
||||
"glslangValidator",
|
||||
"-V",
|
||||
|
@ -216,7 +206,7 @@ pub fn build(b: *std.Build) !void {
|
|||
break: compile_vertex &compile_command.step;
|
||||
});
|
||||
|
||||
link_command.step.dependOn(compile_fragment: {
|
||||
exe.step.dependOn(compile_fragment: {
|
||||
const compile_command = b.addSystemCommand(&.{
|
||||
"glslangValidator",
|
||||
"-V",
|
||||
|
|
|
@ -7,22 +7,19 @@ layout (location = 1) in vec2 uv;
|
|||
|
||||
layout (location = 0) out vec4 texel;
|
||||
|
||||
layout (binding = 0) readonly buffer Camera {
|
||||
mat4 projection_matrix;
|
||||
};
|
||||
|
||||
layout (binding = 1) readonly buffer Material {
|
||||
layout (binding = 0) readonly buffer Effect {
|
||||
float effect_magnitude;
|
||||
};
|
||||
|
||||
void main() {
|
||||
const vec2 red_channel_uv = uv + vec2(effect_magnitude, 0.0);
|
||||
const vec2 blue_channel_uv = uv + vec2(-effect_magnitude, 0.0);
|
||||
const vec4 original_texel = texture(sprite, uv);
|
||||
vec4 color1 = texture(sprite, uv) / 3.0;
|
||||
vec4 color2 = texture(sprite, uv + 0.002 * effect_magnitude) / 3.0;
|
||||
vec4 color3 = texture(sprite, uv - 0.002 * effect_magnitude) / 3.0;
|
||||
|
||||
texel = vec4(texture(sprite, red_channel_uv).r, original_texel.g, texture(sprite, blue_channel_uv).b, original_texel.a) * color;
|
||||
color1 *= 2.0;
|
||||
color2.g = 0.0;
|
||||
color2.b = 0.0;
|
||||
color3.r = 0.0;
|
||||
|
||||
if (texel.a == 0) {
|
||||
discard;
|
||||
}
|
||||
texel = color * (color1 + color2 + color3);
|
||||
}
|
||||
|
|
32
src/main.zig
32
src/main.zig
|
@ -4,10 +4,15 @@ const std = @import("std");
|
|||
|
||||
const ona = @import("ona");
|
||||
|
||||
const ChromaticAberration = extern struct {
|
||||
magnitude: f32,
|
||||
};
|
||||
|
||||
const Actors = struct {
|
||||
instances: coral.stack.Sequential(@Vector(2, f32)) = .{.allocator = coral.heap.allocator},
|
||||
body_texture: ona.gfx.Texture = .default,
|
||||
render_texture: ona.gfx.Texture = .default,
|
||||
ca_effect: ona.gfx.Effect = .default,
|
||||
};
|
||||
|
||||
const Player = struct {
|
||||
|
@ -37,6 +42,8 @@ fn load(config: ona.Write(ona.gfx.Config), actors: ona.Write(Actors), assets: on
|
|||
},
|
||||
});
|
||||
|
||||
actors.res.ca_effect = try assets.res.load_effect_file(coral.files.bundle, "./ca.frag.spv");
|
||||
|
||||
try actors.res.instances.push_grow(.{0, 0});
|
||||
}
|
||||
|
||||
|
@ -44,12 +51,9 @@ fn exit(actors: ona.Write(Actors)) void {
|
|||
actors.res.instances.deinit();
|
||||
}
|
||||
|
||||
fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors), config: ona.Read(ona.gfx.Config)) !void {
|
||||
try commands.set_target(.{
|
||||
.texture = actors.res.render_texture,
|
||||
.clear_color = .{0, 0, 0, 0},
|
||||
.clear_depth = 0,
|
||||
.clear_stencil = 0,
|
||||
fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors)) !void {
|
||||
try commands.set_effect(actors.res.ca_effect, ChromaticAberration{
|
||||
.magnitude = 15.0,
|
||||
});
|
||||
|
||||
for (actors.res.instances.values) |instance| {
|
||||
|
@ -63,22 +67,6 @@ fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors), config: ona.Rea
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
try commands.set_target(.{
|
||||
.clear_color = null,
|
||||
.clear_depth = null,
|
||||
.clear_stencil = null,
|
||||
});
|
||||
|
||||
try commands.draw_texture(.{
|
||||
.texture = actors.res.render_texture,
|
||||
|
||||
.transform = .{
|
||||
.origin = .{@floatFromInt(config.res.width / 2), @floatFromInt(config.res.height / 2)},
|
||||
.xbasis = .{@floatFromInt(config.res.width), 0},
|
||||
.ybasis = .{0, @floatFromInt(config.res.height)},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn update(player: ona.Read(Player), actors: ona.Write(Actors), mapping: ona.Read(ona.act.Mapping)) !void {
|
||||
|
|
|
@ -25,12 +25,11 @@ const std = @import("std");
|
|||
pub const Assets = struct {
|
||||
window: *ext.SDL_Window,
|
||||
texture_formats: coral.stack.Sequential(TextureFormat),
|
||||
staging_arena: std.heap.ArenaAllocator,
|
||||
frame_rendered: std.Thread.ResetEvent = .{},
|
||||
|
||||
pub const LoadError = std.mem.Allocator.Error;
|
||||
|
||||
pub const LoadFileError = LoadError || coral.files.AccessError || error {
|
||||
pub const LoadFileError = LoadError || coral.files.ReadAllError || error {
|
||||
FormatUnsupported,
|
||||
};
|
||||
|
||||
|
@ -41,7 +40,6 @@ pub const Assets = struct {
|
|||
|
||||
fn deinit(self: *Assets) void {
|
||||
rendering.enqueue_work(.shutdown);
|
||||
self.staging_arena.deinit();
|
||||
self.texture_formats.deinit();
|
||||
}
|
||||
|
||||
|
@ -64,12 +62,40 @@ pub const Assets = struct {
|
|||
try rendering.startup(window);
|
||||
|
||||
return .{
|
||||
.staging_arena = std.heap.ArenaAllocator.init(coral.heap.allocator),
|
||||
.texture_formats = .{.allocator = coral.heap.allocator},
|
||||
.window = window,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn load_effect_file(_: *Assets, storage: coral.files.Storage, path: []const u8) LoadFileError!Effect {
|
||||
if (!std.mem.endsWith(u8, path, ".spv")) {
|
||||
return error.FormatUnsupported;
|
||||
}
|
||||
|
||||
const fragment_file_stat = try storage.stat(path);
|
||||
const fragment_spirv = try coral.heap.allocator.alloc(u32, fragment_file_stat.size / @alignOf(u32));
|
||||
|
||||
defer {
|
||||
coral.heap.allocator.free(fragment_spirv);
|
||||
}
|
||||
|
||||
_ = try storage.read_all(path, std.mem.sliceAsBytes(fragment_spirv), .{});
|
||||
|
||||
var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Effect){};
|
||||
|
||||
rendering.enqueue_work(.{
|
||||
.load_effect = .{
|
||||
.desc = .{
|
||||
.fragment_spirv = fragment_spirv,
|
||||
},
|
||||
|
||||
.loaded = &loaded,
|
||||
},
|
||||
});
|
||||
|
||||
return loaded.get().*;
|
||||
}
|
||||
|
||||
pub fn load_texture(_: *Assets, desc: Texture.Desc) std.mem.Allocator.Error!Texture {
|
||||
var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Texture){};
|
||||
|
||||
|
@ -84,12 +110,10 @@ pub const Assets = struct {
|
|||
}
|
||||
|
||||
pub fn load_texture_file(self: *Assets, storage: coral.files.Storage, path: []const u8) LoadFileError!Texture {
|
||||
defer {
|
||||
const max_cache_size = 536870912;
|
||||
var arena = std.heap.ArenaAllocator.init(coral.heap.allocator);
|
||||
|
||||
if (!self.staging_arena.reset(.{.retain_with_limit = max_cache_size})) {
|
||||
std.log.warn("failed to retain staging arena size of {} bytes", .{max_cache_size});
|
||||
}
|
||||
defer {
|
||||
arena.deinit();
|
||||
}
|
||||
|
||||
for (self.texture_formats.values) |format| {
|
||||
|
@ -97,12 +121,26 @@ pub const Assets = struct {
|
|||
continue;
|
||||
}
|
||||
|
||||
return self.load_texture(try format.load_file(&self.staging_arena, storage, path));
|
||||
return self.load_texture(try format.load_file(&arena, storage, path));
|
||||
}
|
||||
|
||||
return error.FormatUnsupported;
|
||||
}
|
||||
|
||||
pub fn update_effect(_: *Assets, effect: Effect, properties: anytype) bool {
|
||||
var setted = coral.asyncio.Future(bool){};
|
||||
|
||||
rendering.enqueue_work(.{
|
||||
.update_effect = .{
|
||||
.properties = std.mem.asBytes(properties),
|
||||
.effect = effect,
|
||||
.setted = &setted,
|
||||
},
|
||||
});
|
||||
|
||||
return setted.get().*;
|
||||
}
|
||||
|
||||
pub const thread_restriction = .main;
|
||||
};
|
||||
|
||||
|
@ -137,6 +175,15 @@ pub const Commands = struct {
|
|||
try self.list.append(.{.draw_texture = command});
|
||||
}
|
||||
|
||||
pub fn set_effect(self: Commands, effect: handles.Effect, properties: anytype) std.mem.Allocator.Error!void {
|
||||
try self.list.append(.{
|
||||
.set_effect = .{
|
||||
.properties = std.mem.asBytes(&properties),
|
||||
.effect = effect,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_target(self: Commands, command: rendering.Command.SetTarget) std.mem.Allocator.Error!void {
|
||||
try self.list.append(.{.set_target = command});
|
||||
}
|
||||
|
@ -164,6 +211,8 @@ pub const Input = union (enum) {
|
|||
};
|
||||
};
|
||||
|
||||
pub const Effect = handles.Effect;
|
||||
|
||||
pub const Rect = struct {
|
||||
left: f32,
|
||||
top: f32,
|
||||
|
|
|
@ -2,6 +2,10 @@ const coral = @import("coral");
|
|||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Effect = Handle(struct {
|
||||
fragment_spirv: []const u32,
|
||||
});
|
||||
|
||||
fn Handle(comptime HandleDesc: type) type {
|
||||
return enum (u32) {
|
||||
default,
|
||||
|
|
|
@ -16,6 +16,7 @@ const std = @import("std");
|
|||
|
||||
pub const Command = union (enum) {
|
||||
draw_texture: DrawTexture,
|
||||
set_effect: SetEffect,
|
||||
set_target: SetTarget,
|
||||
|
||||
pub const DrawTexture = struct {
|
||||
|
@ -23,6 +24,11 @@ pub const Command = union (enum) {
|
|||
transform: lina.Transform2D,
|
||||
};
|
||||
|
||||
pub const SetEffect = struct {
|
||||
effect: handles.Effect,
|
||||
properties: []const coral.io.Byte,
|
||||
};
|
||||
|
||||
pub const SetTarget = struct {
|
||||
texture: ?handles.Texture = null,
|
||||
clear_color: ?lina.Color,
|
||||
|
@ -31,10 +37,16 @@ pub const Command = union (enum) {
|
|||
};
|
||||
|
||||
fn clone(self: Command, arena: *std.heap.ArenaAllocator) !Command {
|
||||
_ = arena;
|
||||
|
||||
return switch (self) {
|
||||
.draw_texture => |draw_texture| .{.draw_texture = draw_texture},
|
||||
|
||||
.set_effect => |set_effect| .{
|
||||
.set_effect = .{
|
||||
.properties = try arena.allocator().dupe(coral.io.Byte, set_effect.properties),
|
||||
.effect = set_effect.effect,
|
||||
},
|
||||
},
|
||||
|
||||
.set_target => |set_target| .{.set_target = set_target},
|
||||
};
|
||||
}
|
||||
|
@ -42,6 +54,7 @@ pub const Command = union (enum) {
|
|||
fn process(self: Command, pools: *Pools, frame: *Frame) !void {
|
||||
try switch (self) {
|
||||
.draw_texture => |draw_texture| frame.draw_texture(pools, draw_texture),
|
||||
.set_effect => |set_effect| frame.set_effect(pools, set_effect),
|
||||
.set_target => |set_target| frame.set_target(pools, set_target),
|
||||
};
|
||||
}
|
||||
|
@ -123,6 +136,7 @@ const Frame = struct {
|
|||
flushed_count: usize = 0,
|
||||
current_source_texture: handles.Texture = .default,
|
||||
current_target_texture: ?handles.Texture = null,
|
||||
current_effect: handles.Effect = .default,
|
||||
|
||||
const DrawTexture = extern struct {
|
||||
transform: lina.Transform2D,
|
||||
|
@ -175,29 +189,29 @@ const Frame = struct {
|
|||
.render => |render| {
|
||||
bindings.fs.images[0] = render.color_image;
|
||||
bindings.fs.samplers[0] = render.sampler;
|
||||
bindings.vs.images[0] = render.color_image;
|
||||
bindings.vs.samplers[0] = render.sampler;
|
||||
},
|
||||
|
||||
.static => |static| {
|
||||
bindings.fs.images[0] = static.image;
|
||||
bindings.fs.samplers[0] = static.sampler;
|
||||
bindings.vs.images[0] = static.image;
|
||||
bindings.vs.samplers[0] = static.sampler;
|
||||
},
|
||||
}
|
||||
|
||||
if (self.current_target_texture) |target_texture| {
|
||||
const target_view_buffer = pools.textures.get(@intFromEnum(target_texture)).?.render.view_buffer;
|
||||
|
||||
bindings.fs.storage_buffers[0] = target_view_buffer;
|
||||
bindings.vs.storage_buffers[0] = target_view_buffer;
|
||||
} else {
|
||||
bindings.fs.storage_buffers[0] = view_buffer;
|
||||
bindings.vs.storage_buffers[0] = view_buffer;
|
||||
}
|
||||
|
||||
sokol.gfx.applyPipeline(texture_batching_pipeline);
|
||||
const effect = pools.effects.get(@intFromEnum(self.current_effect)).?;
|
||||
|
||||
sokol.gfx.applyPipeline(effect.pipeline);
|
||||
|
||||
if (effect.has_properties_buffer) |properties_buffer| {
|
||||
bindings.fs.storage_buffers[0] = properties_buffer;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const buffer_index = self.flushed_count / batches_per_buffer;
|
||||
|
@ -217,11 +231,29 @@ const Frame = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_effect(self: *Frame, pools: *Pools, command: Command.SetEffect) void {
|
||||
if (command.effect != self.current_effect) {
|
||||
self.flush(pools);
|
||||
}
|
||||
|
||||
self.current_effect = command.effect;
|
||||
|
||||
if (command.properties.len != 0) {
|
||||
if (pools.effects.get(@intFromEnum(self.current_effect)).?.has_properties_buffer) |properties_buffer| {
|
||||
sokol.gfx.updateBuffer(properties_buffer, sokol.gfx.asRange(command.properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_target(self: *Frame, pools: *Pools, command: Command.SetTarget) void {
|
||||
sokol.gfx.endPass();
|
||||
|
||||
var pass = sokol.gfx.Pass{
|
||||
.action = .{.stencil = .{.load_action = .CLEAR}},
|
||||
.action = .{
|
||||
.stencil = .{
|
||||
.load_action = .CLEAR,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (command.clear_color) |color| {
|
||||
|
@ -265,15 +297,24 @@ const Frame = struct {
|
|||
var texture_batch_buffers = coral.stack.Sequential(sokol.gfx.Buffer){.allocator = coral.heap.allocator};
|
||||
|
||||
const batches_per_buffer = 512;
|
||||
|
||||
var texture_batching_pipeline: sokol.gfx.Pipeline = undefined;
|
||||
};
|
||||
|
||||
const Pools = struct {
|
||||
effects: EffectPool,
|
||||
textures: TexturePool,
|
||||
|
||||
const EffectPool = coral.Pool(resources.Effect);
|
||||
|
||||
const TexturePool = coral.Pool(resources.Texture);
|
||||
|
||||
fn create_effect(self: *Pools, desc: handles.Effect.Desc) !handles.Effect {
|
||||
var effect = try resources.Effect.init(desc);
|
||||
|
||||
errdefer effect.deinit();
|
||||
|
||||
return @enumFromInt(try self.effects.insert(effect));
|
||||
}
|
||||
|
||||
fn create_texture(self: *Pools, desc: handles.Texture.Desc) !handles.Texture {
|
||||
var texture = try resources.Texture.init(desc);
|
||||
|
||||
|
@ -292,12 +333,28 @@ const Pools = struct {
|
|||
self.textures.deinit();
|
||||
}
|
||||
|
||||
fn destroy_texture(self: *Pools, texture_key: handles.Texture) bool {
|
||||
switch (texture_key) {
|
||||
fn destroy_effect(self: *Pools, handle: handles.Effect) bool {
|
||||
switch (handle) {
|
||||
.default => {},
|
||||
|
||||
else => {
|
||||
var texture = self.textures.remove(@intFromEnum(texture_key)) orelse {
|
||||
var effect = self.effects.remove(@intFromEnum(handle)) orelse {
|
||||
return false;
|
||||
};
|
||||
|
||||
effect.deinit();
|
||||
},
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn destroy_texture(self: *Pools, handle: handles.Texture) bool {
|
||||
switch (handle) {
|
||||
.default => {},
|
||||
|
||||
else => {
|
||||
var texture = self.textures.remove(@intFromEnum(handle)) orelse {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -310,10 +367,17 @@ const Pools = struct {
|
|||
|
||||
fn init(allocator: std.mem.Allocator) !Pools {
|
||||
var pools = Pools{
|
||||
.effects = EffectPool.init(allocator),
|
||||
.textures = TexturePool.init(allocator),
|
||||
};
|
||||
|
||||
errdefer pools.deinit();
|
||||
errdefer {
|
||||
pools.deinit();
|
||||
}
|
||||
|
||||
_ = try pools.create_effect(.{
|
||||
.fragment_spirv = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
||||
});
|
||||
|
||||
const default_texture_data = [_]u32{
|
||||
0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF,
|
||||
|
@ -343,16 +407,23 @@ const Pools = struct {
|
|||
|
||||
pub const Work = union (enum) {
|
||||
create_commands: CreateCommandsWork,
|
||||
load_effect: LoadEffectWork,
|
||||
load_texture: LoadTextureWork,
|
||||
render_frame: RenderFrameWork,
|
||||
rotate_commands: RotateCommandsWork,
|
||||
shutdown,
|
||||
unload_effect: UnloadEffectWork,
|
||||
unload_texture: UnloadTextureWork,
|
||||
|
||||
pub const CreateCommandsWork = struct {
|
||||
created: *coral.asyncio.Future(std.mem.Allocator.Error!*Commands),
|
||||
};
|
||||
|
||||
pub const LoadEffectWork = struct {
|
||||
desc: handles.Effect.Desc,
|
||||
loaded: *coral.asyncio.Future(std.mem.Allocator.Error!handles.Effect),
|
||||
};
|
||||
|
||||
pub const LoadTextureWork = struct {
|
||||
desc: handles.Texture.Desc,
|
||||
loaded: *coral.asyncio.Future(std.mem.Allocator.Error!handles.Texture),
|
||||
|
@ -369,6 +440,10 @@ pub const Work = union (enum) {
|
|||
finished: *std.Thread.ResetEvent,
|
||||
};
|
||||
|
||||
pub const UnloadEffectWork = struct {
|
||||
handle: handles.Effect,
|
||||
};
|
||||
|
||||
pub const UnloadTextureWork = struct {
|
||||
handle: handles.Texture,
|
||||
};
|
||||
|
@ -395,62 +470,6 @@ const vertex_indices = .{
|
|||
.instance = 1,
|
||||
};
|
||||
|
||||
const vertex_layout_state = sokol.gfx.VertexLayoutState{
|
||||
.attrs = get: {
|
||||
var attrs = [_]sokol.gfx.VertexAttrState{.{}} ** 16;
|
||||
|
||||
attrs[0] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = vertex_indices.mesh,
|
||||
};
|
||||
|
||||
attrs[1] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = vertex_indices.mesh,
|
||||
};
|
||||
|
||||
attrs[2] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = vertex_indices.instance,
|
||||
};
|
||||
|
||||
attrs[3] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = vertex_indices.instance,
|
||||
};
|
||||
|
||||
attrs[4] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = vertex_indices.instance,
|
||||
};
|
||||
|
||||
attrs[5] = .{
|
||||
.format = .UBYTE4N,
|
||||
.buffer_index = vertex_indices.instance,
|
||||
};
|
||||
|
||||
attrs[6] = .{
|
||||
.format = .FLOAT,
|
||||
.buffer_index = vertex_indices.instance,
|
||||
};
|
||||
|
||||
attrs[7] = .{
|
||||
.format = .FLOAT4,
|
||||
.buffer_index = vertex_indices.instance,
|
||||
};
|
||||
|
||||
break: get attrs;
|
||||
},
|
||||
|
||||
.buffers = get: {
|
||||
var vertex_buffer_layouts = [_]sokol.gfx.VertexBufferLayoutState{.{}} ** 8;
|
||||
|
||||
vertex_buffer_layouts[vertex_indices.instance].step_func = .PER_INSTANCE;
|
||||
|
||||
break: get vertex_buffer_layouts;
|
||||
},
|
||||
};
|
||||
|
||||
fn run(window: *ext.SDL_Window) !void {
|
||||
const context = configure_and_create: {
|
||||
var result = @as(c_int, 0);
|
||||
|
@ -530,21 +549,6 @@ fn run(window: *ext.SDL_Window) !void {
|
|||
.usage = .IMMUTABLE,
|
||||
});
|
||||
|
||||
const shader_spirv = @embedFile("./shaders/2d_default.spv");
|
||||
var spirv_unit = try spirv.Unit.init(coral.heap.allocator);
|
||||
|
||||
defer spirv_unit.deinit();
|
||||
|
||||
try spirv_unit.compile(shader_spirv, .vertex);
|
||||
try spirv_unit.compile(shader_spirv, .fragment);
|
||||
|
||||
Frame.texture_batching_pipeline = sokol.gfx.makePipeline(.{
|
||||
.label = "2D drawing pipeline",
|
||||
.layout = vertex_layout_state,
|
||||
.shader = sokol.gfx.makeShader(spirv_unit.shader_desc),
|
||||
.index_type = .UINT16,
|
||||
});
|
||||
|
||||
var has_commands_head: ?*Commands = null;
|
||||
var has_commands_tail: ?*Commands = null;
|
||||
|
||||
|
@ -575,6 +579,14 @@ fn run(window: *ext.SDL_Window) !void {
|
|||
}
|
||||
},
|
||||
|
||||
.load_effect => |load| {
|
||||
const effect = try pools.create_effect(load.desc);
|
||||
|
||||
if (!load.loaded.resolve(effect)) {
|
||||
std.debug.assert(pools.destroy_effect(effect));
|
||||
}
|
||||
},
|
||||
|
||||
.load_texture => |load| {
|
||||
const texture = try pools.create_texture(load.desc);
|
||||
|
||||
|
@ -650,6 +662,12 @@ fn run(window: *ext.SDL_Window) !void {
|
|||
break;
|
||||
},
|
||||
|
||||
.unload_effect => |unload| {
|
||||
if (!pools.destroy_effect(unload.handle)) {
|
||||
@panic("Attempt to unload a non-existent effect");
|
||||
}
|
||||
},
|
||||
|
||||
.unload_texture => |unload| {
|
||||
if (!pools.destroy_texture(unload.handle)) {
|
||||
@panic("Attempt to unload a non-existent texture");
|
||||
|
|
|
@ -10,6 +10,140 @@ const spirv = @import("./spirv.zig");
|
|||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Effect = struct {
|
||||
shader: sokol.gfx.Shader,
|
||||
pipeline: sokol.gfx.Pipeline,
|
||||
has_properties_buffer: ?sokol.gfx.Buffer = null,
|
||||
|
||||
pub fn deinit(self: *Effect) void {
|
||||
sokol.gfx.destroyPipeline(self.pipeline);
|
||||
sokol.gfx.destroyShader(self.shader);
|
||||
|
||||
if (self.has_properties_buffer) |parameters_buffer| {
|
||||
sokol.gfx.destroyBuffer(parameters_buffer);
|
||||
}
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn init(desc: handles.Effect.Desc) !Effect {
|
||||
var spirv_unit = try spirv.Unit.init(coral.heap.allocator);
|
||||
|
||||
defer spirv_unit.deinit();
|
||||
|
||||
try spirv_unit.compile(&spirv.to_ops(@embedFile("./shaders/2d_default.vert.spv")), .vertex);
|
||||
try spirv_unit.compile(desc.fragment_spirv, .fragment);
|
||||
|
||||
const shader = sokol.gfx.makeShader(spirv_unit.shader_desc());
|
||||
|
||||
const pipeline = sokol.gfx.makePipeline(pipeline_desc: {
|
||||
var pipeline_desc = sokol.gfx.PipelineDesc{
|
||||
.label = "Effect pipeline",
|
||||
.layout = vertex_layout_state,
|
||||
.shader = shader,
|
||||
.index_type = .UINT16,
|
||||
.blend_color = .{.r = 1.0, .g = 1.0, .b = 1.0, .a = 1.0},
|
||||
};
|
||||
|
||||
pipeline_desc.colors[0] = .{
|
||||
.write_mask = .RGBA,
|
||||
|
||||
.blend = .{
|
||||
.enabled = true,
|
||||
.src_factor_rgb = .SRC_ALPHA,
|
||||
.dst_factor_rgb = .ONE_MINUS_SRC_ALPHA,
|
||||
},
|
||||
};
|
||||
|
||||
break: pipeline_desc pipeline_desc;
|
||||
});
|
||||
|
||||
errdefer {
|
||||
sokol.gfx.destroyPipeline(pipeline);
|
||||
sokol.gfx.destroyShader(shader);
|
||||
}
|
||||
|
||||
if (spirv_unit.shader_stage(.fragment).has_storage_buffer[0]) |storage_buffer| {
|
||||
const properties_buffer = sokol.gfx.makeBuffer(.{
|
||||
.size = storage_buffer.minimum_size,
|
||||
.type = .STORAGEBUFFER,
|
||||
.usage = .STREAM,
|
||||
});
|
||||
|
||||
errdefer {
|
||||
sokol.gfx.destroyBuffer(properties_buffer);
|
||||
}
|
||||
|
||||
return .{
|
||||
.shader = shader,
|
||||
.pipeline = pipeline,
|
||||
.has_properties_buffer = properties_buffer,
|
||||
};
|
||||
} else {
|
||||
return .{
|
||||
.shader = shader,
|
||||
.pipeline = pipeline,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const vertex_layout_state = sokol.gfx.VertexLayoutState{
|
||||
.attrs = get: {
|
||||
var attrs = [_]sokol.gfx.VertexAttrState{.{}} ** 16;
|
||||
|
||||
attrs[0] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = 0,
|
||||
};
|
||||
|
||||
attrs[1] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = 0,
|
||||
};
|
||||
|
||||
attrs[2] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = 1,
|
||||
};
|
||||
|
||||
attrs[3] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = 1,
|
||||
};
|
||||
|
||||
attrs[4] = .{
|
||||
.format = .FLOAT2,
|
||||
.buffer_index = 1,
|
||||
};
|
||||
|
||||
attrs[5] = .{
|
||||
.format = .UBYTE4N,
|
||||
.buffer_index = 1,
|
||||
};
|
||||
|
||||
attrs[6] = .{
|
||||
.format = .FLOAT,
|
||||
.buffer_index = 1,
|
||||
};
|
||||
|
||||
attrs[7] = .{
|
||||
.format = .FLOAT4,
|
||||
.buffer_index = 1,
|
||||
};
|
||||
|
||||
break: get attrs;
|
||||
},
|
||||
|
||||
.buffers = get: {
|
||||
var vertex_buffer_layouts = [_]sokol.gfx.VertexBufferLayoutState{.{}} ** 8;
|
||||
|
||||
vertex_buffer_layouts[1].step_func = .PER_INSTANCE;
|
||||
|
||||
break: get vertex_buffer_layouts;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
pub const Texture = union (enum) {
|
||||
render: Render,
|
||||
static: Static,
|
||||
|
|
|
@ -7,10 +7,6 @@ layout (location = 1) in vec2 uv;
|
|||
|
||||
layout (location = 0) out vec4 texel;
|
||||
|
||||
layout (binding = 0) readonly buffer View {
|
||||
mat4 projection_matrix;
|
||||
};
|
||||
|
||||
void main() {
|
||||
texel = texture(sprite, uv) * color;
|
||||
|
||||
|
|
|
@ -15,22 +15,225 @@ pub const Error = std.mem.Allocator.Error || error {
|
|||
UnsupportedBackend,
|
||||
};
|
||||
|
||||
pub const Unit = struct {
|
||||
arena: std.heap.ArenaAllocator,
|
||||
context: ext.spvc_context,
|
||||
shader_desc: sokol.gfx.ShaderDesc,
|
||||
attrs_used: u32 = 0,
|
||||
pub const Shader = struct {
|
||||
decompiled_source: [:0]const u8 = "",
|
||||
has_storage_buffer: [max_storage_buffers]?StorageBuffer = [_]?StorageBuffer{null} ** max_storage_buffers,
|
||||
has_image: [max_images]?Image = [_]?Image{null} ** max_images,
|
||||
has_sampler: [max_samplers]?Sampler = [_]?Sampler{null} ** max_samplers,
|
||||
has_image_sampler_pair: [max_image_sampler_pairs]?ImageSamplerPair = [_]?ImageSamplerPair{null} ** max_image_sampler_pairs,
|
||||
|
||||
pub fn compile(self: *Unit, spirv_data: []const u8, stage: Stage) Error!void {
|
||||
if ((spirv_data.len % @alignOf(u32)) != 0) {
|
||||
pub const Image = struct {
|
||||
is_multisampled: bool,
|
||||
};
|
||||
|
||||
pub const ImageSamplerPair = struct {
|
||||
image_slot: usize,
|
||||
sampler_slot: usize,
|
||||
name: [:0]const u8,
|
||||
};
|
||||
|
||||
pub const Sampler = enum {
|
||||
filtering,
|
||||
non_filtering,
|
||||
comparison,
|
||||
};
|
||||
|
||||
pub const StorageBuffer = struct {
|
||||
minimum_size: usize,
|
||||
is_readonly: bool,
|
||||
};
|
||||
|
||||
pub const max_images = 12;
|
||||
|
||||
pub const max_image_sampler_pairs = 12;
|
||||
|
||||
pub const max_samplers = 8;
|
||||
|
||||
pub const max_storage_buffers = 8;
|
||||
|
||||
fn reflect_image_samplers(self: *Shader, _: Stage, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void {
|
||||
var 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(&sampled_images.ptr),
|
||||
&sampled_images.len,
|
||||
));
|
||||
|
||||
if (sampled_images.len > max_image_sampler_pairs) {
|
||||
return error.UnsupportedSPIRV;
|
||||
}
|
||||
|
||||
for (0 .. sampled_images.len, sampled_images) |i, sampled_image| {
|
||||
const sampled_image_type = ext.spvc_compiler_get_type_handle(compiler, sampled_image.type_id);
|
||||
|
||||
if (ext.spvc_type_get_basetype(sampled_image_type) != ext.SPVC_BASETYPE_SAMPLED_IMAGE) {
|
||||
return error.InvalidSPIRV;
|
||||
}
|
||||
|
||||
const spirv_ops: []const u32 = @alignCast(std.mem.bytesAsSlice(u32, spirv_data));
|
||||
try switch (ext.spvc_type_get_image_dimension(sampled_image_type)) {
|
||||
ext.SpvDim2D => {},
|
||||
else => error.InvalidSPIRV,
|
||||
};
|
||||
|
||||
const execution_model, const stage_desc = switch (stage) {
|
||||
.vertex => .{ext.SpvExecutionModelVertex, &self.shader_desc.vs},
|
||||
.fragment => .{ext.SpvExecutionModelFragment, &self.shader_desc.fs},
|
||||
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 => {},
|
||||
else => error.InvalidSPIRV,
|
||||
};
|
||||
|
||||
self.has_image[i] = .{
|
||||
.is_multisampled = ext.spvc_type_get_image_multisampled(sampled_image_type) != 0,
|
||||
};
|
||||
|
||||
self.has_sampler[i] = .filtering;
|
||||
|
||||
self.has_image_sampler_pair[i] = .{
|
||||
.name = std.mem.span(ext.spvc_compiler_get_name(compiler, sampled_image.id)),
|
||||
.image_slot = @intCast(i),
|
||||
.sampler_slot = @intCast(i),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn reflect_storage_buffers(self: *Shader, stage: Stage, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void {
|
||||
var storage_buffers: []const ext.spvc_reflected_resource = &.{};
|
||||
|
||||
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
||||
resources,
|
||||
ext.SPVC_RESOURCE_TYPE_STORAGE_BUFFER,
|
||||
@ptrCast(&storage_buffers.ptr),
|
||||
&storage_buffers.len,
|
||||
));
|
||||
|
||||
const binding_offset: c_uint = if (stage == .fragment) max_storage_buffers else 0;
|
||||
|
||||
for (storage_buffers) |storage_buffer| {
|
||||
const binding = ext.spvc_compiler_get_decoration(compiler, storage_buffer.id, ext.SpvDecorationBinding);
|
||||
|
||||
if (binding >= max_storage_buffers) {
|
||||
return error.InvalidSPIRV;
|
||||
}
|
||||
|
||||
ext.spvc_compiler_set_decoration(compiler, storage_buffer.id, ext.SpvDecorationBinding, binding_offset + binding);
|
||||
|
||||
var block_decorations: []const ext.SpvDecoration = &.{};
|
||||
|
||||
try to_error(ext.spvc_compiler_get_buffer_block_decorations(
|
||||
compiler,
|
||||
storage_buffer.id,
|
||||
@ptrCast(&block_decorations.ptr),
|
||||
&block_decorations.len,
|
||||
));
|
||||
|
||||
var minimum_size: usize = 0;
|
||||
|
||||
try to_error(ext.spvc_compiler_get_declared_struct_size(compiler, ext.spvc_compiler_get_type_handle(compiler, storage_buffer.base_type_id), &minimum_size));
|
||||
|
||||
self.has_storage_buffer[binding] = .{
|
||||
.is_readonly = std.mem.indexOfScalar(ext.SpvDecoration, block_decorations, ext.SpvDecorationNonWritable) != null,
|
||||
.minimum_size = minimum_size,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn reflect_uniform_buffers(self: *Shader, _: Stage, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void {
|
||||
var 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(&uniform_buffers.ptr),
|
||||
&uniform_buffers.len,
|
||||
));
|
||||
|
||||
if (uniform_buffers.len != 0) {
|
||||
return error.InvalidSPIRV;
|
||||
}
|
||||
|
||||
// TODO: Support for older APIs?
|
||||
_ = self;
|
||||
_ = compiler;
|
||||
}
|
||||
|
||||
pub fn stage_desc(self: Shader) sokol.gfx.ShaderStageDesc {
|
||||
var stage = sokol.gfx.ShaderStageDesc{
|
||||
.entry = "main",
|
||||
.source = self.decompiled_source,
|
||||
};
|
||||
|
||||
for (0 .. max_storage_buffers) |slot| {
|
||||
const storage_buffer = &(self.has_storage_buffer[slot] orelse {
|
||||
continue;
|
||||
});
|
||||
|
||||
stage.storage_buffers[slot] = .{
|
||||
.readonly = storage_buffer.is_readonly,
|
||||
.used = true,
|
||||
};
|
||||
}
|
||||
|
||||
for (0 .. max_images) |slot| {
|
||||
const image = &(self.has_image[slot] orelse {
|
||||
continue;
|
||||
});
|
||||
|
||||
stage.images[slot] = .{
|
||||
.multisampled = image.is_multisampled,
|
||||
.image_type = ._2D,
|
||||
.sample_type = .FLOAT,
|
||||
.used = true,
|
||||
};
|
||||
}
|
||||
|
||||
for (0 .. max_samplers) |slot| {
|
||||
const sampler = &(self.has_sampler[slot] orelse {
|
||||
continue;
|
||||
});
|
||||
|
||||
stage.samplers[slot] = .{
|
||||
.sampler_type = switch (sampler.*) {
|
||||
.filtering => .FILTERING,
|
||||
.non_filtering => .NONFILTERING,
|
||||
.comparison => .COMPARISON,
|
||||
},
|
||||
|
||||
.used = true,
|
||||
};
|
||||
}
|
||||
|
||||
for (0 .. max_image_sampler_pairs) |slot| {
|
||||
const image_sampler_pair = &(self.has_image_sampler_pair[slot] orelse {
|
||||
continue;
|
||||
});
|
||||
|
||||
stage.image_sampler_pairs[slot] = .{
|
||||
.glsl_name = image_sampler_pair.name,
|
||||
.image_slot = @intCast(image_sampler_pair.image_slot),
|
||||
.sampler_slot = @intCast(image_sampler_pair.sampler_slot),
|
||||
.used = true,
|
||||
};
|
||||
}
|
||||
|
||||
return stage;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Stage = enum {
|
||||
vertex,
|
||||
fragment,
|
||||
};
|
||||
|
||||
pub const Unit = struct {
|
||||
arena: std.heap.ArenaAllocator,
|
||||
context: ext.spvc_context,
|
||||
attrs_used: u32 = 0,
|
||||
stages: [std.enums.values(Stage).len]Shader = .{.{}, .{}},
|
||||
|
||||
pub fn compile(self: *Unit, spirv_ops: []const u32, stage: Stage) Error!void {
|
||||
const execution_model, const shader = switch (stage) {
|
||||
.vertex => .{ext.SpvExecutionModelVertex, &self.stages[0]},
|
||||
.fragment => .{ext.SpvExecutionModelFragment, &self.stages[1]},
|
||||
};
|
||||
|
||||
const Backend = struct {
|
||||
|
@ -73,8 +276,8 @@ pub const Unit = struct {
|
|||
|
||||
for (combined_image_samplers) |combined_image_sampler| {
|
||||
const name = try coral.utf8.alloc_formatted(arena_allocator, "{image_name}_{sampler_name}", .{
|
||||
.image_name = std.mem.span(@as([*:0]const u8, ext.spvc_compiler_get_name(compiler, combined_image_sampler.image_id))),
|
||||
.sampler_name = std.mem.span(@as([*:0]const u8, ext.spvc_compiler_get_name(compiler, combined_image_sampler.sampler_id))),
|
||||
.image_name = std.mem.span(ext.spvc_compiler_get_name(compiler, combined_image_sampler.image_id)),
|
||||
.sampler_name = std.mem.span(ext.spvc_compiler_get_name(compiler, combined_image_sampler.sampler_id)),
|
||||
});
|
||||
|
||||
ext.spvc_compiler_set_name(compiler, combined_image_sampler.combined_id, name);
|
||||
|
@ -86,7 +289,7 @@ pub const Unit = struct {
|
|||
break: parse_and_configure compiler;
|
||||
};
|
||||
|
||||
try to_error(ext.spvc_compiler_set_entry_point(compiler, stage_desc.entry, @intCast(execution_model)));
|
||||
try to_error(ext.spvc_compiler_set_entry_point(compiler, "main", @intCast(execution_model)));
|
||||
|
||||
const resources = create: {
|
||||
var resources: ext.spvc_resources = null;
|
||||
|
@ -96,27 +299,29 @@ pub const Unit = struct {
|
|||
break: create resources;
|
||||
};
|
||||
|
||||
try reflect_uniform_buffers(compiler, resources, stage_desc);
|
||||
try reflect_storage_buffers(compiler, resources, stage_desc);
|
||||
try reflect_image_samplers(compiler, resources, stage_desc);
|
||||
try shader.reflect_uniform_buffers(stage, compiler, resources);
|
||||
try shader.reflect_storage_buffers(stage, compiler, resources);
|
||||
try shader.reflect_image_samplers(stage, compiler, resources);
|
||||
|
||||
try to_error(ext.spvc_compiler_install_compiler_options(compiler, create: {
|
||||
var options: ext.spvc_compiler_options = null;
|
||||
var compiler_options: ext.spvc_compiler_options = null;
|
||||
|
||||
try to_error(ext.spvc_compiler_create_compiler_options(compiler, &options));
|
||||
try to_error(ext.spvc_compiler_create_compiler_options(compiler, &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));
|
||||
try to_error(ext.spvc_compiler_options_set_uint(compiler_options, entry, value));
|
||||
}
|
||||
|
||||
break: create options;
|
||||
break: create compiler_options;
|
||||
}));
|
||||
|
||||
try to_error(ext.spvc_compiler_compile(compiler, @ptrCast(&stage_desc.source)));
|
||||
try to_error(ext.spvc_compiler_compile(compiler, @ptrCast(&shader.decompiled_source.ptr)));
|
||||
|
||||
std.log.info("{s}", .{stage_desc.source});
|
||||
shader.decompiled_source.len = std.mem.span(shader.decompiled_source.ptr).len;
|
||||
|
||||
std.log.info("{s}", .{shader.decompiled_source});
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Unit) void {
|
||||
|
@ -140,18 +345,19 @@ pub const Unit = struct {
|
|||
return .{
|
||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||
.context = context,
|
||||
|
||||
.shader_desc = .{
|
||||
.vs = .{.entry = "main"},
|
||||
.fs = .{.entry = "main"},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Stage = enum {
|
||||
fragment,
|
||||
vertex,
|
||||
pub fn shader_desc(self: Unit) sokol.gfx.ShaderDesc {
|
||||
return .{
|
||||
.vs = self.shader_stage(.vertex).stage_desc(),
|
||||
.fs = self.shader_stage(.fragment).stage_desc(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn shader_stage(self: *const Unit, stage: Stage) *const Shader {
|
||||
return &self.stages[@intFromEnum(stage)];
|
||||
}
|
||||
};
|
||||
|
||||
fn log_context_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callconv(.C) void {
|
||||
|
@ -159,109 +365,6 @@ fn log_context_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callco
|
|||
std.log.err("{s}", .{error_message});
|
||||
}
|
||||
|
||||
fn reflect_image_samplers(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage_desc: *sokol.gfx.ShaderStageDesc) Error!void {
|
||||
var 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(&sampled_images.ptr),
|
||||
&sampled_images.len,
|
||||
));
|
||||
|
||||
if (sampled_images.len > stage_desc.image_sampler_pairs.len) {
|
||||
return error.UnsupportedSPIRV;
|
||||
}
|
||||
|
||||
for (0 .. sampled_images.len, sampled_images) |i, sampled_image| {
|
||||
const sampled_image_type = ext.spvc_compiler_get_type_handle(compiler, sampled_image.type_id);
|
||||
|
||||
if (ext.spvc_type_get_basetype(sampled_image_type) != ext.SPVC_BASETYPE_SAMPLED_IMAGE) {
|
||||
return error.InvalidSPIRV;
|
||||
}
|
||||
|
||||
stage_desc.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_desc.samplers[i] = .{
|
||||
.sampler_type = .DEFAULT,
|
||||
.used = true,
|
||||
};
|
||||
|
||||
stage_desc.image_sampler_pairs[i] = .{
|
||||
.glsl_name = ext.spvc_compiler_get_name(compiler, sampled_image.id),
|
||||
.image_slot = @intCast(i),
|
||||
.sampler_slot = @intCast(i),
|
||||
.used = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn reflect_storage_buffers(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage_desc: *sokol.gfx.ShaderStageDesc) Error!void {
|
||||
var storage_buffers: []const ext.spvc_reflected_resource = &.{};
|
||||
|
||||
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
||||
resources,
|
||||
ext.SPVC_RESOURCE_TYPE_STORAGE_BUFFER,
|
||||
@ptrCast(&storage_buffers.ptr),
|
||||
&storage_buffers.len,
|
||||
));
|
||||
|
||||
for (storage_buffers) |storage_buffer| {
|
||||
const binding = ext.spvc_compiler_get_decoration(compiler, storage_buffer.id, ext.SpvDecorationBinding);
|
||||
|
||||
if (binding >= stage_desc.storage_buffers.len) {
|
||||
return error.InvalidSPIRV;
|
||||
}
|
||||
|
||||
var block_decorations: []const ext.SpvDecoration = &.{};
|
||||
|
||||
try to_error(ext.spvc_compiler_get_buffer_block_decorations(
|
||||
compiler,
|
||||
storage_buffer.id,
|
||||
@ptrCast(&block_decorations.ptr),
|
||||
&block_decorations.len,
|
||||
));
|
||||
|
||||
stage_desc.storage_buffers[binding] = .{
|
||||
.readonly = std.mem.indexOfScalar(ext.SpvDecoration, block_decorations, ext.SpvDecorationNonWritable) != null,
|
||||
.used = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn reflect_uniform_buffers(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage_desc: *sokol.gfx.ShaderStageDesc) Error!void {
|
||||
var 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(&uniform_buffers.ptr),
|
||||
&uniform_buffers.len,
|
||||
));
|
||||
|
||||
if (uniform_buffers.len != 0) {
|
||||
return error.InvalidSPIRV;
|
||||
}
|
||||
|
||||
// TODO: Support for older APIs?
|
||||
_ = stage_desc;
|
||||
_ = compiler;
|
||||
}
|
||||
|
||||
fn to_error(result: ext.spvc_result) Error!void {
|
||||
return switch (result) {
|
||||
ext.SPVC_SUCCESS => {},
|
||||
|
@ -272,3 +375,11 @@ fn to_error(result: ext.spvc_result) Error!void {
|
|||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn to_ops(raw: anytype) [raw.len / @alignOf(u32)]u32 {
|
||||
var ops: [raw.len / @alignOf(u32)]u32 = undefined;
|
||||
|
||||
@memcpy(std.mem.sliceAsBytes(&ops), raw);
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue