From 67dee07f8e13cc5b129b8185f9099d9315e481e3 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sun, 21 Jul 2024 18:59:28 +0100 Subject: [PATCH] Implement CRT shader effect support --- debug/crt.frag | 52 +++++++ src/main.zig | 60 ++++++-- src/ona/App.zig | 1 + src/ona/gfx/handles.zig | 57 +++---- src/ona/gfx/rendering.zig | 222 ++++++++++++++++++---------- src/ona/gfx/resources.zig | 42 +++--- src/ona/gfx/shaders/2d_default.vert | 3 +- src/ona/gfx/spirv.zig | 44 +++--- src/ona/ona.zig | 5 +- 9 files changed, 332 insertions(+), 154 deletions(-) create mode 100644 debug/crt.frag diff --git a/debug/crt.frag b/debug/crt.frag new file mode 100644 index 0000000..682f44e --- /dev/null +++ b/debug/crt.frag @@ -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); +} + diff --git a/src/main.zig b/src/main.zig index d9139d5..4d84891 100644 --- a/src/main.zig +++ b/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 { diff --git a/src/ona/App.zig b/src/ona/App.zig index e985626..ab428d2 100644 --- a/src/ona/App.zig +++ b/src/ona/App.zig @@ -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 { diff --git a/src/ona/gfx/handles.zig b/src/ona/gfx/handles.zig index 942134f..ac1680d 100644 --- a/src/ona/gfx/handles.zig +++ b/src/ona/gfx/handles.zig @@ -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, + }; + } + }; }; -}); +}; + diff --git a/src/ona/gfx/rendering.zig b/src/ona/gfx/rendering.zig index 4d53f11..95f19fb 100644 --- a/src/ona/gfx/rendering.zig +++ b/src/ona/gfx/rendering.zig @@ -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(); diff --git a/src/ona/gfx/resources.zig b/src/ona/gfx/resources.zig index 7a34da9..ca1e3e1 100644 --- a/src/ona/gfx/resources.zig +++ b/src/ona/gfx/resources.zig @@ -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, }, }, }; diff --git a/src/ona/gfx/shaders/2d_default.vert b/src/ona/gfx/shaders/2d_default.vert index 4c4bb5e..d5045cb 100644 --- a/src/ona/gfx/shaders/2d_default.vert +++ b/src/ona/gfx/shaders/2d_default.vert @@ -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); } diff --git a/src/ona/gfx/spirv.zig b/src/ona/gfx/spirv.zig index 6405c68..def2040 100644 --- a/src/ona/gfx/spirv.zig +++ b/src/ona/gfx/spirv.zig @@ -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, }, diff --git a/src/ona/ona.zig b/src/ona/ona.zig index c3b51ea..da5b237 100644 --- a/src/ona/ona.zig +++ b/src/ona/ona.zig @@ -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;