Implement CRT shader effect support
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
56342d9d8e
commit
67dee07f8e
|
@ -0,0 +1,52 @@
|
||||||
|
#version 430
|
||||||
|
|
||||||
|
layout (binding = 0) uniform sampler2D sprite;
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 color;
|
||||||
|
layout (location = 1) in vec2 uv;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 texel;
|
||||||
|
|
||||||
|
layout (binding = 0) uniform Effect {
|
||||||
|
float screen_width;
|
||||||
|
float screen_height;
|
||||||
|
float time;
|
||||||
|
};
|
||||||
|
|
||||||
|
vec3 scanline(vec2 coord, vec3 screen)
|
||||||
|
{
|
||||||
|
screen.rgb -= sin((coord.y + (time * 29.0))) * 0.02;
|
||||||
|
return screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 crt(vec2 coord, float bend)
|
||||||
|
{
|
||||||
|
// put in symmetrical coords
|
||||||
|
coord = (coord - 0.5) * 2.0;
|
||||||
|
|
||||||
|
coord *= 1.0;
|
||||||
|
|
||||||
|
// deform coords
|
||||||
|
coord.x *= 1.0 + pow((abs(coord.y) / bend), 2.0);
|
||||||
|
coord.y *= 1.0 + pow((abs(coord.x) / bend), 2.0);
|
||||||
|
|
||||||
|
// transform back to 0.0 - 1.0 space
|
||||||
|
coord = (coord / 2.0) + 0.5;
|
||||||
|
|
||||||
|
return coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 crtCoords = crt(uv, 4.8);
|
||||||
|
|
||||||
|
// Split the color channels
|
||||||
|
texel.rgb = texture(sprite, crtCoords).rgb;
|
||||||
|
texel.a = 1;
|
||||||
|
|
||||||
|
// HACK: this bend produces a shitty moire pattern.
|
||||||
|
// Up the bend for the scanline
|
||||||
|
vec2 screenSpace = crtCoords * vec2(screen_width, screen_height);
|
||||||
|
texel.rgb = scanline(screenSpace, texel.rgb);
|
||||||
|
}
|
||||||
|
|
52
src/main.zig
52
src/main.zig
|
@ -9,11 +9,20 @@ const ChromaticAberration = extern struct {
|
||||||
padding: [12]u8 = undefined,
|
padding: [12]u8 = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CRT = extern struct {
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
time: f32,
|
||||||
|
padding: [4]u8 = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
const Actors = struct {
|
const Actors = struct {
|
||||||
instances: coral.stack.Sequential(@Vector(2, f32)) = .{.allocator = coral.heap.allocator},
|
instances: coral.stack.Sequential(@Vector(2, f32)) = .{.allocator = coral.heap.allocator},
|
||||||
body_texture: ona.gfx.Texture = .default,
|
body_texture: ona.gfx.Texture = .default,
|
||||||
render_texture: ona.gfx.Texture = .default,
|
render_texture: ona.gfx.Texture = .default,
|
||||||
ca_effect: ona.gfx.Effect = .default,
|
ca_effect: ona.gfx.Effect = .default,
|
||||||
|
crt_effect: ona.gfx.Effect = .default,
|
||||||
|
staging_texture: ona.gfx.Texture = .default,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Player = struct {
|
const Player = struct {
|
||||||
|
@ -44,6 +53,7 @@ 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");
|
actors.res.ca_effect = try assets.res.load_effect_file(coral.files.bundle, "./ca.frag.spv");
|
||||||
|
actors.res.crt_effect = try assets.res.load_effect_file(coral.files.bundle, "./crt.frag.spv");
|
||||||
|
|
||||||
try actors.res.instances.push_grow(.{0, 0});
|
try actors.res.instances.push_grow(.{0, 0});
|
||||||
}
|
}
|
||||||
|
@ -52,22 +62,46 @@ fn exit(actors: ona.Write(Actors)) void {
|
||||||
actors.res.instances.deinit();
|
actors.res.instances.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors)) !void {
|
fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors), app: ona.Read(ona.App)) !void {
|
||||||
try commands.set_effect(actors.res.ca_effect, ChromaticAberration{
|
try commands.set_target(.{
|
||||||
.effect_magnitude = 15.0,
|
.texture = actors.res.render_texture,
|
||||||
|
.clear_color = ona.gfx.colors.black,
|
||||||
|
.clear_depth = 0,
|
||||||
|
.clear_stencil = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (actors.res.instances.values) |instance| {
|
|
||||||
try commands.draw_texture(.{
|
try commands.draw_texture(.{
|
||||||
.texture = actors.res.body_texture,
|
.texture = .default,
|
||||||
|
|
||||||
.transform = .{
|
.transform = .{
|
||||||
.origin = instance,
|
.origin = .{1280 / 2, 720 / 2},
|
||||||
.xbasis = .{64, 0},
|
.xbasis = .{1280, 0},
|
||||||
.ybasis = .{0, 64},
|
.ybasis = .{0, 720},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try commands.set_effect(actors.res.crt_effect, CRT{
|
||||||
|
.width = 1280,
|
||||||
|
.height = 720,
|
||||||
|
.time = @floatCast(app.res.elapsed_time),
|
||||||
|
});
|
||||||
|
|
||||||
|
try commands.set_target(.{
|
||||||
|
.texture = .backbuffer,
|
||||||
|
.clear_color = null,
|
||||||
|
.clear_depth = null,
|
||||||
|
.clear_stencil = null,
|
||||||
|
});
|
||||||
|
|
||||||
|
try commands.draw_texture(.{
|
||||||
|
.texture = actors.res.render_texture,
|
||||||
|
|
||||||
|
.transform = .{
|
||||||
|
.origin = .{1280 / 2, 720 / 2},
|
||||||
|
.xbasis = .{1280, 0},
|
||||||
|
.ybasis = .{0, 720},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(player: ona.Read(Player), actors: ona.Write(Actors), mapping: ona.Read(ona.act.Mapping)) !void {
|
fn update(player: ona.Read(Player), actors: ona.Write(Actors), mapping: ona.Read(ona.act.Mapping)) !void {
|
||||||
|
|
|
@ -4,6 +4,7 @@ const flow = @import("flow");
|
||||||
|
|
||||||
events: *const Events,
|
events: *const Events,
|
||||||
target_frame_time: f64,
|
target_frame_time: f64,
|
||||||
|
elapsed_time: f64,
|
||||||
is_running: bool,
|
is_running: bool,
|
||||||
|
|
||||||
pub const Events = struct {
|
pub const Events = struct {
|
||||||
|
|
|
@ -17,7 +17,12 @@ fn Handle(comptime HandleDesc: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Texture = Handle(struct {
|
pub const Texture = enum (u32) {
|
||||||
|
default,
|
||||||
|
backbuffer,
|
||||||
|
_,
|
||||||
|
|
||||||
|
pub const Desc = struct {
|
||||||
format: Format,
|
format: Format,
|
||||||
access: Access,
|
access: Access,
|
||||||
|
|
||||||
|
@ -46,4 +51,6 @@ pub const Texture = Handle(struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub const Command = union (enum) {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SetTarget = struct {
|
pub const SetTarget = struct {
|
||||||
texture: ?handles.Texture = null,
|
texture: handles.Texture,
|
||||||
clear_color: ?lina.Color,
|
clear_color: ?lina.Color,
|
||||||
clear_depth: ?f32,
|
clear_depth: ?f32,
|
||||||
clear_stencil: ?u8,
|
clear_stencil: ?u8,
|
||||||
|
@ -135,7 +135,7 @@ const Frame = struct {
|
||||||
drawn_count: usize = 0,
|
drawn_count: usize = 0,
|
||||||
flushed_count: usize = 0,
|
flushed_count: usize = 0,
|
||||||
current_source_texture: handles.Texture = .default,
|
current_source_texture: handles.Texture = .default,
|
||||||
current_target_texture: ?handles.Texture = null,
|
current_target_texture: handles.Texture = .backbuffer,
|
||||||
current_effect: handles.Effect = .default,
|
current_effect: handles.Effect = .default,
|
||||||
|
|
||||||
const DrawTexture = extern struct {
|
const DrawTexture = extern struct {
|
||||||
|
@ -185,24 +185,27 @@ 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)).?.access) {
|
switch (pools.get_texture(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] = default_sampler;
|
||||||
},
|
},
|
||||||
|
|
||||||
.static => |static| {
|
.static => |static| {
|
||||||
bindings.fs.images[0] = static.image;
|
bindings.fs.images[0] = static.image;
|
||||||
bindings.fs.samplers[0] = static.sampler;
|
bindings.fs.samplers[0] = default_sampler;
|
||||||
|
},
|
||||||
|
|
||||||
|
.empty => {
|
||||||
|
@panic("Cannot render empty textures");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const effect = pools.effects.get(@intFromEnum(self.current_effect)).?;
|
const effect = pools.get_effect(self.current_effect).?;
|
||||||
|
|
||||||
sokol.gfx.applyPipeline(effect.pipeline);
|
sokol.gfx.applyPipeline(effect.pipeline);
|
||||||
|
|
||||||
if (self.current_target_texture) |target_texture| {
|
const texture = pools.get_texture(self.current_target_texture).?;
|
||||||
const texture = pools.textures.get(@intFromEnum(target_texture)).?;
|
|
||||||
|
|
||||||
sokol.gfx.applyUniforms(.VS, 0, sokol.gfx.asRange(&lina.orthographic_projection(-1.0, 1.0, .{
|
sokol.gfx.applyUniforms(.VS, 0, sokol.gfx.asRange(&lina.orthographic_projection(-1.0, 1.0, .{
|
||||||
.left = 0,
|
.left = 0,
|
||||||
|
@ -210,16 +213,10 @@ const Frame = struct {
|
||||||
.right = @floatFromInt(texture.width),
|
.right = @floatFromInt(texture.width),
|
||||||
.bottom = @floatFromInt(texture.height),
|
.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),
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (effect.properties.len != 0) {
|
||||||
sokol.gfx.applyUniforms(.FS, 0, sokol.gfx.asRange(effect.properties));
|
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;
|
||||||
|
@ -246,7 +243,7 @@ const Frame = struct {
|
||||||
|
|
||||||
self.current_effect = command.effect;
|
self.current_effect = command.effect;
|
||||||
|
|
||||||
if (pools.effects.get(@intFromEnum(self.current_effect))) |effect| {
|
if (pools.get_effect(self.current_effect)) |effect| {
|
||||||
@memcpy(effect.properties, command.properties);
|
@memcpy(effect.properties, command.properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,17 +277,13 @@ const Frame = struct {
|
||||||
pass.action.depth = .{.load_action = .LOAD};
|
pass.action.depth = .{.load_action = .LOAD};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.texture) |texture| {
|
pass.attachments = switch (pools.get_texture(self.current_target_texture).?.access) {
|
||||||
pass.attachments = switch (pools.textures.get(@intFromEnum(texture)).?.access) {
|
|
||||||
.static => @panic("Cannot render to static textures"),
|
.static => @panic("Cannot render to static textures"),
|
||||||
|
.empty => @panic("Cannot render to empty textures"),
|
||||||
.render => |render| render.attachments,
|
.render => |render| render.attachments,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.current_target_texture = command.texture;
|
self.current_target_texture = command.texture;
|
||||||
} else {
|
|
||||||
pass.swapchain = self.swapchain;
|
|
||||||
self.current_target_texture = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
sokol.gfx.beginPass(pass);
|
sokol.gfx.beginPass(pass);
|
||||||
}
|
}
|
||||||
|
@ -313,7 +306,7 @@ const Pools = struct {
|
||||||
|
|
||||||
const TexturePool = coral.Pool(resources.Texture);
|
const TexturePool = coral.Pool(resources.Texture);
|
||||||
|
|
||||||
fn create_effect(self: *Pools, desc: handles.Effect.Desc) !handles.Effect {
|
pub fn create_effect(self: *Pools, desc: handles.Effect.Desc) !handles.Effect {
|
||||||
var effect = try resources.Effect.init(desc);
|
var effect = try resources.Effect.init(desc);
|
||||||
|
|
||||||
errdefer effect.deinit();
|
errdefer effect.deinit();
|
||||||
|
@ -321,7 +314,7 @@ const Pools = struct {
|
||||||
return @enumFromInt(try self.effects.insert(effect));
|
return @enumFromInt(try self.effects.insert(effect));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_texture(self: *Pools, desc: handles.Texture.Desc) !handles.Texture {
|
pub fn create_texture(self: *Pools, desc: handles.Texture.Desc) !handles.Texture {
|
||||||
var texture = try resources.Texture.init(desc);
|
var texture = try resources.Texture.init(desc);
|
||||||
|
|
||||||
errdefer texture.deinit();
|
errdefer texture.deinit();
|
||||||
|
@ -329,7 +322,7 @@ const Pools = struct {
|
||||||
return @enumFromInt(try self.textures.insert(texture));
|
return @enumFromInt(try self.textures.insert(texture));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Pools) void {
|
pub fn deinit(self: *Pools) void {
|
||||||
var textures = self.textures.values();
|
var textures = self.textures.values();
|
||||||
|
|
||||||
while (textures.next()) |texture| {
|
while (textures.next()) |texture| {
|
||||||
|
@ -339,7 +332,7 @@ const Pools = struct {
|
||||||
self.textures.deinit();
|
self.textures.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_effect(self: *Pools, handle: handles.Effect) bool {
|
pub fn destroy_effect(self: *Pools, handle: handles.Effect) bool {
|
||||||
switch (handle) {
|
switch (handle) {
|
||||||
.default => {},
|
.default => {},
|
||||||
|
|
||||||
|
@ -355,7 +348,7 @@ const Pools = struct {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_texture(self: *Pools, handle: handles.Texture) bool {
|
pub fn destroy_texture(self: *Pools, handle: handles.Texture) bool {
|
||||||
switch (handle) {
|
switch (handle) {
|
||||||
.default => {},
|
.default => {},
|
||||||
|
|
||||||
|
@ -371,7 +364,15 @@ const Pools = struct {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(allocator: std.mem.Allocator) !Pools {
|
pub fn get_effect(self: *Pools, handle: handles.Effect) ?*resources.Effect {
|
||||||
|
return self.effects.get(@intFromEnum(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_texture(self: *Pools, handle: handles.Texture) ?*resources.Texture {
|
||||||
|
return self.textures.get(@intFromEnum(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) !Pools {
|
||||||
var pools = Pools{
|
var pools = Pools{
|
||||||
.effects = EffectPool.init(allocator),
|
.effects = EffectPool.init(allocator),
|
||||||
.textures = TexturePool.init(allocator),
|
.textures = TexturePool.init(allocator),
|
||||||
|
@ -381,31 +382,47 @@ const Pools = struct {
|
||||||
pools.deinit();
|
pools.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = try pools.create_effect(.{
|
const assert = struct {
|
||||||
.fragment_spirv_ops = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
fn is_handle(expected: anytype, actual: @TypeOf(expected)) void {
|
||||||
});
|
std.debug.assert(actual == expected);
|
||||||
|
}
|
||||||
const default_texture_data = [_]u32{
|
|
||||||
0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF,
|
|
||||||
0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF,
|
|
||||||
0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF,
|
|
||||||
0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF,
|
|
||||||
0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF,
|
|
||||||
0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF,
|
|
||||||
0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF,
|
|
||||||
0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = try pools.create_texture(.{
|
assert.is_handle(handles.Effect.default, try pools.create_effect(.{
|
||||||
|
.fragment_spirv_ops = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
||||||
|
}));
|
||||||
|
|
||||||
|
assert.is_handle(handles.Texture.default, try pools.create_texture(.{
|
||||||
.format = .rgba8,
|
.format = .rgba8,
|
||||||
|
|
||||||
.access = .{
|
.access = .{
|
||||||
.static = .{
|
.static = .{
|
||||||
.data = std.mem.asBytes(&default_texture_data),
|
.data = std.mem.asBytes(&[_]u32{
|
||||||
|
0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080,
|
||||||
|
0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000,
|
||||||
|
0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080,
|
||||||
|
0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000,
|
||||||
|
0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080,
|
||||||
|
0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000,
|
||||||
|
0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080,
|
||||||
|
0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000,
|
||||||
|
}),
|
||||||
|
|
||||||
.width = 8,
|
.width = 8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
assert.is_handle(handles.Texture.backbuffer, try pools.create_texture(.{
|
||||||
|
.format = .rgba8,
|
||||||
|
|
||||||
|
.access = .{
|
||||||
|
.render = .{
|
||||||
|
.width = 0,
|
||||||
|
.height = 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
return pools;
|
return pools;
|
||||||
}
|
}
|
||||||
|
@ -457,6 +474,8 @@ pub const Work = union (enum) {
|
||||||
var pending: coral.asyncio.BlockingQueue(1024, Work) = .{};
|
var pending: coral.asyncio.BlockingQueue(1024, Work) = .{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var default_sampler: sokol.gfx.Sampler = undefined;
|
||||||
|
|
||||||
pub fn enqueue_work(work: Work) void {
|
pub fn enqueue_work(work: Work) void {
|
||||||
Work.pending.enqueue(work);
|
Work.pending.enqueue(work);
|
||||||
}
|
}
|
||||||
|
@ -541,6 +560,8 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
.type = .VERTEXBUFFER,
|
.type = .VERTEXBUFFER,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
default_sampler = sokol.gfx.makeSampler(.{});
|
||||||
|
|
||||||
var has_commands_head: ?*Commands = null;
|
var has_commands_head: ?*Commands = null;
|
||||||
var has_commands_tail: ?*Commands = null;
|
var has_commands_tail: ?*Commands = null;
|
||||||
|
|
||||||
|
@ -588,6 +609,23 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
},
|
},
|
||||||
|
|
||||||
.render_frame => |render_frame| {
|
.render_frame => |render_frame| {
|
||||||
|
const backbuffer = pools.get_texture(.backbuffer).?;
|
||||||
|
|
||||||
|
if (backbuffer.width != render_frame.width or backbuffer.height != render_frame.height) {
|
||||||
|
backbuffer.deinit();
|
||||||
|
|
||||||
|
backbuffer.* = try resources.Texture.init(.{
|
||||||
|
.format = .rgba8,
|
||||||
|
|
||||||
|
.access = .{
|
||||||
|
.render = .{
|
||||||
|
.width = render_frame.width,
|
||||||
|
.height = render_frame.height,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var frame = Frame{
|
var frame = Frame{
|
||||||
.swapchain = .{
|
.swapchain = .{
|
||||||
.width = render_frame.width,
|
.width = render_frame.width,
|
||||||
|
@ -599,6 +637,51 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sokol.gfx.beginPass(pass: {
|
||||||
|
var pass = sokol.gfx.Pass{
|
||||||
|
.action = .{
|
||||||
|
.stencil = .{
|
||||||
|
.load_action = .CLEAR,
|
||||||
|
},
|
||||||
|
|
||||||
|
.depth = .{
|
||||||
|
.load_action = .CLEAR,
|
||||||
|
.clear_value = 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pass.action.colors[0] = .{
|
||||||
|
.load_action = .CLEAR,
|
||||||
|
.clear_value = @bitCast(render_frame.clear_color),
|
||||||
|
};
|
||||||
|
|
||||||
|
pass.attachments = pools.get_texture(.backbuffer).?.access.render.attachments;
|
||||||
|
|
||||||
|
break: pass pass;
|
||||||
|
});
|
||||||
|
|
||||||
|
var has_commands = has_commands_head;
|
||||||
|
|
||||||
|
while (has_commands) |commands| : (has_commands = commands.has_next) {
|
||||||
|
for (commands.submitted_commands()) |command| {
|
||||||
|
try command.process(&pools, &frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.flush(&pools);
|
||||||
|
|
||||||
|
if (frame.current_target_texture != .backbuffer) {
|
||||||
|
frame.set_target(&pools, .{
|
||||||
|
.texture = .backbuffer,
|
||||||
|
.clear_color = null,
|
||||||
|
.clear_depth = null,
|
||||||
|
.clear_stencil = null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sokol.gfx.endPass();
|
||||||
|
|
||||||
sokol.gfx.beginPass(swapchain_pass: {
|
sokol.gfx.beginPass(swapchain_pass: {
|
||||||
var pass = sokol.gfx.Pass{
|
var pass = sokol.gfx.Pass{
|
||||||
.swapchain = frame.swapchain,
|
.swapchain = frame.swapchain,
|
||||||
|
@ -609,29 +692,20 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pass.action.colors[0] = .{
|
pass.action.colors[0] = .{.load_action = .CLEAR};
|
||||||
.clear_value = @bitCast(render_frame.clear_color),
|
|
||||||
.load_action = .CLEAR,
|
|
||||||
};
|
|
||||||
|
|
||||||
break: swapchain_pass pass;
|
break: swapchain_pass pass;
|
||||||
});
|
});
|
||||||
|
|
||||||
var has_commands = has_commands_head;
|
try frame.draw_texture(&pools, .{
|
||||||
|
.texture = .backbuffer,
|
||||||
|
|
||||||
while (has_commands) |commands| : (has_commands = commands.has_next) {
|
.transform = .{
|
||||||
for (commands.submitted_commands()) |command| {
|
.origin = .{@as(f32, @floatFromInt(render_frame.width)) / 2, @as(f32, @floatFromInt(render_frame.height)) / 2},
|
||||||
try command.process(&pools, &frame);
|
.xbasis = .{@floatFromInt(render_frame.width), 0},
|
||||||
}
|
.ybasis = .{0, @floatFromInt(render_frame.height)},
|
||||||
|
},
|
||||||
if (frame.current_target_texture != .default) {
|
|
||||||
frame.set_target(&pools, .{
|
|
||||||
.clear_color = null,
|
|
||||||
.clear_depth = null,
|
|
||||||
.clear_stencil = null,
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.flush(&pools);
|
frame.flush(&pools);
|
||||||
sokol.gfx.endPass();
|
sokol.gfx.endPass();
|
||||||
|
|
|
@ -123,11 +123,11 @@ pub const Effect = struct {
|
||||||
.float2 => .FLOAT2,
|
.float2 => .FLOAT2,
|
||||||
.float3 => .FLOAT3,
|
.float3 => .FLOAT3,
|
||||||
.float4 => .FLOAT4,
|
.float4 => .FLOAT4,
|
||||||
.int => .INT,
|
.integer => .INT,
|
||||||
.int2 => .INT2,
|
.integer2 => .INT2,
|
||||||
.int3 => .INT3,
|
.integer3 => .INT3,
|
||||||
.int4 => .INT4,
|
.integer4 => .INT4,
|
||||||
.mat4 => .MAT4,
|
.matrix4 => .MAT4,
|
||||||
},
|
},
|
||||||
|
|
||||||
.name = uniform.name,
|
.name = uniform.name,
|
||||||
|
@ -244,19 +244,18 @@ pub const Texture = struct {
|
||||||
access: Access,
|
access: Access,
|
||||||
|
|
||||||
pub const Access = union (enum) {
|
pub const Access = union (enum) {
|
||||||
|
empty,
|
||||||
render: RenderAccess,
|
render: RenderAccess,
|
||||||
static: StaticAccess,
|
static: StaticAccess,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RenderAccess = struct {
|
pub const RenderAccess = struct {
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const StaticAccess = struct {
|
pub const StaticAccess = struct {
|
||||||
sampler: sokol.gfx.Sampler,
|
|
||||||
image: sokol.gfx.Image,
|
image: sokol.gfx.Image,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -265,14 +264,14 @@ pub const Texture = struct {
|
||||||
.render => |render| {
|
.render => |render| {
|
||||||
sokol.gfx.destroyImage(render.color_image);
|
sokol.gfx.destroyImage(render.color_image);
|
||||||
sokol.gfx.destroyImage(render.depth_image);
|
sokol.gfx.destroyImage(render.depth_image);
|
||||||
sokol.gfx.destroySampler(render.sampler);
|
|
||||||
sokol.gfx.destroyAttachments(render.attachments);
|
sokol.gfx.destroyAttachments(render.attachments);
|
||||||
},
|
},
|
||||||
|
|
||||||
.static => |static| {
|
.static => |static| {
|
||||||
sokol.gfx.destroyImage(static.image);
|
sokol.gfx.destroyImage(static.image);
|
||||||
sokol.gfx.destroySampler(static.sampler);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.empty => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
|
@ -286,6 +285,14 @@ pub const Texture = struct {
|
||||||
|
|
||||||
switch (desc.access) {
|
switch (desc.access) {
|
||||||
.render => |render| {
|
.render => |render| {
|
||||||
|
if (render.width == 0 or render.height == 0) {
|
||||||
|
return .{
|
||||||
|
.width = render.width,
|
||||||
|
.height = render.height,
|
||||||
|
.access = .empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const color_image = sokol.gfx.makeImage(.{
|
const color_image = sokol.gfx.makeImage(.{
|
||||||
.pixel_format = pixel_format,
|
.pixel_format = pixel_format,
|
||||||
.width = render.width,
|
.width = render.width,
|
||||||
|
@ -314,8 +321,6 @@ pub const Texture = struct {
|
||||||
break: attachments_desc attachments_desc;
|
break: attachments_desc attachments_desc;
|
||||||
});
|
});
|
||||||
|
|
||||||
const sampler = sokol.gfx.makeSampler(.{});
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.width = render.width,
|
.width = render.width,
|
||||||
.height = render.height,
|
.height = render.height,
|
||||||
|
@ -323,11 +328,10 @@ pub const Texture = struct {
|
||||||
.access = .{
|
.access = .{
|
||||||
.render = .{
|
.render = .{
|
||||||
.attachments = attachments,
|
.attachments = attachments,
|
||||||
.sampler = sampler,
|
|
||||||
.color_image = color_image,
|
.color_image = color_image,
|
||||||
.depth_image = depth_image,
|
.depth_image = depth_image,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -336,6 +340,14 @@ pub const Texture = struct {
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (static.width == 0 or height == 0) {
|
||||||
|
return .{
|
||||||
|
.width = static.width,
|
||||||
|
.height = height,
|
||||||
|
.access = .empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const image = sokol.gfx.makeImage(image_desc: {
|
const image = sokol.gfx.makeImage(image_desc: {
|
||||||
var image_desc = sokol.gfx.ImageDesc{
|
var image_desc = sokol.gfx.ImageDesc{
|
||||||
.height = height,
|
.height = height,
|
||||||
|
@ -348,10 +360,7 @@ pub const Texture = struct {
|
||||||
break: image_desc image_desc;
|
break: image_desc image_desc;
|
||||||
});
|
});
|
||||||
|
|
||||||
const sampler = sokol.gfx.makeSampler(.{});
|
|
||||||
|
|
||||||
errdefer {
|
errdefer {
|
||||||
sokol.gfx.destroySampler(sampler);
|
|
||||||
sokol.gfx.destroyImage(image);
|
sokol.gfx.destroyImage(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +371,6 @@ pub const Texture = struct {
|
||||||
.access = .{
|
.access = .{
|
||||||
.static = .{
|
.static = .{
|
||||||
.image = image,
|
.image = image,
|
||||||
.sampler = sampler,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,8 +21,9 @@ void main() {
|
||||||
const vec2 world_position = instance_origin + mesh_xy.x * instance_xbasis + mesh_xy.y * instance_ybasis;
|
const vec2 world_position = instance_origin + mesh_xy.x * instance_xbasis + mesh_xy.y * instance_ybasis;
|
||||||
const vec2 projected_position = (projection_matrix * vec4(world_position, 0, 1)).xy;
|
const vec2 projected_position = (projection_matrix * vec4(world_position, 0, 1)).xy;
|
||||||
const vec2 rect_size = instance_rect.zw - instance_rect.xy;
|
const vec2 rect_size = instance_rect.zw - instance_rect.xy;
|
||||||
|
const vec4 screen_position = vec4(projected_position, instance_depth, 1.0);
|
||||||
|
|
||||||
gl_Position = vec4(projected_position, instance_depth, 1.0);
|
gl_Position = screen_position;
|
||||||
color = instance_tint;
|
color = instance_tint;
|
||||||
uv = instance_rect.xy + (mesh_uv * rect_size);
|
uv = instance_rect.xy + (mesh_uv * rect_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,11 +67,11 @@ pub const Stage = struct {
|
||||||
float2,
|
float2,
|
||||||
float3,
|
float3,
|
||||||
float4,
|
float4,
|
||||||
int,
|
integer,
|
||||||
int2,
|
integer2,
|
||||||
int3,
|
integer3,
|
||||||
int4,
|
integer4,
|
||||||
mat4,
|
matrix4,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,29 +82,27 @@ pub const Stage = struct {
|
||||||
pub const max_uniforms = 16;
|
pub const max_uniforms = 16;
|
||||||
|
|
||||||
pub fn size(self: UniformBlock) usize {
|
pub fn size(self: UniformBlock) usize {
|
||||||
const alignment: usize = switch (self.layout) {
|
|
||||||
.std140 => 16,
|
|
||||||
};
|
|
||||||
|
|
||||||
var accumulated_size: usize = 0;
|
var accumulated_size: usize = 0;
|
||||||
|
|
||||||
for (self.uniforms) |uniform| {
|
for (self.uniforms) |uniform| {
|
||||||
const type_size = @max(1, uniform.len) * @as(usize, switch (uniform.type) {
|
accumulated_size += @max(1, uniform.len) * @as(usize, switch (uniform.type) {
|
||||||
.float => 4,
|
.float => 4,
|
||||||
.float2 => 8,
|
.float2 => 8,
|
||||||
.float3 => 12,
|
.float3 => 12,
|
||||||
.float4 => 16,
|
.float4 => 16,
|
||||||
.int => 4,
|
.integer => 4,
|
||||||
.int2 => 8,
|
.integer2 => 8,
|
||||||
.int3 => 12,
|
.integer3 => 12,
|
||||||
.int4 => 16,
|
.integer4 => 16,
|
||||||
.mat4 => 64,
|
.matrix4 => 64,
|
||||||
});
|
});
|
||||||
|
|
||||||
accumulated_size += (type_size + (alignment - 1)) & ~(alignment - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return accumulated_size;
|
const alignment: usize = switch (self.layout) {
|
||||||
|
.std140 => 16,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (accumulated_size + (alignment - 1)) & ~(alignment - 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -212,7 +210,7 @@ pub const Stage = struct {
|
||||||
.type = try switch (ext.spvc_type_get_basetype(member_type_handle)) {
|
.type = try switch (ext.spvc_type_get_basetype(member_type_handle)) {
|
||||||
ext.SPVC_BASETYPE_FP32 => switch (ext.spvc_type_get_vector_size(member_type_handle)) {
|
ext.SPVC_BASETYPE_FP32 => switch (ext.spvc_type_get_vector_size(member_type_handle)) {
|
||||||
4 => switch (ext.spvc_type_get_columns(member_type_handle)) {
|
4 => switch (ext.spvc_type_get_columns(member_type_handle)) {
|
||||||
4 => Uniform.Type.mat4,
|
4 => Uniform.Type.matrix4,
|
||||||
1 => Uniform.Type.float4,
|
1 => Uniform.Type.float4,
|
||||||
else => error.UnsupportedSPIRV,
|
else => error.UnsupportedSPIRV,
|
||||||
},
|
},
|
||||||
|
@ -224,10 +222,10 @@ pub const Stage = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
ext.SPVC_BASETYPE_INT32 => try switch (ext.spvc_type_get_vector_size(member_type_handle)) {
|
ext.SPVC_BASETYPE_INT32 => try switch (ext.spvc_type_get_vector_size(member_type_handle)) {
|
||||||
1 => Uniform.Type.int,
|
1 => Uniform.Type.integer,
|
||||||
2 => Uniform.Type.int2,
|
2 => Uniform.Type.integer2,
|
||||||
3 => Uniform.Type.int3,
|
3 => Uniform.Type.integer3,
|
||||||
4 => Uniform.Type.int4,
|
4 => Uniform.Type.integer4,
|
||||||
else => error.UnsupportedSPIRV,
|
else => error.UnsupportedSPIRV,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
||||||
const app = try world.set_get_resource(App{
|
const app = try world.set_get_resource(App{
|
||||||
.events = &events,
|
.events = &events,
|
||||||
.target_frame_time = 1.0 / @as(f64, @floatFromInt(options.tick_rate)),
|
.target_frame_time = 1.0 / @as(f64, @floatFromInt(options.tick_rate)),
|
||||||
|
.elapsed_time = 0,
|
||||||
.is_running = true,
|
.is_running = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -85,7 +86,8 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
||||||
try setup(&world, events);
|
try setup(&world, events);
|
||||||
try world.run_event(events.load);
|
try world.run_event(events.load);
|
||||||
|
|
||||||
var ticks_previous = std.time.milliTimestamp();
|
const ticks_initial = std.time.milliTimestamp();
|
||||||
|
var ticks_previous = ticks_initial;
|
||||||
var accumulated_time = @as(f64, 0);
|
var accumulated_time = @as(f64, 0);
|
||||||
|
|
||||||
while (app.is_running) {
|
while (app.is_running) {
|
||||||
|
@ -93,6 +95,7 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
||||||
const milliseconds_per_second = 1000.0;
|
const milliseconds_per_second = 1000.0;
|
||||||
const delta_time = @as(f64, @floatFromInt(ticks_current - ticks_previous)) / milliseconds_per_second;
|
const delta_time = @as(f64, @floatFromInt(ticks_current - ticks_previous)) / milliseconds_per_second;
|
||||||
|
|
||||||
|
app.elapsed_time = @as(f64, @floatFromInt(ticks_current - ticks_initial)) / milliseconds_per_second;
|
||||||
ticks_previous = ticks_current;
|
ticks_previous = ticks_current;
|
||||||
accumulated_time += delta_time;
|
accumulated_time += delta_time;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue