renderer-mvp/post-processing #56
|
@ -7,7 +7,7 @@ layout (location = 1) in vec2 uv;
|
||||||
|
|
||||||
layout (location = 0) out vec4 texel;
|
layout (location = 0) out vec4 texel;
|
||||||
|
|
||||||
layout (binding = 0) readonly buffer Effect {
|
layout (binding = 0) uniform Effect {
|
||||||
float effect_magnitude;
|
float effect_magnitude;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ const std = @import("std");
|
||||||
const ona = @import("ona");
|
const ona = @import("ona");
|
||||||
|
|
||||||
const ChromaticAberration = extern struct {
|
const ChromaticAberration = extern struct {
|
||||||
magnitude: f32,
|
effect_magnitude: f32,
|
||||||
|
padding: [12]u8 = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Actors = struct {
|
const Actors = struct {
|
||||||
|
@ -53,7 +54,7 @@ fn exit(actors: ona.Write(Actors)) void {
|
||||||
|
|
||||||
fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors)) !void {
|
fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors)) !void {
|
||||||
try commands.set_effect(actors.res.ca_effect, ChromaticAberration{
|
try commands.set_effect(actors.res.ca_effect, ChromaticAberration{
|
||||||
.magnitude = 15.0,
|
.effect_magnitude = 15.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (actors.res.instances.values) |instance| {
|
for (actors.res.instances.values) |instance| {
|
||||||
|
|
|
@ -73,20 +73,22 @@ pub const Assets = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const fragment_file_stat = try storage.stat(path);
|
const fragment_file_stat = try storage.stat(path);
|
||||||
const fragment_spirv = try coral.heap.allocator.alloc(u32, fragment_file_stat.size / @alignOf(u32));
|
const fragment_spirv_ops = try coral.heap.allocator.alloc(u32, fragment_file_stat.size / @alignOf(u32));
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
coral.heap.allocator.free(fragment_spirv);
|
coral.heap.allocator.free(fragment_spirv_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = try storage.read_all(path, std.mem.sliceAsBytes(fragment_spirv), .{});
|
const bytes_read = try storage.read_all(path, std.mem.sliceAsBytes(fragment_spirv_ops), .{});
|
||||||
|
|
||||||
|
std.debug.assert(bytes_read.len == fragment_file_stat.size);
|
||||||
|
|
||||||
var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Effect){};
|
var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Effect){};
|
||||||
|
|
||||||
rendering.enqueue_work(.{
|
rendering.enqueue_work(.{
|
||||||
.load_effect = .{
|
.load_effect = .{
|
||||||
.desc = .{
|
.desc = .{
|
||||||
.fragment_spirv = fragment_spirv,
|
.fragment_spirv_ops = fragment_spirv_ops,
|
||||||
},
|
},
|
||||||
|
|
||||||
.loaded = &loaded,
|
.loaded = &loaded,
|
||||||
|
@ -127,20 +129,6 @@ pub const Assets = struct {
|
||||||
return error.FormatUnsupported;
|
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;
|
pub const thread_restriction = .main;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -213,13 +201,6 @@ pub const Input = union (enum) {
|
||||||
|
|
||||||
pub const Effect = handles.Effect;
|
pub const Effect = handles.Effect;
|
||||||
|
|
||||||
pub const Rect = struct {
|
|
||||||
left: f32,
|
|
||||||
top: f32,
|
|
||||||
right: f32,
|
|
||||||
bottom: f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Texture = handles.Texture;
|
pub const Texture = handles.Texture;
|
||||||
|
|
||||||
pub fn poll(app: flow.Write(App), inputs: msg.Send(Input)) !void {
|
pub fn poll(app: flow.Write(App), inputs: msg.Send(Input)) !void {
|
||||||
|
|
|
@ -3,7 +3,7 @@ const coral = @import("coral");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Effect = Handle(struct {
|
pub const Effect = Handle(struct {
|
||||||
fragment_spirv: []const u32,
|
fragment_spirv_ops: []const u32,
|
||||||
});
|
});
|
||||||
|
|
||||||
fn Handle(comptime HandleDesc: type) type {
|
fn Handle(comptime HandleDesc: type) type {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const gfx = @import("../gfx.zig");
|
|
||||||
|
|
||||||
pub const Color = @Vector(4, f32);
|
pub const Color = @Vector(4, f32);
|
||||||
|
|
||||||
pub fn EvenOrderMatrix(comptime n: usize, comptime Element: type) type {
|
pub fn EvenOrderMatrix(comptime n: usize, comptime Element: type) type {
|
||||||
|
@ -16,7 +14,14 @@ pub const Transform2D = extern struct {
|
||||||
|
|
||||||
pub const ProjectionMatrix = EvenOrderMatrix(4, f32);
|
pub const ProjectionMatrix = EvenOrderMatrix(4, f32);
|
||||||
|
|
||||||
pub fn orthographic_projection(near: f32, far: f32, viewport: gfx.Rect) EvenOrderMatrix(4, f32) {
|
pub const Rect = struct {
|
||||||
|
left: f32,
|
||||||
|
top: f32,
|
||||||
|
right: f32,
|
||||||
|
bottom: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn orthographic_projection(near: f32, far: f32, viewport: Rect) EvenOrderMatrix(4, f32) {
|
||||||
const width = viewport.right - viewport.left;
|
const width = viewport.right - viewport.left;
|
||||||
const height = viewport.bottom - viewport.top;
|
const height = viewport.bottom - viewport.top;
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ const Frame = struct {
|
||||||
|
|
||||||
bindings.vertex_buffers[vertex_indices.mesh] = quad_vertex_buffer;
|
bindings.vertex_buffers[vertex_indices.mesh] = quad_vertex_buffer;
|
||||||
|
|
||||||
switch (pools.textures.get(@intFromEnum(self.current_source_texture)).?.*) {
|
switch (pools.textures.get(@intFromEnum(self.current_source_texture)).?.access) {
|
||||||
.render => |render| {
|
.render => |render| {
|
||||||
bindings.fs.images[0] = render.color_image;
|
bindings.fs.images[0] = render.color_image;
|
||||||
bindings.fs.samplers[0] = render.sampler;
|
bindings.fs.samplers[0] = render.sampler;
|
||||||
|
@ -197,22 +197,30 @@ const Frame = struct {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.current_target_texture) |target_texture| {
|
|
||||||
const target_view_buffer = pools.textures.get(@intFromEnum(target_texture)).?.render.view_buffer;
|
|
||||||
|
|
||||||
bindings.vs.storage_buffers[0] = target_view_buffer;
|
|
||||||
} else {
|
|
||||||
bindings.vs.storage_buffers[0] = view_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const effect = pools.effects.get(@intFromEnum(self.current_effect)).?;
|
const effect = pools.effects.get(@intFromEnum(self.current_effect)).?;
|
||||||
|
|
||||||
sokol.gfx.applyPipeline(effect.pipeline);
|
sokol.gfx.applyPipeline(effect.pipeline);
|
||||||
|
|
||||||
if (effect.has_properties_buffer) |properties_buffer| {
|
if (self.current_target_texture) |target_texture| {
|
||||||
bindings.fs.storage_buffers[0] = properties_buffer;
|
const texture = pools.textures.get(@intFromEnum(target_texture)).?;
|
||||||
|
|
||||||
|
sokol.gfx.applyUniforms(.VS, 0, sokol.gfx.asRange(&lina.orthographic_projection(-1.0, 1.0, .{
|
||||||
|
.left = 0,
|
||||||
|
.top = 0,
|
||||||
|
.right = @floatFromInt(texture.width),
|
||||||
|
.bottom = @floatFromInt(texture.height),
|
||||||
|
})));
|
||||||
|
} else {
|
||||||
|
sokol.gfx.applyUniforms(.VS, 0, sokol.gfx.asRange(&lina.orthographic_projection(-1.0, 1.0, .{
|
||||||
|
.left = 0,
|
||||||
|
.top = 0,
|
||||||
|
.right = @floatFromInt(self.swapchain.width),
|
||||||
|
.bottom = @floatFromInt(self.swapchain.height),
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sokol.gfx.applyUniforms(.FS, 0, sokol.gfx.asRange(effect.properties));
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const buffer_index = self.flushed_count / batches_per_buffer;
|
const buffer_index = self.flushed_count / batches_per_buffer;
|
||||||
const buffer_offset = self.flushed_count % batches_per_buffer;
|
const buffer_offset = self.flushed_count % batches_per_buffer;
|
||||||
|
@ -238,10 +246,8 @@ const Frame = struct {
|
||||||
|
|
||||||
self.current_effect = command.effect;
|
self.current_effect = command.effect;
|
||||||
|
|
||||||
if (command.properties.len != 0) {
|
if (pools.effects.get(@intFromEnum(self.current_effect))) |effect| {
|
||||||
if (pools.effects.get(@intFromEnum(self.current_effect)).?.has_properties_buffer) |properties_buffer| {
|
@memcpy(effect.properties, command.properties);
|
||||||
sokol.gfx.updateBuffer(properties_buffer, sokol.gfx.asRange(command.properties));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +281,7 @@ const Frame = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.texture) |texture| {
|
if (command.texture) |texture| {
|
||||||
pass.attachments = switch (pools.textures.get(@intFromEnum(texture)).?.*) {
|
pass.attachments = switch (pools.textures.get(@intFromEnum(texture)).?.access) {
|
||||||
.static => @panic("Cannot render to static textures"),
|
.static => @panic("Cannot render to static textures"),
|
||||||
.render => |render| render.attachments,
|
.render => |render| render.attachments,
|
||||||
};
|
};
|
||||||
|
@ -376,7 +382,7 @@ const Pools = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = try pools.create_effect(.{
|
_ = try pools.create_effect(.{
|
||||||
.fragment_spirv = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
.fragment_spirv_ops = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
||||||
});
|
});
|
||||||
|
|
||||||
const default_texture_data = [_]u32{
|
const default_texture_data = [_]u32{
|
||||||
|
@ -463,8 +469,6 @@ var quad_index_buffer: sokol.gfx.Buffer = undefined;
|
||||||
|
|
||||||
var quad_vertex_buffer: sokol.gfx.Buffer = undefined;
|
var quad_vertex_buffer: sokol.gfx.Buffer = undefined;
|
||||||
|
|
||||||
var view_buffer: sokol.gfx.Buffer = undefined;
|
|
||||||
|
|
||||||
const vertex_indices = .{
|
const vertex_indices = .{
|
||||||
.mesh = 0,
|
.mesh = 0,
|
||||||
.instance = 1,
|
.instance = 1,
|
||||||
|
@ -537,18 +541,6 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
.type = .VERTEXBUFFER,
|
.type = .VERTEXBUFFER,
|
||||||
});
|
});
|
||||||
|
|
||||||
view_buffer = sokol.gfx.makeBuffer(.{
|
|
||||||
.data = sokol.gfx.asRange(&lina.orthographic_projection(-1.0, 1.0, .{
|
|
||||||
.left = 0,
|
|
||||||
.top = 0,
|
|
||||||
.right = @floatFromInt(1280),
|
|
||||||
.bottom = @floatFromInt(720),
|
|
||||||
})),
|
|
||||||
|
|
||||||
.type = .STORAGEBUFFER,
|
|
||||||
.usage = .IMMUTABLE,
|
|
||||||
});
|
|
||||||
|
|
||||||
var has_commands_head: ?*Commands = null;
|
var has_commands_head: ?*Commands = null;
|
||||||
var has_commands_tail: ?*Commands = null;
|
var has_commands_tail: ?*Commands = null;
|
||||||
|
|
||||||
|
|
|
@ -13,29 +13,50 @@ const std = @import("std");
|
||||||
pub const Effect = struct {
|
pub const Effect = struct {
|
||||||
shader: sokol.gfx.Shader,
|
shader: sokol.gfx.Shader,
|
||||||
pipeline: sokol.gfx.Pipeline,
|
pipeline: sokol.gfx.Pipeline,
|
||||||
has_properties_buffer: ?sokol.gfx.Buffer = null,
|
properties: []coral.io.Byte,
|
||||||
|
|
||||||
pub fn deinit(self: *Effect) void {
|
pub fn deinit(self: *Effect) void {
|
||||||
|
coral.heap.allocator.free(self.properties);
|
||||||
sokol.gfx.destroyPipeline(self.pipeline);
|
sokol.gfx.destroyPipeline(self.pipeline);
|
||||||
sokol.gfx.destroyShader(self.shader);
|
sokol.gfx.destroyShader(self.shader);
|
||||||
|
|
||||||
if (self.has_properties_buffer) |parameters_buffer| {
|
|
||||||
sokol.gfx.destroyBuffer(parameters_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(desc: handles.Effect.Desc) !Effect {
|
pub fn init(desc: handles.Effect.Desc) spirv.Error!Effect {
|
||||||
var spirv_unit = try spirv.Unit.init(coral.heap.allocator);
|
var spirv_arena = std.heap.ArenaAllocator.init(coral.heap.allocator);
|
||||||
|
|
||||||
defer spirv_unit.deinit();
|
defer {
|
||||||
|
spirv_arena.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
try spirv_unit.compile(&spirv.to_ops(@embedFile("./shaders/2d_default.vert.spv")), .vertex);
|
const spirv_program = try spirv.analyze(&spirv_arena, .{
|
||||||
try spirv_unit.compile(desc.fragment_spirv, .fragment);
|
.target = try switch (sokol.gfx.queryBackend()) {
|
||||||
|
.GLCORE => spirv.Target.glsl,
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
const shader = sokol.gfx.makeShader(spirv_unit.shader_desc());
|
.vertex_source = .{
|
||||||
|
.ops = &spirv.to_ops(@embedFile("./shaders/2d_default.vert.spv")),
|
||||||
|
},
|
||||||
|
|
||||||
|
.fragment_source = .{
|
||||||
|
.ops = desc.fragment_spirv_ops,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const shader = sokol.gfx.makeShader(shader_desc: {
|
||||||
|
const shader_desc = sokol.gfx.ShaderDesc{
|
||||||
|
.vs = stage_desc(spirv_program.vertex_stage),
|
||||||
|
.fs = stage_desc(spirv_program.fragment_stage),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Vertex attributes, for some reason they aren't needed?
|
||||||
|
|
||||||
|
break: shader_desc shader_desc;
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Review blending rules.
|
||||||
const pipeline = sokol.gfx.makePipeline(pipeline_desc: {
|
const pipeline = sokol.gfx.makePipeline(pipeline_desc: {
|
||||||
var pipeline_desc = sokol.gfx.PipelineDesc{
|
var pipeline_desc = sokol.gfx.PipelineDesc{
|
||||||
.label = "Effect pipeline",
|
.label = "Effect pipeline",
|
||||||
|
@ -58,33 +79,106 @@ pub const Effect = struct {
|
||||||
break: pipeline_desc pipeline_desc;
|
break: pipeline_desc pipeline_desc;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const properties = try coral.heap.allocator.alloc(
|
||||||
|
coral.io.Byte,
|
||||||
|
if (spirv_program.fragment_stage.has_uniform_blocks[0]) |uniform_block| uniform_block.size() else 0,
|
||||||
|
);
|
||||||
|
|
||||||
errdefer {
|
errdefer {
|
||||||
|
coral.heap.allocator.free(properties);
|
||||||
sokol.gfx.destroyPipeline(pipeline);
|
sokol.gfx.destroyPipeline(pipeline);
|
||||||
sokol.gfx.destroyShader(shader);
|
sokol.gfx.destroyShader(shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spirv_unit.shader_stage(.fragment).has_storage_buffer[0]) |storage_buffer| {
|
return .{
|
||||||
const properties_buffer = sokol.gfx.makeBuffer(.{
|
.shader = shader,
|
||||||
.size = storage_buffer.minimum_size,
|
.pipeline = pipeline,
|
||||||
.type = .STORAGEBUFFER,
|
.properties = properties,
|
||||||
.usage = .STREAM,
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stage_desc(spirv_stage: spirv.Stage) sokol.gfx.ShaderStageDesc {
|
||||||
|
var stage = sokol.gfx.ShaderStageDesc{
|
||||||
|
.entry = spirv_stage.entry_point,
|
||||||
|
.source = spirv_stage.source,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (0 .. spirv.Stage.max_uniform_blocks) |slot| {
|
||||||
|
const uniform_block = &(spirv_stage.has_uniform_blocks[slot] orelse {
|
||||||
|
continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
errdefer {
|
const stage_uniform_block = &stage.uniform_blocks[slot];
|
||||||
sokol.gfx.destroyBuffer(properties_buffer);
|
|
||||||
|
stage_uniform_block.layout = switch (uniform_block.layout) {
|
||||||
|
.std140 => .STD140,
|
||||||
|
};
|
||||||
|
|
||||||
|
stage_uniform_block.size = uniform_block.size();
|
||||||
|
|
||||||
|
for (stage_uniform_block.uniforms[0 .. uniform_block.uniforms.len], uniform_block.uniforms) |*stage_uniform, uniform| {
|
||||||
|
stage_uniform.* = .{
|
||||||
|
.type = switch (uniform.type) {
|
||||||
|
.float => .FLOAT,
|
||||||
|
.float2 => .FLOAT2,
|
||||||
|
.float3 => .FLOAT3,
|
||||||
|
.float4 => .FLOAT4,
|
||||||
|
.int => .INT,
|
||||||
|
.int2 => .INT2,
|
||||||
|
.int3 => .INT3,
|
||||||
|
.int4 => .INT4,
|
||||||
|
.mat4 => .MAT4,
|
||||||
|
},
|
||||||
|
|
||||||
|
.name = uniform.name,
|
||||||
|
.array_count = uniform.len,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{
|
for (0 .. spirv.Stage.max_images) |slot| {
|
||||||
.shader = shader,
|
const image = &(spirv_stage.has_images[slot] orelse {
|
||||||
.pipeline = pipeline,
|
continue;
|
||||||
.has_properties_buffer = properties_buffer,
|
});
|
||||||
};
|
|
||||||
} else {
|
stage.images[slot] = .{
|
||||||
return .{
|
.multisampled = image.is_multisampled,
|
||||||
.shader = shader,
|
.image_type = ._2D,
|
||||||
.pipeline = pipeline,
|
.sample_type = .FLOAT,
|
||||||
|
.used = true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (0 .. spirv.Stage.max_samplers) |slot| {
|
||||||
|
const sampler = &(spirv_stage.has_samplers[slot] orelse {
|
||||||
|
continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
stage.samplers[slot] = .{
|
||||||
|
.sampler_type = switch (sampler.*) {
|
||||||
|
.filtering => .FILTERING,
|
||||||
|
.non_filtering => .NONFILTERING,
|
||||||
|
.comparison => .COMPARISON,
|
||||||
|
},
|
||||||
|
|
||||||
|
.used = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0 .. spirv.Stage.max_image_sampler_pairs) |slot| {
|
||||||
|
const image_sampler_pair = &(spirv_stage.has_image_sampler_pairs[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;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vertex_layout_state = sokol.gfx.VertexLayoutState{
|
const vertex_layout_state = sokol.gfx.VertexLayoutState{
|
||||||
|
@ -144,113 +238,114 @@ pub const Effect = struct {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Texture = union (enum) {
|
pub const Texture = struct {
|
||||||
render: Render,
|
width: u16,
|
||||||
static: Static,
|
height: u16,
|
||||||
|
access: Access,
|
||||||
|
|
||||||
const Render = struct {
|
pub const Access = union (enum) {
|
||||||
|
render: RenderAccess,
|
||||||
|
static: StaticAccess,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const RenderAccess = struct {
|
||||||
sampler: sokol.gfx.Sampler,
|
sampler: sokol.gfx.Sampler,
|
||||||
color_image: sokol.gfx.Image,
|
color_image: sokol.gfx.Image,
|
||||||
depth_image: sokol.gfx.Image,
|
depth_image: sokol.gfx.Image,
|
||||||
attachments: sokol.gfx.Attachments,
|
attachments: sokol.gfx.Attachments,
|
||||||
view_buffer: sokol.gfx.Buffer,
|
};
|
||||||
|
|
||||||
fn deinit(self: *Render) void {
|
pub const StaticAccess = struct {
|
||||||
sokol.gfx.destroyImage(self.color_image);
|
sampler: sokol.gfx.Sampler,
|
||||||
sokol.gfx.destroyImage(self.depth_image);
|
image: sokol.gfx.Image,
|
||||||
sokol.gfx.destroySampler(self.sampler);
|
};
|
||||||
sokol.gfx.destroyAttachments(self.attachments);
|
|
||||||
sokol.gfx.destroyBuffer(self.view_buffer);
|
pub fn deinit(self: *Texture) void {
|
||||||
|
switch (self.access) {
|
||||||
|
.render => |render| {
|
||||||
|
sokol.gfx.destroyImage(render.color_image);
|
||||||
|
sokol.gfx.destroyImage(render.depth_image);
|
||||||
|
sokol.gfx.destroySampler(render.sampler);
|
||||||
|
sokol.gfx.destroyAttachments(render.attachments);
|
||||||
|
},
|
||||||
|
|
||||||
|
.static => |static| {
|
||||||
|
sokol.gfx.destroyImage(static.image);
|
||||||
|
sokol.gfx.destroySampler(static.sampler);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(format: handles.Texture.Desc.Format, access: handles.Texture.Desc.Access.Render) std.mem.Allocator.Error!Render {
|
pub fn init(desc: handles.Texture.Desc) std.mem.Allocator.Error!Texture {
|
||||||
const color_image = sokol.gfx.makeImage(.{
|
const pixel_format = switch (desc.format) {
|
||||||
.pixel_format = switch (format) {
|
|
||||||
.rgba8 => sokol.gfx.PixelFormat.RGBA8,
|
.rgba8 => sokol.gfx.PixelFormat.RGBA8,
|
||||||
.bgra8 => sokol.gfx.PixelFormat.BGRA8,
|
.bgra8 => sokol.gfx.PixelFormat.BGRA8,
|
||||||
},
|
};
|
||||||
|
|
||||||
.width = access.width,
|
switch (desc.access) {
|
||||||
.height = access.height,
|
.render => |render| {
|
||||||
|
const color_image = sokol.gfx.makeImage(.{
|
||||||
|
.pixel_format = pixel_format,
|
||||||
|
.width = render.width,
|
||||||
|
.height = render.height,
|
||||||
.render_target = true,
|
.render_target = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const depth_image = sokol.gfx.makeImage(.{
|
const depth_image = sokol.gfx.makeImage(.{
|
||||||
.width = access.width,
|
.width = render.width,
|
||||||
.height = access.height,
|
.height = render.height,
|
||||||
.render_target = true,
|
.render_target = true,
|
||||||
.pixel_format = .DEPTH_STENCIL,
|
.pixel_format = .DEPTH_STENCIL,
|
||||||
});
|
});
|
||||||
|
|
||||||
const attachments = sokol.gfx.makeAttachments(attachments_desc: {
|
const attachments = sokol.gfx.makeAttachments(attachments_desc: {
|
||||||
var desc = sokol.gfx.AttachmentsDesc{
|
var attachments_desc = sokol.gfx.AttachmentsDesc{
|
||||||
.depth_stencil = .{
|
.depth_stencil = .{
|
||||||
.image = depth_image,
|
.image = depth_image,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
desc.colors[0] = .{
|
attachments_desc.colors[0] = .{
|
||||||
.image = color_image,
|
.image = color_image,
|
||||||
};
|
};
|
||||||
|
|
||||||
break: attachments_desc desc;
|
break: attachments_desc attachments_desc;
|
||||||
});
|
});
|
||||||
|
|
||||||
const sampler = sokol.gfx.makeSampler(.{});
|
const sampler = sokol.gfx.makeSampler(.{});
|
||||||
|
|
||||||
const view_buffer = sokol.gfx.makeBuffer(.{
|
|
||||||
.data = sokol.gfx.asRange(&lina.orthographic_projection(-1.0, 1.0, .{
|
|
||||||
.left = 0,
|
|
||||||
.top = 0,
|
|
||||||
.right = @floatFromInt(access.width),
|
|
||||||
.bottom = @floatFromInt(access.height),
|
|
||||||
})),
|
|
||||||
|
|
||||||
.type = .STORAGEBUFFER,
|
|
||||||
.usage = .IMMUTABLE,
|
|
||||||
});
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.width = render.width,
|
||||||
|
.height = render.height,
|
||||||
|
|
||||||
|
.access = .{
|
||||||
|
.render = .{
|
||||||
.attachments = attachments,
|
.attachments = attachments,
|
||||||
.sampler = sampler,
|
.sampler = sampler,
|
||||||
.color_image = color_image,
|
.color_image = color_image,
|
||||||
.depth_image = depth_image,
|
.depth_image = depth_image,
|
||||||
.view_buffer = view_buffer,
|
},
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
const Static = struct {
|
.static => |static| {
|
||||||
image: sokol.gfx.Image,
|
const height = std.math.cast(u16, static.data.len / (static.width * desc.format.byte_size())) orelse {
|
||||||
sampler: sokol.gfx.Sampler,
|
|
||||||
|
|
||||||
fn deinit(self: *Static) void {
|
|
||||||
sokol.gfx.destroyImage(self.image);
|
|
||||||
sokol.gfx.destroySampler(self.sampler);
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(format: handles.Texture.Desc.Format, access: handles.Texture.Desc.Access.Static) std.mem.Allocator.Error!Static {
|
|
||||||
const image = sokol.gfx.makeImage(image_desc: {
|
|
||||||
var desc = sokol.gfx.ImageDesc{
|
|
||||||
.height = std.math.cast(i32, access.data.len / (access.width * format.byte_size())) orelse {
|
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
},
|
|
||||||
|
|
||||||
.pixel_format = switch (format) {
|
|
||||||
.rgba8 => sokol.gfx.PixelFormat.RGBA8,
|
|
||||||
.bgra8 => sokol.gfx.PixelFormat.BGRA8,
|
|
||||||
},
|
|
||||||
|
|
||||||
.width = access.width,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
desc.data.subimage[0][0] = sokol.gfx.asRange(access.data);
|
const image = sokol.gfx.makeImage(image_desc: {
|
||||||
|
var image_desc = sokol.gfx.ImageDesc{
|
||||||
|
.height = height,
|
||||||
|
.pixel_format = pixel_format,
|
||||||
|
.width = static.width,
|
||||||
|
};
|
||||||
|
|
||||||
break: image_desc desc;
|
image_desc.data.subimage[0][0] = sokol.gfx.asRange(static.data);
|
||||||
|
|
||||||
|
break: image_desc image_desc;
|
||||||
});
|
});
|
||||||
|
|
||||||
const sampler = sokol.gfx.makeSampler(.{});
|
const sampler = sokol.gfx.makeSampler(.{});
|
||||||
|
@ -261,23 +356,17 @@ pub const Texture = union (enum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.width = static.width,
|
||||||
|
.height = height,
|
||||||
|
|
||||||
|
.access = .{
|
||||||
|
.static = .{
|
||||||
.image = image,
|
.image = image,
|
||||||
.sampler = sampler,
|
.sampler = sampler,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
pub fn deinit(self: *Texture) void {
|
|
||||||
switch (self.*) {
|
|
||||||
.static => |*static| static.deinit(),
|
|
||||||
.render => |*render| render.deinit(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(desc: handles.Texture.Desc) std.mem.Allocator.Error!Texture {
|
|
||||||
return switch (desc.access) {
|
|
||||||
.static => |static| .{.static = try Static.init(desc.format, static)},
|
|
||||||
.render => |render| .{.render = try Render.init(desc.format, render)},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ layout (location = 7) in vec4 instance_rect;
|
||||||
layout (location = 0) out vec4 color;
|
layout (location = 0) out vec4 color;
|
||||||
layout (location = 1) out vec2 uv;
|
layout (location = 1) out vec2 uv;
|
||||||
|
|
||||||
layout (binding = 0) readonly buffer View {
|
layout (binding = 0) uniform View {
|
||||||
mat4 projection_matrix;
|
mat4 projection_matrix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,18 +9,33 @@ const sokol = @import("sokol");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Error = std.mem.Allocator.Error || error {
|
pub const Error = std.mem.Allocator.Error || error {
|
||||||
UnsupportedTarget,
|
|
||||||
InvalidSPIRV,
|
InvalidSPIRV,
|
||||||
UnsupportedSPIRV,
|
UnsupportedSPIRV,
|
||||||
UnsupportedBackend,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Shader = struct {
|
pub const Options = struct {
|
||||||
decompiled_source: [:0]const u8 = "",
|
target: Target,
|
||||||
has_storage_buffer: [max_storage_buffers]?StorageBuffer = [_]?StorageBuffer{null} ** max_storage_buffers,
|
vertex_source: Source,
|
||||||
has_image: [max_images]?Image = [_]?Image{null} ** max_images,
|
fragment_source: Source,
|
||||||
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 const Program = struct {
|
||||||
|
vertex_stage: Stage,
|
||||||
|
fragment_stage: Stage,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Source = struct {
|
||||||
|
entry_point: []const u8 = "main",
|
||||||
|
ops: []const u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Stage = struct {
|
||||||
|
entry_point: [:0]const u8,
|
||||||
|
source: [:0]const u8,
|
||||||
|
has_image_sampler_pairs: [max_image_sampler_pairs]?ImageSamplerPair = [_]?ImageSamplerPair{null} ** max_image_sampler_pairs,
|
||||||
|
has_images: [max_images]?Image = [_]?Image{null} ** max_images,
|
||||||
|
has_samplers: [max_samplers]?Sampler = [_]?Sampler{null} ** max_samplers,
|
||||||
|
has_uniform_blocks: [max_uniform_blocks]?UniformBlock = [_]?UniformBlock{null} ** max_uniform_blocks,
|
||||||
|
|
||||||
pub const Image = struct {
|
pub const Image = struct {
|
||||||
is_multisampled: bool,
|
is_multisampled: bool,
|
||||||
|
@ -32,15 +47,65 @@ pub const Shader = struct {
|
||||||
name: [:0]const u8,
|
name: [:0]const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Layout = enum {
|
||||||
|
std140,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Sampler = enum {
|
pub const Sampler = enum {
|
||||||
filtering,
|
filtering,
|
||||||
non_filtering,
|
non_filtering,
|
||||||
comparison,
|
comparison,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const StorageBuffer = struct {
|
pub const Uniform = struct {
|
||||||
minimum_size: usize,
|
name: [:0]const u8,
|
||||||
is_readonly: bool,
|
type: Type,
|
||||||
|
len: u16,
|
||||||
|
|
||||||
|
pub const Type = enum {
|
||||||
|
float,
|
||||||
|
float2,
|
||||||
|
float3,
|
||||||
|
float4,
|
||||||
|
int,
|
||||||
|
int2,
|
||||||
|
int3,
|
||||||
|
int4,
|
||||||
|
mat4,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UniformBlock = struct {
|
||||||
|
layout: Layout,
|
||||||
|
uniforms: []const Uniform,
|
||||||
|
|
||||||
|
pub const max_uniforms = 16;
|
||||||
|
|
||||||
|
pub fn size(self: UniformBlock) usize {
|
||||||
|
const alignment: usize = switch (self.layout) {
|
||||||
|
.std140 => 16,
|
||||||
|
};
|
||||||
|
|
||||||
|
var accumulated_size: usize = 0;
|
||||||
|
|
||||||
|
for (self.uniforms) |uniform| {
|
||||||
|
const type_size = @max(1, uniform.len) * @as(usize, switch (uniform.type) {
|
||||||
|
.float => 4,
|
||||||
|
.float2 => 8,
|
||||||
|
.float3 => 12,
|
||||||
|
.float4 => 16,
|
||||||
|
.int => 4,
|
||||||
|
.int2 => 8,
|
||||||
|
.int3 => 12,
|
||||||
|
.int4 => 16,
|
||||||
|
.mat4 => 64,
|
||||||
|
});
|
||||||
|
|
||||||
|
accumulated_size += (type_size + (alignment - 1)) & ~(alignment - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accumulated_size;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const max_images = 12;
|
pub const max_images = 12;
|
||||||
|
@ -51,7 +116,10 @@ pub const Shader = struct {
|
||||||
|
|
||||||
pub const max_storage_buffers = 8;
|
pub const max_storage_buffers = 8;
|
||||||
|
|
||||||
fn reflect_image_samplers(self: *Shader, _: Stage, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void {
|
pub const max_uniform_blocks = 8;
|
||||||
|
|
||||||
|
fn reflect_images_samplers(self: *Stage, arena: *std.heap.ArenaAllocator, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void {
|
||||||
|
const arena_allocator = arena.allocator();
|
||||||
var sampled_images: []const ext.spvc_reflected_resource = &.{};
|
var sampled_images: []const ext.spvc_reflected_resource = &.{};
|
||||||
|
|
||||||
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
||||||
|
@ -82,196 +150,208 @@ pub const Shader = struct {
|
||||||
else => error.InvalidSPIRV,
|
else => error.InvalidSPIRV,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.has_image[i] = .{
|
self.has_images[i] = .{
|
||||||
.is_multisampled = ext.spvc_type_get_image_multisampled(sampled_image_type) != 0,
|
.is_multisampled = ext.spvc_type_get_image_multisampled(sampled_image_type) != 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.has_sampler[i] = .filtering;
|
self.has_samplers[i] = .filtering;
|
||||||
|
|
||||||
self.has_image_sampler_pair[i] = .{
|
self.has_image_sampler_pairs[i] = .{
|
||||||
.name = std.mem.span(ext.spvc_compiler_get_name(compiler, sampled_image.id)),
|
.name = try arena_allocator.dupeZ(u8, std.mem.span(ext.spvc_compiler_get_name(compiler, sampled_image.id))),
|
||||||
.image_slot = @intCast(i),
|
.image_slot = @intCast(i),
|
||||||
.sampler_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 {
|
fn reflect_uniform_blocks(self: *Stage, arena: *std.heap.ArenaAllocator, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void {
|
||||||
var storage_buffers: []const ext.spvc_reflected_resource = &.{};
|
const arena_allocator = arena.allocator();
|
||||||
|
var reflected_resources: []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(
|
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
||||||
resources,
|
resources,
|
||||||
ext.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
|
ext.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
|
||||||
@ptrCast(&uniform_buffers.ptr),
|
@ptrCast(&reflected_resources.ptr),
|
||||||
&uniform_buffers.len,
|
&reflected_resources.len,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (uniform_buffers.len != 0) {
|
if (reflected_resources.len > max_uniform_blocks) {
|
||||||
return error.InvalidSPIRV;
|
return error.InvalidSPIRV;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support for older APIs?
|
for (0 .. reflected_resources.len, reflected_resources) |i, reflected_resource| {
|
||||||
_ = self;
|
const type_handle = ext.spvc_compiler_get_type_handle(compiler, reflected_resource.base_type_id);
|
||||||
_ = compiler;
|
const is_std430 = ext.spvc_compiler_has_decoration(compiler, reflected_resource.id, ext.SpvDecorationBufferBlock) == ext.SPVC_TRUE;
|
||||||
|
|
||||||
|
if (is_std430) {
|
||||||
|
return error.UnsupportedSPIRV;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stage_desc(self: Shader) sokol.gfx.ShaderStageDesc {
|
const uniform_count = ext.spvc_type_get_num_member_types(type_handle);
|
||||||
var stage = sokol.gfx.ShaderStageDesc{
|
|
||||||
.entry = "main",
|
|
||||||
.source = self.decompiled_source,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (0 .. max_storage_buffers) |slot| {
|
if (uniform_count > UniformBlock.max_uniforms) {
|
||||||
const storage_buffer = &(self.has_storage_buffer[slot] orelse {
|
return error.UnsupportedSPIRV;
|
||||||
continue;
|
|
||||||
});
|
|
||||||
|
|
||||||
stage.storage_buffers[slot] = .{
|
|
||||||
.readonly = storage_buffer.is_readonly,
|
|
||||||
.used = true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (0 .. max_images) |slot| {
|
const uniforms = try arena_allocator.alloc(Uniform, uniform_count);
|
||||||
const image = &(self.has_image[slot] orelse {
|
|
||||||
continue;
|
|
||||||
});
|
|
||||||
|
|
||||||
stage.images[slot] = .{
|
for (uniforms, 0 .. uniform_count) |*uniform, j| {
|
||||||
.multisampled = image.is_multisampled,
|
const member_index: c_uint = @intCast(j);
|
||||||
.image_type = ._2D,
|
const member_type_handle = ext.spvc_compiler_get_type_handle(compiler, ext.spvc_type_get_member_type(type_handle, member_index));
|
||||||
.sample_type = .FLOAT,
|
|
||||||
.used = true,
|
if (ext.spvc_type_get_num_array_dimensions(member_type_handle) > 1) {
|
||||||
};
|
return error.UnsupportedSPIRV;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (0 .. max_samplers) |slot| {
|
uniform.* = .{
|
||||||
const sampler = &(self.has_sampler[slot] orelse {
|
.name = try coral.utf8.alloc_formatted(arena_allocator, "_{id}.{member_name}", .{
|
||||||
continue;
|
.id = reflected_resource.id,
|
||||||
});
|
.member_name = std.mem.span(ext.spvc_compiler_get_member_name(compiler, reflected_resource.base_type_id, member_index)),
|
||||||
|
}),
|
||||||
|
|
||||||
stage.samplers[slot] = .{
|
.type = try switch (ext.spvc_type_get_basetype(member_type_handle)) {
|
||||||
.sampler_type = switch (sampler.*) {
|
ext.SPVC_BASETYPE_FP32 => switch (ext.spvc_type_get_vector_size(member_type_handle)) {
|
||||||
.filtering => .FILTERING,
|
4 => switch (ext.spvc_type_get_columns(member_type_handle)) {
|
||||||
.non_filtering => .NONFILTERING,
|
4 => Uniform.Type.mat4,
|
||||||
.comparison => .COMPARISON,
|
1 => Uniform.Type.float4,
|
||||||
|
else => error.UnsupportedSPIRV,
|
||||||
},
|
},
|
||||||
|
|
||||||
.used = true,
|
1 => Uniform.Type.float,
|
||||||
|
2 => Uniform.Type.float2,
|
||||||
|
3 => Uniform.Type.float3,
|
||||||
|
else => error.UnsupportedSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
ext.SPVC_BASETYPE_INT32 => try switch (ext.spvc_type_get_vector_size(member_type_handle)) {
|
||||||
|
1 => Uniform.Type.int,
|
||||||
|
2 => Uniform.Type.int2,
|
||||||
|
3 => Uniform.Type.int3,
|
||||||
|
4 => Uniform.Type.int4,
|
||||||
|
else => error.UnsupportedSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
else => error.UnsupportedSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
.len = std.math.cast(u16, ext.spvc_type_get_array_dimension(member_type_handle, 0)) orelse {
|
||||||
|
return error.UnsupportedSPIRV;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (0 .. max_image_sampler_pairs) |slot| {
|
self.has_uniform_blocks[i] = .{
|
||||||
const image_sampler_pair = &(self.has_image_sampler_pair[slot] orelse {
|
.uniforms = uniforms,
|
||||||
continue;
|
.layout = .std140,
|
||||||
});
|
|
||||||
|
|
||||||
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 {
|
pub const Target = enum {
|
||||||
vertex,
|
glsl,
|
||||||
fragment,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Unit = struct {
|
pub fn analyze(arena: *std.heap.ArenaAllocator, options: Options) Error!Program {
|
||||||
arena: std.heap.ArenaAllocator,
|
var context: ext.spvc_context = null;
|
||||||
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 {
|
if (ext.spvc_context_create(&context) != ext.SPVC_SUCCESS) {
|
||||||
const execution_model, const shader = switch (stage) {
|
return error.OutOfMemory;
|
||||||
.vertex => .{ext.SpvExecutionModelVertex, &self.stages[0]},
|
}
|
||||||
.fragment => .{ext.SpvExecutionModelFragment, &self.stages[1]},
|
|
||||||
|
defer {
|
||||||
|
ext.spvc_context_destroy(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.spvc_context_set_error_callback(context, log_errors, null);
|
||||||
|
|
||||||
|
const arena_allocator = arena.allocator();
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.vertex_stage = vertex_stage: {
|
||||||
|
const compiler = try parse(arena, context, options.target, options.vertex_source);
|
||||||
|
|
||||||
|
var stage = Stage{
|
||||||
|
.entry_point = try arena_allocator.dupeZ(u8, options.vertex_source.entry_point),
|
||||||
|
.source = try compile(arena, compiler),
|
||||||
};
|
};
|
||||||
|
|
||||||
const Backend = struct {
|
const resources = create: {
|
||||||
target: ext.spvc_backend,
|
var resources: ext.spvc_resources = null;
|
||||||
option_values: []const struct {ext.spvc_compiler_option, c_uint},
|
|
||||||
|
try to_error(ext.spvc_compiler_create_shader_resources(compiler, &resources));
|
||||||
|
|
||||||
|
break: create resources;
|
||||||
};
|
};
|
||||||
|
|
||||||
const backend: Backend = try switch (sokol.gfx.queryBackend()) {
|
try stage.reflect_images_samplers(arena, compiler, resources);
|
||||||
.GLCORE => .{
|
try stage.reflect_uniform_blocks(arena, compiler, resources);
|
||||||
.target = ext.SPVC_BACKEND_GLSL,
|
|
||||||
|
|
||||||
.option_values = &.{
|
std.log.info("{s}", .{stage.source});
|
||||||
.{ext.SPVC_COMPILER_OPTION_GLSL_VERSION, 430},
|
|
||||||
.{ext.SPVC_COMPILER_OPTION_GLSL_ES, @intFromBool(false)},
|
break: vertex_stage stage;
|
||||||
.{ext.SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, @intFromBool(false)},
|
|
||||||
.{ext.SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS, @intFromBool(true)},
|
|
||||||
.{ext.SPVC_COMPILER_OPTION_FLIP_VERTEX_Y, @intFromBool(true)},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
else => error.UnsupportedBackend,
|
.fragment_stage = fragment_stage: {
|
||||||
|
const compiler = try parse(arena, context, options.target, options.fragment_source);
|
||||||
|
|
||||||
|
var stage = Stage{
|
||||||
|
.entry_point = try arena_allocator.dupeZ(u8, options.fragment_source.entry_point),
|
||||||
|
.source = try compile(arena, compiler),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compiler = parse_and_configure: {
|
const resources = create: {
|
||||||
|
var resources: ext.spvc_resources = null;
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_create_shader_resources(compiler, &resources));
|
||||||
|
|
||||||
|
break: create resources;
|
||||||
|
};
|
||||||
|
|
||||||
|
try stage.reflect_images_samplers(arena, compiler, resources);
|
||||||
|
try stage.reflect_uniform_blocks(arena, compiler, resources);
|
||||||
|
|
||||||
|
std.log.info("{s}", .{stage.source});
|
||||||
|
|
||||||
|
break: fragment_stage stage;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile(arena: *std.heap.ArenaAllocator, compiler: ext.spvc_compiler) Error![:0]const u8 {
|
||||||
|
const arena_allocator = arena.allocator();
|
||||||
|
var source: [*:0]const u8 = "";
|
||||||
|
|
||||||
|
try to_error(ext.spvc_compiler_compile(compiler, @ptrCast(&source)));
|
||||||
|
|
||||||
|
return arena_allocator.dupeZ(u8, std.mem.span(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callconv(.C) void {
|
||||||
|
std.debug.assert(userdata == null);
|
||||||
|
std.log.err("{s}", .{error_message});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(arena: *std.heap.ArenaAllocator, context: ext.spvc_context, target: Target, source: Source) Error!ext.spvc_compiler {
|
||||||
var parsed_ir: ext.spvc_parsed_ir = null;
|
var parsed_ir: ext.spvc_parsed_ir = null;
|
||||||
|
|
||||||
try to_error(ext.spvc_context_parse_spirv(self.context, spirv_ops.ptr, spirv_ops.len, &parsed_ir));
|
try to_error(ext.spvc_context_parse_spirv(context, source.ops.ptr, source.ops.len, &parsed_ir));
|
||||||
|
|
||||||
var compiler: ext.spvc_compiler = null;
|
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));
|
const spvc_target = switch (target) {
|
||||||
|
.glsl => ext.SPVC_BACKEND_GLSL,
|
||||||
|
};
|
||||||
|
|
||||||
|
try to_error(ext.spvc_context_create_compiler(context, spvc_target, parsed_ir, ext.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler));
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_build_combined_image_samplers(compiler));
|
try to_error(ext.spvc_compiler_build_combined_image_samplers(compiler));
|
||||||
|
|
||||||
var combined_image_samplers: []const ext.spvc_combined_image_sampler = &.{};
|
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));
|
try to_error(ext.spvc_compiler_get_combined_image_samplers(compiler, @ptrCast(&combined_image_samplers.ptr), &combined_image_samplers.len));
|
||||||
|
|
||||||
const arena_allocator = self.arena.allocator();
|
const arena_allocator = arena.allocator();
|
||||||
var binding: u32 = 0;
|
var binding: u32 = 0;
|
||||||
|
|
||||||
for (combined_image_samplers) |combined_image_sampler| {
|
for (combined_image_samplers) |combined_image_sampler| {
|
||||||
|
@ -286,83 +366,29 @@ pub const Unit = struct {
|
||||||
binding += 1;
|
binding += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
break: parse_and_configure compiler;
|
const option_values: []const struct {ext.spvc_compiler_option, c_uint} = switch (target) {
|
||||||
|
.glsl => &.{
|
||||||
|
.{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)},
|
||||||
|
.{ext.SPVC_COMPILER_OPTION_FLIP_VERTEX_Y, @intFromBool(true)},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_set_entry_point(compiler, "main", @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 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 compiler_options: ext.spvc_compiler_options = null;
|
var compiler_options: ext.spvc_compiler_options = null;
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_create_compiler_options(compiler, &compiler_options));
|
try to_error(ext.spvc_compiler_create_compiler_options(compiler, &compiler_options));
|
||||||
|
|
||||||
for (backend.option_values) |option_value| {
|
for (option_values) |option_value| {
|
||||||
const entry, const value = option_value;
|
const entry, const value = option_value;
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_options_set_uint(compiler_options, entry, value));
|
try to_error(ext.spvc_compiler_options_set_uint(compiler_options, entry, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
break: create compiler_options;
|
try to_error(ext.spvc_compiler_install_compiler_options(compiler, compiler_options));
|
||||||
}));
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_compile(compiler, @ptrCast(&shader.decompiled_source.ptr)));
|
return compiler;
|
||||||
|
|
||||||
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 {
|
|
||||||
ext.spvc_context_destroy(self.context);
|
|
||||||
self.arena.deinit();
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) 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 .{
|
|
||||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
|
||||||
.context = context,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
std.debug.assert(userdata == null);
|
|
||||||
std.log.err("{s}", .{error_message});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_error(result: ext.spvc_result) Error!void {
|
fn to_error(result: ext.spvc_result) Error!void {
|
||||||
|
|
Loading…
Reference in New Issue