From 4b59b2d387706a47917bcb9a33af21ac3457d1fb Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Mon, 18 Jul 2022 03:14:55 -0700 Subject: [PATCH] lots more goodies and compiling again --- src/math/float4.zig | 89 ++++++++++++++++- src/math/float4x4.zig | 63 +++++++++++- src/math/quat.zig | 29 ++++++ src/rendering/Camera.zig | 32 ++++++ src/rendering/vulkan/Bindings.zig | 102 +++++++++++++++++++ src/rendering/vulkan/Command.zig | 41 ++++++-- src/rendering/vulkan/Context.zig | 3 +- src/rendering/vulkan/DepthBuffer.zig | 0 src/rendering/vulkan/MainPass.zig | 77 ++++++++------- src/rendering/vulkan/Pass.zig | 61 ++++++++++++ src/rendering/vulkan/RenderPass.zig | 85 ++++++++-------- src/rendering/vulkan/Renderer.zig | 22 ++--- src/rendering/vulkan/Shader.zig | 12 +++ src/rendering/vulkan/Targets.zig | 140 +++++++++++++++++++++++++++ src/rendering/vulkan/device.zig | 2 + src/rendering/vulkan/framebuffer.zig | 37 +++---- src/rendering/vulkan/image.zig | 2 +- src/rendering/vulkan/memory.zig | 44 ++++----- src/rendering/vulkan/settings.zig | 65 +++++++++++++ src/rendering/vulkan/swapchain.zig | 49 +++++++++- 20 files changed, 808 insertions(+), 147 deletions(-) create mode 100644 src/math/quat.zig create mode 100644 src/rendering/vulkan/Bindings.zig delete mode 100644 src/rendering/vulkan/DepthBuffer.zig create mode 100644 src/rendering/vulkan/Targets.zig diff --git a/src/math/float4.zig b/src/math/float4.zig index 6d6a18b..7ed1ffc 100644 --- a/src/math/float4.zig +++ b/src/math/float4.zig @@ -1 +1,88 @@ -pub const Float4 = @Vector(4, f32); +const std = @import("std"); + +pub const Float4 = packed struct { + const Self = @This(); + + x: f32 = 0, + y: f32 = 0, + z: f32 = 0, + w: f32 = 0, + + pub inline fn add(lhs: Self, rhs: Self) Self { + return Self{ + .x = lhs.x + rhs.x, + .y = lhs.y + rhs.y, + .z = lhs.z + rhs.z, + .w = lhs.w + rhs.w, + }; + } + + pub inline fn sub(lhs: Self, rhs: Self) Self { + return Self{ + .x = lhs.x - rhs.x, + .y = lhs.y - rhs.y, + .z = lhs.z - rhs.z, + .w = lhs.w - rhs.w, + }; + } + + pub inline fn mul(lhs: Self, rhs: Self) Self { + return Self{ + .x = lhs.x * rhs.x, + .y = lhs.y * rhs.y, + .z = lhs.z * rhs.z, + .w = lhs.w * rhs.w, + }; + } + + pub inline fn mulvs(lhs: Self, rhs: f32) Self { + return Self{ + .x = lhs.x * rhs, + .y = lhs.y * rhs, + .z = lhs.z * rhs, + .w = lhs.w * rhs, + }; + } + + pub inline fn divvs(lhs: Self, rhs: f32) Self { + return lhs.mulvs(1.0 / rhs); + } + + pub inline fn sum3(self: Self) f32 { + return self.x + self.y + self.z; + } + + pub inline fn dot3(a: Self, b: Self) f32 { + return a.mul(b).sum3(); + } + + pub inline fn zxy(self: Self) Self { + return Self{ + .x = self.z, + .y = self.x, + .z = self.y, + .w = 0, + }; + } + + pub inline fn cross3(a: Self, b: Self) Self { + return zxy(sub(mul(zxy(a), b), mul(a, zxy(b)))); + } + + pub inline fn max(a: Self, b: Self) Self { + return Self{ + .x = @maximum(a.x, b.x), + .y = @maximum(a.y, b.y), + .z = @maximum(a.z, b.z), + .w = @maximum(a.w, b.w), + }; + } + + pub inline fn length3(self: Self) f32 { + return @sqrt(@maximum(std.math.f32_epsilon, self.dot3(self))); + } + + pub inline fn normalize3(self: Self) Self { + return self.divvs(self.length3()); + } +}; diff --git a/src/math/float4x4.zig b/src/math/float4x4.zig index ec8a3ba..f62567d 100644 --- a/src/math/float4x4.zig +++ b/src/math/float4x4.zig @@ -1,9 +1,64 @@ +const std = @import("std"); const Float4 = @import("float4.zig").Float4; // TODO: comptime magic? pub const Float4x4 = packed struct { - c0: Float4, - c1: Float4, - c2: Float4, - c3: Float4, + const Self = @This(); + + const zero = Self{}; + + c0: Float4 = Float4{}, + c1: Float4 = Float4{}, + c2: Float4 = Float4{}, + c3: Float4 = Float4{}, + + pub inline fn mulCol(self: Self, col: Float4) Float4 { + const a = self.c0.mulvs(col.x); + const b = self.c1.mulvs(col.y); + const c = self.c2.mulvs(col.z); + const d = self.c0.mulvs(col.w); + return a.add(b).add(c.add(d)); + } + + pub inline fn mul(a: Self, b: Self) Self { + return Self{ + .c0 = a.mulCol(b.c0), + .c1 = a.mulCol(b.c1), + .c2 = a.mulCol(b.c2), + .c3 = a.mulCol(b.c3), + }; + } + + pub inline fn lookAt(eye: Float4, at: Float4, up: Float4) Float4x4 { + // right-handed (ew) + const f = at.sub(eye).normalize3(); + const s = f.cross3(up).normalize3(); + const u = s.cross3(f).normalize3(); + + const tx = -s.dot3(eye); + const ty = -u.dot3(eye); + const tz = f.dot3(eye); + return Float4x4{ + .c0 = .{ .x = s.x, .y = u.x, .z = -f.x, .w = 0 }, + .c1 = .{ .x = s.y, .y = u.y, .z = -f.y, .w = 0 }, + .c2 = .{ .x = s.z, .y = u.z, .z = -f.z, .w = 0 }, + .c3 = .{ .x = tx, .y = ty, .z = tz, .w = 1 }, + }; + } + + pub inline fn glPerspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) Self { + const t = std.math.tan(fov_y * 0.5); + var m = zero; + m.c0.x = 1.0 / (aspect * t); + m.c1.y = 1.0 / t; + m.c2.z = -1.0 * (z_far * z_near) / (z_far - z_near); + m.c3.z = -2.0 * (z_far * z_near) / (z_far - z_near); + return m; + } + + pub inline fn vkPerspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) Self { + var m = glPerspective(fov_y, aspect, z_near, z_far); + m.c1.y *= -1.0; + return m; + } }; diff --git a/src/math/quat.zig b/src/math/quat.zig new file mode 100644 index 0000000..5e8b6d0 --- /dev/null +++ b/src/math/quat.zig @@ -0,0 +1,29 @@ +const Float4 = @import("float4.zig").Float4; + +pub const Quat = struct { + const Self = @This(); + + v: Float4, + + pub inline fn mulDir(q: Self, dir: Float4) Float4 { + const uv = q.v.cross3(dir); + const uuv = q.v.cross3(uv); + + var y = uv.mulvs(q.v.w); + y = y.add(uuv); + y = y.mulvs(2); + y = y.add(dir); + + return y; + } + + pub inline fn fwd(q: Self) Float4 { + const dir = Float4{ .x = 0, .y = 0, .z = -1, .w = 0 }; + return q.mulDir(dir); + } + + pub inline fn up(q: Self) Float4 { + const dir = Float4{ .x = 0, .y = 1, .z = 0, .w = 0 }; + return q.mulDir(dir); + } +}; diff --git a/src/rendering/Camera.zig b/src/rendering/Camera.zig index e69de29..a5858fc 100644 --- a/src/rendering/Camera.zig +++ b/src/rendering/Camera.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const Float4 = @import("../math/float4.zig").Float4; +const Float4x4 = @import("../math/float4x4.zig").Float4x4; +const Quat = @import("../math/quat.zig").Quat; + +const Self = @This(); + +var s_camera: Self = std.mem.zeroes(Self); + +position: Float4, +rotation: Quat, +z_near: f32, +z_far: f32, +fov_y: f32, + +pub fn get() Self { + return s_camera; +} + +pub inline fn getProj(self: *const Self, aspect: f32) Float4x4 { + return Float4x4.vkPerspective(std.math.degreesToRadians(f32, self.fov_y), aspect, self.z_near, self.z_far); +} + +pub inline fn getView(self: *const Self) Float4x4 { + const pos = self.position; + const rot = self.rotation; + return Float4x4.lookAt(pos, pos.add(rot.fwd()), rot.up()); +} + +pub inline fn getWorldToClip(self: *const Self, aspect: f32) Float4x4 { + return self.getProj(aspect).mul(self.getView()); +} diff --git a/src/rendering/vulkan/Bindings.zig b/src/rendering/vulkan/Bindings.zig new file mode 100644 index 0000000..fb28671 --- /dev/null +++ b/src/rendering/vulkan/Bindings.zig @@ -0,0 +1,102 @@ +const std = @import("std"); +const assert = std.debug.assert; +const vk = @import("vulkan"); + +const Device = @import("device.zig").Device; +const ProfileMark = @import("../../common/profiler.zig").ProfileMark; +const settings = @import("settings.zig"); +const Swapchain = @import("swapchain.zig").Swapchain; + +var s_sets = std.mem.zeroes([settings.resource_sets]vk.DescriptorSet); +var s_writes = std.mem.zeroes([settings.Bid.Count]vk.WriteDescriptorSet); +var s_bindings = std.mem.zeroes([settings.Bid.Count]Binding); +var s_dirty = std.mem.zeroes([settings.Bid.Count]i32); + +const pm_update = ProfileMark.init("Bindings.update"); +pub fn update(device: *const Device, swapchain: *const Swapchain) void { + pm_update.begin(); + defer pm_update.end(); + + // kara says: why do validation layers think there is a command buffer in flight using this desc set? + // submit.awaitAll(); + + //const set = getSet(swapchain); + // TexTable.write(set); + flush(device, swapchain); +} + +pub fn getSet(swapchain: *const Swapchain) vk.DescriptorSet { + const sync_index = swapchain.sync_index; + assert(sync_index < s_sets.len); + assert(s_sets[sync_index] != .null_handle); + return s_sets[sync_index]; +} + +const pm_flush = ProfileMark.init("Bindings.flush"); +pub fn flush(device: *const Device, swapchain: *const Swapchain) void { + pm_flush.begin(); + defer pm_flush.end(); + + const set = getSet(swapchain); + var write_count: i32 = 0; + for (s_bindings) |binding, i| { + assert(s_dirty[i] >= 0); + if (s_dirty[i] <= 0) { + continue; + } + + s_dirty[i] -= 1; + + var write = &s_writes[write_count]; + write_count += 1; + write.* = .{ + .s_type = .write_descriptor_set, + .descriptor_type = binding.desc_type, + .dst_set = set, + .dst_binding = i, + .dst_array_element = 0, + .descriptor_count = 1, + }; + + switch (binding.info) { + .buffer => |*buffer| { + switch (binding.desc_type) { + .uniform_buffer, .storage_buffer, .uniform_buffer_dynamic, .storage_buffer_dynamic => { + write.p_buffer_info = buffer; + }, + else => { + assert(false); + }, + } + }, + .image => |*image| { + switch (binding.desc_type) { + .sampler, .combined_image_sampler, .sampled_image, .storage_image, .input_attachment => { + write.p_image_info = image; + }, + else => { + assert(false); + }, + } + }, + } + + if (write_count > 0) { + device.dispatch.updateDescriptorSets(device.handle, write_count, s_writes, 0, null); + } + } +} + +pub const Binding = struct { + const Type = enum { + buffer, + image, + }; + const Union = union(Type) { + buffer: vk.DescriptorBufferInfo, + image: vk.DescriptorImageInfo, + }; + + desc_type: vk.DescriptorType, + info: Union, +}; diff --git a/src/rendering/vulkan/Command.zig b/src/rendering/vulkan/Command.zig index cc83725..3deae2a 100644 --- a/src/rendering/vulkan/Command.zig +++ b/src/rendering/vulkan/Command.zig @@ -14,6 +14,7 @@ const Renderer = @import("Renderer.zig"); const SubmitId = @import("submit_id.zig").SubmitId; const Fence = @import("sync.zig").Fence; const Semaphore = @import("sync.zig").Semaphore; +const Bindings = @import("Bindings.zig"); const queues = @import("queues.zig"); @@ -210,18 +211,36 @@ pub const Buffer = struct { self.ended = false; } + pub fn bindDescSets(self: *Self, device: *const Device, bindpoint: vk.PipelineBindPoint, layout: vk.PipelineLayout, set_count: u32, sets: [*]const vk.DescriptorSet) void { + assert(self.handle != .null_handle); + assert(self.gfx or self.comp); + assert(layout != .null_handle); + if (set_count > 0) { + device.dispatch.cmdBindDescriptorSets(self.handle, bindpoint, layout, 0, set_count, sets, 0, undefined); + } + } + + pub fn bindPass(self: *Self, device: *const Device, swapchain: *const Swapchain, pass: *const Pass) void { + assert(self.handle != .null_handle); + assert(self.gfx or self.comp); + assert(pass.pipeline != .null_handle); + device.dispatch.cmdBindPipeline(self.handle, pass.bindpoint, pass.pipeline); + const set = Bindings.getSet(swapchain); + self.bindDescSets(device, pass.bindpoint, pass.layout, 1, @ptrCast([*]const vk.DescriptorSet, &set)); + } + const pm_buffer_beginrenderpass = ProfileMark.init("Command.Buffer.beginRenderPass"); - pub fn beginRenderPass(self: *Self, device: *const Device, pass: vk.RenderPass, framebuf: vk.Framebuffer, rect: vk.Rect2D, clear_values: []*const vk.ClearValue) void { + pub fn beginRenderPass(self: *Self, device: *const Device, pass: vk.RenderPass, framebuf: vk.Framebuffer, rect: vk.Rect2D, clear_values: []const vk.ClearValue) void { pm_buffer_beginrenderpass.begin(); defer pm_buffer_beginrenderpass.end(); assert(self.handle != .null_handle); assert(!self.in_render_pass); - device.dispatch.beginRenderPass(self.handle, &.{ + device.dispatch.cmdBeginRenderPass(self.handle, &.{ .render_pass = pass, .framebuffer = framebuf, .render_area = rect, - .clear_value_count = clear_values.len, + .clear_value_count = @intCast(u32, clear_values.len), .p_clear_values = clear_values.ptr, }, .@"inline"); self.in_render_pass = true; @@ -239,7 +258,7 @@ pub const Buffer = struct { assert(self.handle != .null_handle); assert(self.in_render_pass); device.dispatch.cmdEndRenderPass(self.handle); - self.in_render_pass = 0; + self.in_render_pass = false; self.subpass = 0; } @@ -262,19 +281,19 @@ pub const Buffer = struct { assert(self.handle != .null_handle); assert(self.gfx or self.comp); assert(size == pass.push_constant_bytes); - assert(pass.layout.toInt() != 0); + assert(pass.layout != .null_handle); assert(pass.stage_flags.toInt() != 0); device.dispatch.cmdPushConstants(self.handle, pass.layout, pass.stage_flags, 0, size, constants); } pub fn viewport(self: *Self, device: *const Device, vp: vk.Viewport, scissor: vk.Rect2D) void { assert(self.handle != .null_handle); - device.dispatch.cmdSetViewport(self.handle, 0, 1, &vp); - device.dispatch.cmdSetScissor(self.handle, 0, 1, &scissor); + device.dispatch.cmdSetViewport(self.handle, 0, 1, @ptrCast([*]const vk.Viewport, &vp)); + device.dispatch.cmdSetScissor(self.handle, 0, 1, @ptrCast([*]const vk.Rect2D, &scissor)); } - pub fn defaultViewport(self: *Self) void { - self.viewport(Swapchain.getViewport(), Swapchain.getRect()); + pub fn defaultViewport(self: *Self, device: *const Device, swapchain: *const Swapchain) void { + self.viewport(device, swapchain.getViewport(), swapchain.getRect()); } pub fn draw(self: *Self, device: *const Device, vertex_count: i32, first_vertex: i32) void { @@ -303,6 +322,10 @@ pub const Buffer = struct { pm_buffer_submit.begin(); defer pm_buffer_submit.end(); + _ = wait_sema; + _ = signal_sema; + _ = wait_mask; + assert(self.began); assert(!self.ended); assert(!self.submitted); diff --git a/src/rendering/vulkan/Context.zig b/src/rendering/vulkan/Context.zig index 271e4fd..f26175b 100644 --- a/src/rendering/vulkan/Context.zig +++ b/src/rendering/vulkan/Context.zig @@ -12,9 +12,8 @@ const Self = @This(); pub var context: Self = Self{}; -pub fn init() Self { +pub fn init() void { context = std.mem.zeroes(@This()); - return context; } pub fn deinit() void { diff --git a/src/rendering/vulkan/DepthBuffer.zig b/src/rendering/vulkan/DepthBuffer.zig deleted file mode 100644 index e69de29..0000000 diff --git a/src/rendering/vulkan/MainPass.zig b/src/rendering/vulkan/MainPass.zig index f139617..00b6e1f 100644 --- a/src/rendering/vulkan/MainPass.zig +++ b/src/rendering/vulkan/MainPass.zig @@ -10,7 +10,7 @@ const Camera = @import("../Camera.zig"); const Shader = @import("Shader.zig"); const Pass = @import("Pass.zig"); const framebuffer = @import("framebuffer.zig"); -const DepthBuffer = @import("DepthBuffer.zig"); +const Targets = @import("Targets.zig"); const RenderPass = @import("RenderPass.zig"); const Float4 = @import("../../math/float4.zig").Float4; const Float4x4 = @import("../../math/float4x4.zig").Float4x4; @@ -19,14 +19,14 @@ const PushConstants = struct { local_to_clip: Float4x4, }; -pub fn init() !void { +pub fn init(device: *const Device, swapchain: *Swapchain) !void { errdefer { - std.debug.print("Failed to init MainPass"); + std.debug.print("Failed to init MainPass", .{}); deinit(); } // try ScreenBlit.init() - try DepthPass.init(); + try DepthPass.init(device, swapchain); try OpaquePass.init(); // try Exposure.init(); // try UIPass.init(); @@ -52,12 +52,12 @@ pub fn setup() void { } const pm_execute = ProfileMark.init("MainPass.execute"); -pub fn execute() void { +pub fn execute(device: *const Device, swapchain: *Swapchain) !void { pm_execute.begin(); defer pm_execute.end(); // todo: pt_trace convar check - DepthPass.execute(); + try DepthPass.execute(device, swapchain); OpaquePass.execute(); // Exposure.execute(); @@ -68,10 +68,10 @@ const DepthPass = struct { var s_render_pass: vk.RenderPass = undefined; var s_pass: Pass = undefined; - pub fn init() !void { + pub fn init(device: *const Device, swapchain: *Swapchain) !void { errdefer DepthPass.deinit(); - const depth_buffer = DepthBuffer.get(); + const depth_buffer = swapchain.getDepthBuffer(); var info = RenderPass.Description{ .src_stage_mask = .{ .early_fragment_tests_bit = true }, @@ -83,51 +83,49 @@ const DepthPass = struct { info.attachments[0] = .{ .format = depth_buffer.format, .layout = .depth_stencil_attachment_optimal, - .load = vk.VK_ATTACHMENT_LOAD_OP_CLEAR, - .store = vk.VK_ATTACHMENT_LOAD_OP_STORE, + .load = .clear, + .store = .store, }; - s_render_pass = try RenderPass.get(&info); + s_render_pass = try RenderPass.get(device, info); - var shaders = std.mem.zeroes([2]vk.PipelineShaderStageCreateInfo); + var shaders: [2]vk.PipelineShaderStageCreateInfo = undefined; try Shader.init(&shaders[0], "DepthOnly.hlsl", "VSMain", .vertex); errdefer Shader.deinit(&shaders[0]); try Shader.init(&shaders[1], "DepthOnly.hlsl", "PSMain", .fragment); errdefer Shader.deinit(&shaders[1]); - const vert_bindings = []vk.VertexInputBindingDescription{ + var vert_bindings = [_]vk.VertexInputBindingDescription{ .{ .binding = 0, .stride = @sizeOf(Float4), .input_rate = .vertex, }, }; - const vert_attributes = []vk.VertexInputAtttributeDescription{ + var vert_attributes = [_]vk.VertexInputAttributeDescription{ .{ .binding = 0, .format = .r32g32b32a32_sfloat, .location = 0, + .offset = 0, }, }; - try Pass.new(&s_pass, &.{ + const createInfo = Pass.Description{ .push_constant_bytes = @sizeOf(PushConstants), - .shader_count = shaders.len, - .shaders = shaders, + .shaders = &shaders, .render_pass = s_render_pass, - .subpass = 0, + .subpass = -1, .vert_layout = .{ - .binding_count = vert_bindings.len, - .bindings = vert_bindings.ptr, - .attribute_count = vert_attributes.len, - .attributes = vert_attributes.ptr, + .bindings = &vert_bindings, + .attributes = &vert_attributes, }, .fixed_funcs = .{ - .viewport = Swapchain.getViewport(), - .scissor = Swapchain.getRect(), + .viewport = swapchain.getViewport(), + .scissor = swapchain.getRect(), .topology = .triangle_list, .polygon_mode = .fill, .front_face = .counter_clockwise, - .cull_mode = .back_bit, + .cull_mode = .{ .back_bit = true }, .depth_compare_op = .less, .scissor_on = false, .depth_clamp = false, @@ -135,7 +133,8 @@ const DepthPass = struct { .depth_write_enable = true, .attachment_count = 0, }, - }); + }; + try Pass.init(&s_pass, &createInfo); } pub fn deinit() void { @@ -145,15 +144,15 @@ const DepthPass = struct { pub fn setup() void {} const pm_depth_execute = ProfileMark.init("DepthPass.execute"); - pub fn execute(device: *const Device) void { + pub fn execute(device: *const Device, swapchain: *Swapchain) !void { pm_depth_execute.begin(); defer pm_depth_execute.end(); const camera = Camera.get(); - const world_to_clip = camera.getWorldToClip(Swapchain.getAspect()); + const world_to_clip = camera.getWorldToClip(swapchain.getAspect()); - const attachments = []Image{DepthBuffer.get()}; + const attachments = &[_]*Image{swapchain.getDepthBuffer()}; const rect = vk.Rect2D{ .offset = std.mem.zeroes(vk.Offset2D), .extent = .{ @@ -162,14 +161,14 @@ const DepthPass = struct { }, }; - const fbuf = framebuffer.get(attachments, rect.extent.width, rect.extent.height); + const fbuf = try framebuffer.getOrAdd(device, attachments, rect.extent.width, rect.extent.height); const cmd = try Command.Buffer.get(.graphics, device); - Image.State.depthAttachWrite(cmd, attachments[0]); + _ = try Image.State.depthAttachWrite(device, cmd, attachments[0]); - cmd.defaultViewport(device); - cmd.bindPass(device, &s_pass); - cmd.beginRenderPass(device, s_render_pass, fbuf, rect, .{ + cmd.defaultViewport(device, swapchain); + cmd.bindPass(device, swapchain, &s_pass); + cmd.beginRenderPass(device, s_render_pass, fbuf, rect, &.{ .{ .depth_stencil = .{ .depth = 1.0, .stencil = 0.0 }, }, @@ -184,4 +183,12 @@ const DepthPass = struct { } }; -const OpaquePass = struct {}; +const OpaquePass = struct { + pub fn init() !void {} + + pub fn deinit() void {} + + pub fn setup() void {} + + pub fn execute() void {} +}; diff --git a/src/rendering/vulkan/Pass.zig b/src/rendering/vulkan/Pass.zig index e69de29..f148a92 100644 --- a/src/rendering/vulkan/Pass.zig +++ b/src/rendering/vulkan/Pass.zig @@ -0,0 +1,61 @@ +const vk = @import("vulkan"); + +const Self = @This(); + +pipeline: vk.Pipeline, +layout: vk.PipelineLayout, +bindpoint: vk.PipelineBindPoint, +stage_flags: vk.ShaderStageFlags, +push_constant_bytes: i32, + +const VertexLayout = struct { + bindings: []vk.VertexInputBindingDescription, + attributes: []vk.VertexInputAttributeDescription, +}; + +const BlendState = struct { + color_wite_mask: vk.ColorComponentFlags, + blend_enable: bool, + src_color_blend_factor: vk.BlendFactor, + dst_color_blend_factor: vk.BlendFactor, + color_blend_op: vk.BlendOp, + src_alpha_blend_factor: vk.BlendFactor, + dst_alpha_blend_factor: vk.BlendFactor, + alpha_blend_op: vk.BlendOp, +}; + +const FixedFuncs = struct { + viewport: vk.Viewport, + scissor: vk.Rect2D, + topology: vk.PrimitiveTopology, + polygon_mode: vk.PolygonMode, + cull_mode: vk.CullModeFlags, + front_face: vk.FrontFace, + depth_compare_op: vk.CompareOp, + scissor_on: bool, + depth_clamp: bool, + depth_test_enable: bool, + depth_write_enable: bool, + attachment_count: u3, + attachments: ?[8]BlendState = null, +}; + +pub const Description = struct { + // Graphics and compute + push_constant_bytes: i32, + shaders: []vk.PipelineShaderStageCreateInfo, + // Graphics only + render_pass: vk.RenderPass, + subpass: i32, + vert_layout: VertexLayout, + fixed_funcs: FixedFuncs, +}; + +pub fn init(self: *Self, desc: *const Description) !void { + _ = self; + _ = desc; +} + +pub fn deinit(self: *Self) void { + _ = self; +} diff --git a/src/rendering/vulkan/RenderPass.zig b/src/rendering/vulkan/RenderPass.zig index 641df35..acb500b 100644 --- a/src/rendering/vulkan/RenderPass.zig +++ b/src/rendering/vulkan/RenderPass.zig @@ -1,13 +1,14 @@ const std = @import("std"); const vk = @import("vulkan"); -const profiler = @import("/common/profiler.zig"); +const profiler = @import("../../common/profiler.zig"); const Device = @import("device.zig").Device; -const vkd = @import("device.zig").DeviceDispatch; -const allocator: std.mem.Allocator = undefined; +// TODO memory +var gpa = std.heap.GeneralPurposeAllocator(.{}){}; +const allocator = gpa.allocator(); -const s_cache = std.HashMap(Description, vk.RenderPass).init(allocator); +var s_cache = std.AutoHashMap(Description, vk.RenderPass).init(allocator); // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#renderpass-compatibility // compatibility: @@ -16,8 +17,8 @@ const s_cache = std.HashMap(Description, vk.RenderPass).init(allocator); const AttachmentState = packed struct { format: vk.Format, // must match layout: vk.ImageLayout, // can vary - load: u2, // vk.AttachmentLoadOp, can vary - store: u2, // vk.AttachmentStoreOp, can vary + load: vk.AttachmentLoadOp, // can vary + store: vk.AttachmentStoreOp, // can vary }; pub const Description = struct { @@ -29,32 +30,31 @@ pub const Description = struct { }; const pm_get = profiler.ProfileMark.init("renderpass.get"); -pub fn get(desc: *const Description) !vk.RenderPass { +pub fn get(device: *const Device, desc: Description) !vk.RenderPass { pm_get.begin(); defer pm_get.end(); const result = try s_cache.getOrPut(desc); if (result.found_existing) { - return result.value_ptr; + return result.value_ptr.*; } - const attachments = [8]vk.AttachmentDescription{}; - const refs = [attachments.len]vk.AttachmentReference{}; - var attachment_count = 0; - var ref_count = 0; + var attachments = std.mem.zeroes([8]vk.AttachmentDescription); + var refs = std.mem.zeroes([attachments.len]vk.AttachmentReference); + var attachment_count: u32 = 0; + var ref_count: usize = 0; const format0 = desc.attachments[0].format; - const zero_is_depth = format0 >= .d16_unorm and format0 <= .d32_sfloat_s8_uint; + const zero_is_depth = @enumToInt(format0) >= @enumToInt(vk.Format.d16_unorm) and @enumToInt(format0) <= @enumToInt(vk.Format.d32_sfloat_s8_uint); - for (desc.attachments) |dst_attachment, i| { + for (desc.attachments) |*src, i| { attachments[i].load_op = .dont_care; attachments[i].store_op = .dont_care; - const src = &desc.attachments[i]; if (src.format != .@"undefined") { std.debug.assert(attachment_count < attachments.len); - const dst = &dst_attachment; + var dst = &attachments[attachment_count]; dst.format = src.format; dst.initial_layout = src.layout; - dst.samples = .@"1_bit"; + dst.samples = .{ .@"1_bit" = true }; dst.stencil_load_op = .dont_care; dst.stencil_store_op = .dont_care; dst.load_op = src.load; @@ -75,35 +75,42 @@ pub fn get(desc: *const Description) !vk.RenderPass { std.debug.assert(attachment_count <= attachments.len); std.debug.assert(attachment_count <= refs.len); const color_ref_count = if (zero_is_depth) attachment_count - 1 else attachment_count; - const depth_ref_count = if (zero_is_depth) 1 else 0; + const depth_ref_count = if (zero_is_depth) @as(u32, 1) else 0; - const handle = try new(attachment_count, attachments, 1, &.{ + const subpasses = [_]vk.SubpassDescription{.{ + .flags = .{}, + .input_attachment_count = 0, + .p_input_attachments = undefined, + .p_resolve_attachments = null, + .preserve_attachment_count = 0, + .p_preserve_attachments = undefined, .pipeline_bind_point = .graphics, .color_attachment_count = color_ref_count, - .p_color_attachments = if (color_ref_count) &refs[depth_ref_count] else null, - .p_depth_stencil_attachment = if (depth_ref_count) &refs[0] else null, - }, 1, &.{ - .src_subpass = .external, + .p_color_attachments = if (color_ref_count > 0) @ptrCast([*]const vk.AttachmentReference, &refs[depth_ref_count]) else undefined, + .p_depth_stencil_attachment = if (depth_ref_count > 0) @as(?*const vk.AttachmentReference, &refs[0]) else null, + }}; + + const dependencies = [_]vk.SubpassDependency{.{ + .dependency_flags = .{}, + .src_subpass = vk.SUBPASS_EXTERNAL, .dst_subpass = 0, .src_stage_mask = desc.src_stage_mask, - .dst_stage_mask = desc.dest_stage_mask, + .dst_stage_mask = desc.dst_stage_mask, .src_access_mask = desc.src_access_mask, .dst_access_mask = desc.dst_access_mask, - }); + }}; + + const info = vk.RenderPassCreateInfo{ + .flags = .{}, + .attachment_count = attachment_count, + .p_attachments = &attachments, + .subpass_count = subpasses.len, + .p_subpasses = &subpasses, + .dependency_count = dependencies.len, + .p_dependencies = &dependencies, + }; + const handle = try device.dispatch.createRenderPass(device.handle, &info, null); result.value_ptr.* = handle; -} - -fn new(device: *Device, attachment_count: i32, p_attachments: *const vk.AttachmentDescription, subpass_count: i32, p_subpasses: *const vk.SubpassDescription, dependency_count: i32, p_dependencies: *const vk.SubpassDependency) !vk.RenderPass { - const handle: vk.RenderPass = undefined; - try vkd.createRenderPass(device.dev, &.{ - .attachment_count = attachment_count, - .p_attachments = p_attachments, - .subpass_count = subpass_count, - .p_subpasses = p_subpasses, - .dependency_count = dependency_count, - .p_dependencies = p_dependencies, - }, null, &handle); - - return handle; + return result.value_ptr.*; } diff --git a/src/rendering/vulkan/Renderer.zig b/src/rendering/vulkan/Renderer.zig index c92952e..72612a8 100644 --- a/src/rendering/vulkan/Renderer.zig +++ b/src/rendering/vulkan/Renderer.zig @@ -16,14 +16,12 @@ instance: Instance, window: Window, device: Device, swapchain: Swapchain, -context: Context, // sampler: Sampler, TODO // tex_table: TexTable, TODO // bindings: Bindings, TODO // targets: Targets, TODO // mesh_sys: MeshSys, TODO // im_sys: ImSys, TODO -main_pass: MainPass, const Self = @This(); @@ -49,26 +47,24 @@ pub fn init() !Self { try framebuffer.init(); errdefer framebuffer.deinit(&device); - const swapchain = try Swapchain.init(&instance, &window, &device, null); - errdefer swapchain.deinit(); + var swapchain = try Swapchain.init(&instance, &window, &device, null); + errdefer swapchain.deinit(&device); - const context = Context.init(); - errdefer context.deinit(); + Context.init(); + errdefer Context.deinit(); // try self.sampler.init(); // try self.texTable.init(); // try self.bindings.init(); // try self.targets.init(); // try self.meshSys.init(); // try self.imSys.init(); - const main_pass = try MainPass.init(); + try MainPass.init(&device, &swapchain); return Self{ .instance = instance, .window = window, .device = device, .swapchain = swapchain, - .context = context, - .main_pass = main_pass, }; } @@ -89,14 +85,14 @@ pub fn update(self: *Self) !void { // setup phase { - self.main_pass.setup(); + MainPass.setup(); // TODO textable update try Command.flush(&self.device); // TODO bindings update } // execute phase - self.main_pass.execute(); + try MainPass.execute(&self.device, &self.swapchain); // present phase // currently only graphics queue @@ -114,7 +110,7 @@ pub fn deinit(self: Self) void { // TODO: delete lightmap pack - self.main_pass.deinit(); + MainPass.deinit(); // self.imSys.deinit(); // self.meshSys.deinit(); @@ -162,7 +158,7 @@ fn windowUpdate(self: Self) !void { self.device.waitIdle(); // TODO: UI pass del - self.swapchain.deinit(); + self.swapchain.deinit(&self.device); self.window.deinit(); self.window.init(); diff --git a/src/rendering/vulkan/Shader.zig b/src/rendering/vulkan/Shader.zig index e69de29..a5952a8 100644 --- a/src/rendering/vulkan/Shader.zig +++ b/src/rendering/vulkan/Shader.zig @@ -0,0 +1,12 @@ +const vk = @import("vulkan"); +const Thingie = enum { + vertex, + fragment, +}; + +pub fn init(info: *const vk.PipelineShaderStageCreateInfo, file_path: []const u8, section: []const u8, thingie: Thingie) !void { + _ = info; + _ = file_path; + _ = section; + _ = thingie; +} diff --git a/src/rendering/vulkan/Targets.zig b/src/rendering/vulkan/Targets.zig new file mode 100644 index 0000000..984c785 --- /dev/null +++ b/src/rendering/vulkan/Targets.zig @@ -0,0 +1,140 @@ +const assert = @import("std").debug.assert; +const vk = @import("vulkan"); + +const Device = @import("device.zig").Device; +const Swapchain = @import("swapchain.zig").Swapchain; +const settings = @import("settings.zig"); +const queues = @import("queues.zig"); +const Image = @import("image.zig").Image; + +const Self = @This(); + +width: i32, +height: i32, +depth: [settings.resource_sets]Image, +scene: [settings.resource_sets]Image, + +// TODO: scaled render resolution +pub fn getDesiredWidth(swapchain: *const Swapchain) i32 { + getDisplayWidth(swapchain); +} + +pub fn getDesiredHeight(swapchain: *const Swapchain) i32 { + getDisplayHeight(swapchain); +} + +var s_targets: Self = undefined; + +pub fn init(device: *const Device, swapchain: *const Swapchain) !void { + errdefer deinit(device); + + const width = getDesiredWidth(swapchain); + const height = getDesiredHeight(swapchain); + + s_targets.width = width; + s_targets.height = height; + + for (s_targets.scene) |*s| { + const usage: vk.ImageUsageFlags = .{ + .color_attachment_bit = true, + .sampled_bit = true, + .storage_bit = true, + .transfer_src_bit = true, + .transfer_dst_bit = true, + }; + const queue_family_indices = []u32{ + queues.get(.graphics).family, + queues.get(.compute).family, + }; + + try Image.init(s, &.{ + .image_type = .@"2d", + .format = .r16g16b16a16_sfloat, + .extent = .{ + .width = width, + .height = height, + .depth = 1, + }, + .mip_levels = 1, + .array_layers = 1, + .samples = .@"1_bit", + .tiling = .optimal, + .usage = usage, + .sharing_mode = .exclusive, + .queue_family_index_count = queue_family_indices.len, + .p_queue_family_indices = queue_family_indices.ptr, + .initial_layout = .@"undefined", + }, .gpu_only); + } + + for (s_targets.depth) |*d| { + const usage: vk.ImageUsageFlags = .{ + .depth_stencil_attachment_bit = true, + .sampled_bit = true, + }; + const queue_family_indices = []u32{ + queues.get(.graphics).family, + }; + try Image.init(d, &.{ + .image_type = .@"2d", + .format = .x8_d24_unorm_pack32, + .extent = .{ + .width = width, + .height = height, + .depth = 1, + }, + .mip_levels = 1, + .array_layers = 1, + .samples = .@"1_bit", + .tiling = .optimal, + .usage = usage, + .sharing_mode = .exclusive, + .queue_family_index_count = queue_family_indices.len, + .p_queue_family_indices = queue_family_indices.ptr, + .initial_layout = .@"undefined", + }, .gpu_only); + } +} + +pub fn deinit(device: *const Device) void { + for (s_targets.scene) |*s| { + s.deinit(device); + } + + for (s_targets.depth) |*d| { + d.deinit(device); + } +} + +pub fn recreate(device: *const Device) void { + if (getRenderWidth() != getDesiredWidth() or + getRenderHeight() != getDesiredHeight()) + { + deinit(device); + init(); + } +} + +pub fn getDisplayWidth(swapchain: *const Swapchain) i32 { + swapchain.getBackBuffer().width; +} + +pub fn getDisplayHeight(swapchain: *const Swapchain) i32 { + swapchain.getBackBuffer().height; +} + +pub fn getRenderWidth() i32 { + return s_targets.width; +} + +pub fn getRenderHeight() i32 { + return s_targets.height; +} + +pub fn getRenderAspect() f32 { + return @intToFloat(f32, s_targets.width) / @intToFloat(f32, s_targets.height); +} + +pub fn getRenderScale() f32 { + return 1.0; // TODO convar +} diff --git a/src/rendering/vulkan/device.zig b/src/rendering/vulkan/device.zig index 6d9407b..4dd2bb4 100644 --- a/src/rendering/vulkan/device.zig +++ b/src/rendering/vulkan/device.zig @@ -66,12 +66,14 @@ pub const DeviceDispatch = vk.DeviceWrapper(.{ .cmdBeginRenderPass = true, .cmdEndRenderPass = true, .cmdBindPipeline = true, + .cmdBindDescriptorSets = true, .cmdDraw = true, .cmdSetViewport = true, .cmdSetScissor = true, .cmdBindVertexBuffers = true, .cmdCopyBuffer = true, .cmdPipelineBarrier = true, + .cmdPushConstants = true, .getFenceStatus = true, }); diff --git a/src/rendering/vulkan/framebuffer.zig b/src/rendering/vulkan/framebuffer.zig index ee49a11..611a7ba 100644 --- a/src/rendering/vulkan/framebuffer.zig +++ b/src/rendering/vulkan/framebuffer.zig @@ -11,8 +11,8 @@ const allocator = gpa.allocator(); const num_attachments = 8; const Key = struct { - attachments: [num_attachments]vk.ImageView, - formats: [num_attachments]vk.Format, + attachments: [num_attachments]vk.ImageView = std.mem.zeroes([num_attachments]vk.ImageView), + formats: [num_attachments]vk.Format = std.mem.zeroes([num_attachments]vk.Format), width: u16, height: u16, }; @@ -39,18 +39,18 @@ pub fn deinit(device: *const Device) void { s_fbufs.deinit(); } -pub fn getOrAdd(attachments: []*const Image, width: i32, height: i32) !*Framebuffer { +pub fn getOrAdd(device: *const Device, attachments: []*const Image, width: u32, height: u32) !vk.Framebuffer { std.debug.assert(attachments.len >= 1); std.debug.assert(attachments.len <= num_attachments); - const key = Key{ - .width = width, - .height = height, + var key = Key{ + .width = @intCast(u16, width), + .height = @intCast(u16, height), }; for (attachments) |attachment, i| { - if (attachment.view) |view| { - key.attachments[i] = view; + if (attachment.view != .null_handle) { + key.attachments[i] = attachment.view; key.formats[i] = attachment.format; } } @@ -58,12 +58,12 @@ pub fn getOrAdd(attachments: []*const Image, width: i32, height: i32) !*Framebuf const result = s_fbufs.getOrPutAssumeCapacity(key); if (!result.found_existing) { const value = Value{ - .handle = try Framebuffer.init(key.attachments, key.formats, key.width, key.height), + .handle = try Framebuffer.init(device, key.attachments[0..attachments.len], key.formats[0..attachments.len], key.width, key.height), }; result.value_ptr.* = value; } - return result.value_ptr; + return result.value_ptr.handle; } pub fn remove(device: *const Device, view: vk.ImageView) void { @@ -87,8 +87,8 @@ fn containsView(key: *const Key, view: vk.ImageView) bool { } pub const Framebuffer = struct { - pub fn init(device: *Device, attachments: []const vk.ImageView, formats: []const vk.Format, width: i32, height: i32) !vk.Framebuffer { - const pass_desc: RenderPass.Description = .{ + pub fn init(device: *const Device, attachments: []const vk.ImageView, formats: []const vk.Format, width: u32, height: u32) !vk.Framebuffer { + var pass_desc: RenderPass.Description = .{ .src_access_mask = .{ .shader_read_bit = true }, .dst_access_mask = .{ .color_attachment_write_bit = true }, .src_stage_mask = .{ .fragment_shader_bit = true }, @@ -97,17 +97,18 @@ pub const Framebuffer = struct { for (formats) |format, i| { pass_desc.attachments[i].format = format; - const layout = if (i == 0 and format >= .d16_unorm and format <= .d32_sfloat_s8_uint) - .depth_stencil_attachment_optimal + const layout = if (i == 0 and @enumToInt(format) >= @enumToInt(vk.Format.d16_unorm) and @enumToInt(format) <= @enumToInt(vk.Format.d32_sfloat_s8_uint)) + vk.ImageLayout.depth_stencil_attachment_optimal else - .color_attachment_optimal; + vk.ImageLayout.color_attachment_optimal; pass_desc.attachments[i].layout = layout; } - const pass = try RenderPass.get(&pass_desc); - return try device.dispatch.createFramebuffer(device.handle, .{ + const pass = try RenderPass.get(device, pass_desc); + return try device.dispatch.createFramebuffer(device.handle, &.{ + .flags = .{}, .render_pass = pass, - .attachment_count = attachments.len, + .attachment_count = @intCast(u32, attachments.len), .p_attachments = attachments.ptr, .width = width, .height = height, diff --git a/src/rendering/vulkan/image.zig b/src/rendering/vulkan/image.zig index 17c57d5..585458e 100644 --- a/src/rendering/vulkan/image.zig +++ b/src/rendering/vulkan/image.zig @@ -22,7 +22,7 @@ pub const Image = struct { state: State = .{}, handle: vk.Image = .null_handle, allocation: vma.Allocation, - view: vk.ImageView, + view: vk.ImageView = .null_handle, format: vk.Format, width: u16, height: u16, diff --git a/src/rendering/vulkan/memory.zig b/src/rendering/vulkan/memory.zig index f681f5a..d448ca8 100644 --- a/src/rendering/vulkan/memory.zig +++ b/src/rendering/vulkan/memory.zig @@ -213,32 +213,32 @@ const Pool = struct { } }; -const ReleasableType = enum { - buffer, - image, - image_view, - attachment, // view used as an attachment -}; - -const ReleasableUnion = union(ReleasableType) { - buffer: struct { - handle: vk.Buffer, - allocation: vma.Allocation, - }, - image: struct { - handle: vk.Image, - allocation: vma.Allocation, - view: vk.ImageView, - }, - image_view: vk.ImageView, - attachment: vk.ImageView, -}; - pub const Releasable = struct { const Self = @This(); + const Type = enum { + buffer, + image, + image_view, + attachment, // view used as an attachment + }; + + const Union = union(Type) { + buffer: struct { + handle: vk.Buffer, + allocation: vma.Allocation, + }, + image: struct { + handle: vk.Image, + allocation: vma.Allocation, + view: vk.ImageView, + }, + image_view: vk.ImageView, + attachment: vk.ImageView, + }; + submit_id: SubmitId, - object: ReleasableUnion, + object: Union, pub fn init(self: *const Self) void { // TODO diff --git a/src/rendering/vulkan/settings.zig b/src/rendering/vulkan/settings.zig index 57a22d9..e9f1fd5 100644 --- a/src/rendering/vulkan/settings.zig +++ b/src/rendering/vulkan/settings.zig @@ -1,3 +1,5 @@ +const vk = @import("vulkan"); + // Compile-time settings for Vulkan. pub const app_name = "efemra"; @@ -20,3 +22,66 @@ pub const cmds_per_queue = 64; pub fn OnlyIf(comptime setting: bool, t: anytype) type { return if (setting) t else void; } + +pub const Bid = struct { + id: u32, + desc_type: vk.DescriptorType, + count: u32 = 1, + stages: vk.ShaderStageFlags, + + pub const TextureTable = struct { + const T1D = Bid{ + .id = 0, + .desc_type = .combined_image_sampler, + .count = 64, + .stages = .{ .fragment_bit = true, .compute_bit = true }, + }; + const T2D = Bid{ + .id = T1D.id + 1, + .desc_type = .combined_image_sampler, + .count = 512, + .stages = .{ .fragment_bit = true, .compute_bit = true }, + }; + const T3D = Bid{ + .id = T2D.id + 1, + .desc_type = .combined_image_sampler, + .count = 64, + .stages = .{ .fragment_bit = true, .compute_bit = true }, + }; + const TCube = Bid{ + .id = T3D.id + 1, + .desc_type = .combined_image_sampler, + .count = 64, + .stages = .{ .fragment_bit = true, .compute_bit = true }, + }; + const T2DArray = Bid{ + .id = TCube.id + 1, + .desc_type = .combined_image_sampler, + .count = 64, + .stages = .{ .fragment_bit = true, .compute_bit = true }, + }; + }; + + const Globals = Bid{ + .id = TextureTable.T2DArray.id + 1, + .desc_type = .uniform_buffer, + .stages = .{}.complement(), // all + }; + const SceneLuminance = Bid{ + .id = Globals.id + 1, + .desc_type = .combined_image_sampler, + .stages = .{ .fragment_bit = true, .compute_bit = true }, + }; + const HistogramBuffer = Bid{ + .id = SceneLuminance.id + 1, + .desc_type = .storage_image, + .stages = .{ .compute_bit = true }, + }; + const ExposureBuffer = Bid{ + .id = HistogramBuffer.id + 1, + .desc_type = .storage_buffer, + .stages = .{ .fragment_bit = true, .compute_bit = true }, + }; + + const Count = ExposureBuffer.id + 1; +}; diff --git a/src/rendering/vulkan/swapchain.zig b/src/rendering/vulkan/swapchain.zig index 376f4cf..a32183b 100644 --- a/src/rendering/vulkan/swapchain.zig +++ b/src/rendering/vulkan/swapchain.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const assert = std.debug.assert; const vk = @import("vulkan"); const math = @import("std").math; const Command = @import("Command.zig"); @@ -278,7 +279,7 @@ pub const Swapchain = struct { const timeout = std.math.maxInt(u64); const result = try device.dispatch.acquireNextImageKHR(device.handle, self.handle, timeout, self.available_semas[self.sync_index].handle, .null_handle); - std.debug.assert(result.image_index < self.length); + assert(result.image_index < self.length); const sub = self.image_submits[result.image_index]; if (sub.valid) { try sub.wait(device); @@ -296,7 +297,7 @@ pub const Swapchain = struct { const image_index = self.image_index; const sync_index = self.sync_index; { - const backbuf = self.getBackBuffer(); + var backbuf = self.getBackBuffer(); const prev_use = backbuf.state.stage; _ = try Image.State.presentSrc(device, command, backbuf); @@ -322,7 +323,49 @@ pub const Swapchain = struct { } } + pub fn getViewport(self: *const Self) vk.Viewport { + assert(self.handle != .null_handle); + return vk.Viewport{ + .x = 0.0, + .y = 0.0, + .width = @intToFloat(f32, self.width), + .height = @intToFloat(f32, self.height), + .min_depth = 0.0, + .max_depth = 1.0, + }; + } + + pub fn getRect(self: *const Self) vk.Rect2D { + assert(self.handle != .null_handle); + return vk.Rect2D{ + .extent = .{ + .width = self.width, + .height = self.height, + }, + .offset = std.mem.zeroes(vk.Offset2D), + }; + } + + pub fn getAspect(self: *const Self) f32 { + assert(self.handle != .null_handle); + return @intToFloat(f32, self.width) / @intToFloat(f32, self.height); + } + pub fn getBackBuffer(self: *Self) *Image { - return &self.images[self.image_index]; + var img = &self.images[self.image_index]; + assert(img.handle != .null_handle); + return img; + } + + pub fn getDepthBuffer(self: *Self) *Image { + var img = &self.images[self.sync_index]; + assert(img.handle != .null_handle); + return img; + } + + pub fn getSceneBuffer(self: *Self) *Image { + var img = &self.images[self.sync_index]; + assert(img.handle != .null_handle); + return img; } };