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);
|
||||
}
|
||||
|
60
src/main.zig
60
src/main.zig
|
@ -9,11 +9,20 @@ const ChromaticAberration = extern struct {
|
|||
padding: [12]u8 = undefined,
|
||||
};
|
||||
|
||||
const CRT = extern struct {
|
||||
width: f32,
|
||||
height: f32,
|
||||
time: f32,
|
||||
padding: [4]u8 = undefined,
|
||||
};
|
||||
|
||||
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,
|
||||
crt_effect: ona.gfx.Effect = .default,
|
||||
staging_texture: ona.gfx.Texture = .default,
|
||||
};
|
||||
|
||||
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.crt_effect = try assets.res.load_effect_file(coral.files.bundle, "./crt.frag.spv");
|
||||
|
||||
try actors.res.instances.push_grow(.{0, 0});
|
||||
}
|
||||
|
@ -52,22 +62,46 @@ fn exit(actors: ona.Write(Actors)) void {
|
|||
actors.res.instances.deinit();
|
||||
}
|
||||
|
||||
fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors)) !void {
|
||||
try commands.set_effect(actors.res.ca_effect, ChromaticAberration{
|
||||
.effect_magnitude = 15.0,
|
||||
fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors), app: ona.Read(ona.App)) !void {
|
||||
try commands.set_target(.{
|
||||
.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(.{
|
||||
.texture = actors.res.body_texture,
|
||||
try commands.draw_texture(.{
|
||||
.texture = .default,
|
||||
|
||||
.transform = .{
|
||||
.origin = instance,
|
||||
.xbasis = .{64, 0},
|
||||
.ybasis = .{0, 64},
|
||||
},
|
||||
});
|
||||
}
|
||||
.transform = .{
|
||||
.origin = .{1280 / 2, 720 / 2},
|
||||
.xbasis = .{1280, 0},
|
||||
.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 {
|
||||
|
|
|
@ -4,6 +4,7 @@ const flow = @import("flow");
|
|||
|
||||
events: *const Events,
|
||||
target_frame_time: f64,
|
||||
elapsed_time: f64,
|
||||
is_running: bool,
|
||||
|
||||
pub const Events = struct {
|
||||
|
|
|
@ -17,33 +17,40 @@ fn Handle(comptime HandleDesc: type) type {
|
|||
};
|
||||
}
|
||||
|
||||
pub const Texture = Handle(struct {
|
||||
format: Format,
|
||||
access: Access,
|
||||
pub const Texture = enum (u32) {
|
||||
default,
|
||||
backbuffer,
|
||||
_,
|
||||
|
||||
pub const Access = union (enum) {
|
||||
static: Static,
|
||||
render: Render,
|
||||
pub const Desc = struct {
|
||||
format: Format,
|
||||
access: Access,
|
||||
|
||||
pub const Static = struct {
|
||||
width: u16,
|
||||
data: []const coral.io.Byte,
|
||||
};
|
||||
pub const Access = union (enum) {
|
||||
static: Static,
|
||||
render: Render,
|
||||
|
||||
pub const Render = struct {
|
||||
width: u16,
|
||||
height: u16,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Format = enum {
|
||||
rgba8,
|
||||
bgra8,
|
||||
|
||||
pub fn byte_size(self: Format) usize {
|
||||
return switch (self) {
|
||||
.rgba8, .bgra8 => 4,
|
||||
pub const Static = struct {
|
||||
width: u16,
|
||||
data: []const coral.io.Byte,
|
||||
};
|
||||
}
|
||||
|
||||
pub const Render = struct {
|
||||
width: u16,
|
||||
height: u16,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Format = enum {
|
||||
rgba8,
|
||||
bgra8,
|
||||
|
||||
pub fn byte_size(self: Format) usize {
|
||||
return switch (self) {
|
||||
.rgba8, .bgra8 => 4,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ pub const Command = union (enum) {
|
|||
};
|
||||
|
||||
pub const SetTarget = struct {
|
||||
texture: ?handles.Texture = null,
|
||||
texture: handles.Texture,
|
||||
clear_color: ?lina.Color,
|
||||
clear_depth: ?f32,
|
||||
clear_stencil: ?u8,
|
||||
|
@ -135,7 +135,7 @@ const Frame = struct {
|
|||
drawn_count: usize = 0,
|
||||
flushed_count: usize = 0,
|
||||
current_source_texture: handles.Texture = .default,
|
||||
current_target_texture: ?handles.Texture = null,
|
||||
current_target_texture: handles.Texture = .backbuffer,
|
||||
current_effect: handles.Effect = .default,
|
||||
|
||||
const DrawTexture = extern struct {
|
||||
|
@ -185,42 +185,39 @@ const Frame = struct {
|
|||
|
||||
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| {
|
||||
bindings.fs.images[0] = render.color_image;
|
||||
bindings.fs.samplers[0] = render.sampler;
|
||||
bindings.fs.samplers[0] = default_sampler;
|
||||
},
|
||||
|
||||
.static => |static| {
|
||||
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);
|
||||
|
||||
if (self.current_target_texture) |target_texture| {
|
||||
const texture = pools.textures.get(@intFromEnum(target_texture)).?;
|
||||
const texture = pools.get_texture(self.current_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(.VS, 0, sokol.gfx.asRange(&lina.orthographic_projection(-1.0, 1.0, .{
|
||||
.left = 0,
|
||||
.top = 0,
|
||||
.right = @floatFromInt(texture.width),
|
||||
.bottom = @floatFromInt(texture.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) {
|
||||
const buffer_index = self.flushed_count / batches_per_buffer;
|
||||
const buffer_offset = self.flushed_count % batches_per_buffer;
|
||||
|
@ -246,7 +243,7 @@ const Frame = struct {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -280,17 +277,13 @@ const Frame = struct {
|
|||
pass.action.depth = .{.load_action = .LOAD};
|
||||
}
|
||||
|
||||
if (command.texture) |texture| {
|
||||
pass.attachments = switch (pools.textures.get(@intFromEnum(texture)).?.access) {
|
||||
.static => @panic("Cannot render to static textures"),
|
||||
.render => |render| render.attachments,
|
||||
};
|
||||
pass.attachments = switch (pools.get_texture(self.current_target_texture).?.access) {
|
||||
.static => @panic("Cannot render to static textures"),
|
||||
.empty => @panic("Cannot render to empty textures"),
|
||||
.render => |render| render.attachments,
|
||||
};
|
||||
|
||||
self.current_target_texture = command.texture;
|
||||
} else {
|
||||
pass.swapchain = self.swapchain;
|
||||
self.current_target_texture = null;
|
||||
}
|
||||
self.current_target_texture = command.texture;
|
||||
|
||||
sokol.gfx.beginPass(pass);
|
||||
}
|
||||
|
@ -313,7 +306,7 @@ const Pools = struct {
|
|||
|
||||
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);
|
||||
|
||||
errdefer effect.deinit();
|
||||
|
@ -321,7 +314,7 @@ const Pools = struct {
|
|||
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);
|
||||
|
||||
errdefer texture.deinit();
|
||||
|
@ -329,7 +322,7 @@ const Pools = struct {
|
|||
return @enumFromInt(try self.textures.insert(texture));
|
||||
}
|
||||
|
||||
fn deinit(self: *Pools) void {
|
||||
pub fn deinit(self: *Pools) void {
|
||||
var textures = self.textures.values();
|
||||
|
||||
while (textures.next()) |texture| {
|
||||
|
@ -339,7 +332,7 @@ const Pools = struct {
|
|||
self.textures.deinit();
|
||||
}
|
||||
|
||||
fn destroy_effect(self: *Pools, handle: handles.Effect) bool {
|
||||
pub fn destroy_effect(self: *Pools, handle: handles.Effect) bool {
|
||||
switch (handle) {
|
||||
.default => {},
|
||||
|
||||
|
@ -355,7 +348,7 @@ const Pools = struct {
|
|||
return true;
|
||||
}
|
||||
|
||||
fn destroy_texture(self: *Pools, handle: handles.Texture) bool {
|
||||
pub fn destroy_texture(self: *Pools, handle: handles.Texture) bool {
|
||||
switch (handle) {
|
||||
.default => {},
|
||||
|
||||
|
@ -371,7 +364,15 @@ const Pools = struct {
|
|||
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{
|
||||
.effects = EffectPool.init(allocator),
|
||||
.textures = TexturePool.init(allocator),
|
||||
|
@ -381,31 +382,47 @@ const Pools = struct {
|
|||
pools.deinit();
|
||||
}
|
||||
|
||||
_ = try pools.create_effect(.{
|
||||
.fragment_spirv_ops = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
||||
});
|
||||
|
||||
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,
|
||||
const assert = struct {
|
||||
fn is_handle(expected: anytype, actual: @TypeOf(expected)) void {
|
||||
std.debug.assert(actual == expected);
|
||||
}
|
||||
};
|
||||
|
||||
_ = 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,
|
||||
|
||||
.access = .{
|
||||
.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,
|
||||
},
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
assert.is_handle(handles.Texture.backbuffer, try pools.create_texture(.{
|
||||
.format = .rgba8,
|
||||
|
||||
.access = .{
|
||||
.render = .{
|
||||
.width = 0,
|
||||
.height = 0,
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
return pools;
|
||||
}
|
||||
|
@ -457,6 +474,8 @@ pub const Work = union (enum) {
|
|||
var pending: coral.asyncio.BlockingQueue(1024, Work) = .{};
|
||||
};
|
||||
|
||||
var default_sampler: sokol.gfx.Sampler = undefined;
|
||||
|
||||
pub fn enqueue_work(work: Work) void {
|
||||
Work.pending.enqueue(work);
|
||||
}
|
||||
|
@ -541,6 +560,8 @@ fn run(window: *ext.SDL_Window) !void {
|
|||
.type = .VERTEXBUFFER,
|
||||
});
|
||||
|
||||
default_sampler = sokol.gfx.makeSampler(.{});
|
||||
|
||||
var has_commands_head: ?*Commands = null;
|
||||
var has_commands_tail: ?*Commands = null;
|
||||
|
||||
|
@ -588,6 +609,23 @@ fn run(window: *ext.SDL_Window) !void {
|
|||
},
|
||||
|
||||
.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{
|
||||
.swapchain = .{
|
||||
.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: {
|
||||
var pass = sokol.gfx.Pass{
|
||||
.swapchain = frame.swapchain,
|
||||
|
@ -609,29 +692,20 @@ fn run(window: *ext.SDL_Window) !void {
|
|||
},
|
||||
};
|
||||
|
||||
pass.action.colors[0] = .{
|
||||
.clear_value = @bitCast(render_frame.clear_color),
|
||||
.load_action = .CLEAR,
|
||||
};
|
||||
pass.action.colors[0] = .{.load_action = .CLEAR};
|
||||
|
||||
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) {
|
||||
for (commands.submitted_commands()) |command| {
|
||||
try command.process(&pools, &frame);
|
||||
}
|
||||
|
||||
if (frame.current_target_texture != .default) {
|
||||
frame.set_target(&pools, .{
|
||||
.clear_color = null,
|
||||
.clear_depth = null,
|
||||
.clear_stencil = null,
|
||||
});
|
||||
}
|
||||
}
|
||||
.transform = .{
|
||||
.origin = .{@as(f32, @floatFromInt(render_frame.width)) / 2, @as(f32, @floatFromInt(render_frame.height)) / 2},
|
||||
.xbasis = .{@floatFromInt(render_frame.width), 0},
|
||||
.ybasis = .{0, @floatFromInt(render_frame.height)},
|
||||
},
|
||||
});
|
||||
|
||||
frame.flush(&pools);
|
||||
sokol.gfx.endPass();
|
||||
|
|
|
@ -123,11 +123,11 @@ pub const Effect = struct {
|
|||
.float2 => .FLOAT2,
|
||||
.float3 => .FLOAT3,
|
||||
.float4 => .FLOAT4,
|
||||
.int => .INT,
|
||||
.int2 => .INT2,
|
||||
.int3 => .INT3,
|
||||
.int4 => .INT4,
|
||||
.mat4 => .MAT4,
|
||||
.integer => .INT,
|
||||
.integer2 => .INT2,
|
||||
.integer3 => .INT3,
|
||||
.integer4 => .INT4,
|
||||
.matrix4 => .MAT4,
|
||||
},
|
||||
|
||||
.name = uniform.name,
|
||||
|
@ -244,19 +244,18 @@ pub const Texture = struct {
|
|||
access: Access,
|
||||
|
||||
pub const Access = union (enum) {
|
||||
empty,
|
||||
render: RenderAccess,
|
||||
static: StaticAccess,
|
||||
};
|
||||
|
||||
pub const RenderAccess = struct {
|
||||
sampler: sokol.gfx.Sampler,
|
||||
color_image: sokol.gfx.Image,
|
||||
depth_image: sokol.gfx.Image,
|
||||
attachments: sokol.gfx.Attachments,
|
||||
};
|
||||
|
||||
pub const StaticAccess = struct {
|
||||
sampler: sokol.gfx.Sampler,
|
||||
image: sokol.gfx.Image,
|
||||
};
|
||||
|
||||
|
@ -265,14 +264,14 @@ pub const Texture = struct {
|
|||
.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);
|
||||
},
|
||||
|
||||
.empty => {},
|
||||
}
|
||||
|
||||
self.* = undefined;
|
||||
|
@ -286,6 +285,14 @@ pub const Texture = struct {
|
|||
|
||||
switch (desc.access) {
|
||||
.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(.{
|
||||
.pixel_format = pixel_format,
|
||||
.width = render.width,
|
||||
|
@ -314,8 +321,6 @@ pub const Texture = struct {
|
|||
break: attachments_desc attachments_desc;
|
||||
});
|
||||
|
||||
const sampler = sokol.gfx.makeSampler(.{});
|
||||
|
||||
return .{
|
||||
.width = render.width,
|
||||
.height = render.height,
|
||||
|
@ -323,11 +328,10 @@ pub const Texture = struct {
|
|||
.access = .{
|
||||
.render = .{
|
||||
.attachments = attachments,
|
||||
.sampler = sampler,
|
||||
.color_image = color_image,
|
||||
.depth_image = depth_image,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -336,6 +340,14 @@ pub const Texture = struct {
|
|||
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: {
|
||||
var image_desc = sokol.gfx.ImageDesc{
|
||||
.height = height,
|
||||
|
@ -348,10 +360,7 @@ pub const Texture = struct {
|
|||
break: image_desc image_desc;
|
||||
});
|
||||
|
||||
const sampler = sokol.gfx.makeSampler(.{});
|
||||
|
||||
errdefer {
|
||||
sokol.gfx.destroySampler(sampler);
|
||||
sokol.gfx.destroyImage(image);
|
||||
}
|
||||
|
||||
|
@ -362,7 +371,6 @@ pub const Texture = struct {
|
|||
.access = .{
|
||||
.static = .{
|
||||
.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 projected_position = (projection_matrix * vec4(world_position, 0, 1)).xy;
|
||||
const vec2 rect_size = instance_rect.zw - instance_rect.xy;
|
||||
const vec4 screen_position = vec4(projected_position, instance_depth, 1.0);
|
||||
|
||||
gl_Position = vec4(projected_position, instance_depth, 1.0);
|
||||
gl_Position = screen_position;
|
||||
color = instance_tint;
|
||||
uv = instance_rect.xy + (mesh_uv * rect_size);
|
||||
}
|
||||
|
|
|
@ -67,11 +67,11 @@ pub const Stage = struct {
|
|||
float2,
|
||||
float3,
|
||||
float4,
|
||||
int,
|
||||
int2,
|
||||
int3,
|
||||
int4,
|
||||
mat4,
|
||||
integer,
|
||||
integer2,
|
||||
integer3,
|
||||
integer4,
|
||||
matrix4,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -82,29 +82,27 @@ pub const Stage = struct {
|
|||
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) {
|
||||
accumulated_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,
|
||||
.integer => 4,
|
||||
.integer2 => 8,
|
||||
.integer3 => 12,
|
||||
.integer4 => 16,
|
||||
.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)) {
|
||||
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 => Uniform.Type.mat4,
|
||||
4 => Uniform.Type.matrix4,
|
||||
1 => Uniform.Type.float4,
|
||||
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)) {
|
||||
1 => Uniform.Type.int,
|
||||
2 => Uniform.Type.int2,
|
||||
3 => Uniform.Type.int3,
|
||||
4 => Uniform.Type.int4,
|
||||
1 => Uniform.Type.integer,
|
||||
2 => Uniform.Type.integer2,
|
||||
3 => Uniform.Type.integer3,
|
||||
4 => Uniform.Type.integer4,
|
||||
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{
|
||||
.events = &events,
|
||||
.target_frame_time = 1.0 / @as(f64, @floatFromInt(options.tick_rate)),
|
||||
.elapsed_time = 0,
|
||||
.is_running = true,
|
||||
});
|
||||
|
||||
|
@ -85,7 +86,8 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
|||
try setup(&world, events);
|
||||
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);
|
||||
|
||||
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 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;
|
||||
accumulated_time += delta_time;
|
||||
|
||||
|
|
Loading…
Reference in New Issue