diff --git a/assets/shaders/triangle.glsl b/assets/shaders/triangle.glsl index e73e51e..3fa24af 100644 --- a/assets/shaders/triangle.glsl +++ b/assets/shaders/triangle.glsl @@ -1,12 +1,21 @@ #if VERTEX_SHADER vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), + vec2(-0.5, 0.5), vec2(0.5, 0.5), - vec2(-0.5, 0.5) + vec2(0.0, -0.5) ); +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +layout(location = 0) out vec3 VertexColor; + void main() { + VertexColor = colors[gl_VertexIndex]; gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); } @@ -14,10 +23,12 @@ void main() { #if FRAGMENT_SHADER +layout(location = 0) in vec3 VertexColor; + layout(location = 0) out vec4 FragColor; void main() { - FragColor = vec4(0.6, 0.8, 1.0, 1.0); + FragColor = vec4(VertexColor, 1.0); } #endif diff --git a/src/AssetManager.zig b/src/AssetManager.zig index dd02e1a..6aeef07 100644 --- a/src/AssetManager.zig +++ b/src/AssetManager.zig @@ -27,6 +27,8 @@ const Vec3 = @import("zalgebra").Vec3; const Mat4 = @import("zalgebra").Mat4; const sdl = @import("sdl.zig"); const tracy = @import("tracy"); +const vk = @import("vk"); +const GraphicsContext = @import("GraphicsContext.zig"); pub const AssetId = assets.AssetId; pub const Handle = assets.Handle; @@ -56,6 +58,7 @@ rw_lock: std.Thread.RwLock.DefaultRwLock = .{}, asset_watcher: AssetWatcher = undefined, vertex_heap: VertexBufferHeap, +gc: *GraphicsContext, const AssetWatcher = struct { assetman: *AssetManager, @@ -145,7 +148,7 @@ const AssetWatcher = struct { } }; -pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator) AssetManager { +pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, gc: *GraphicsContext) AssetManager { var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; const exe_dir_path = std.fs.selfExeDirPath(&buf) catch @panic("can't find self exe dir path"); const exe_dir = std.fs.openDirAbsolute(exe_dir_path, .{}) catch @panic("can't open self exe dir path"); @@ -155,6 +158,7 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator) AssetM .frame_arena = frame_arena, .exe_dir = exe_dir, .vertex_heap = VertexBufferHeap.init(allocator) catch @panic("OOM"), + .gc = gc, }; } @@ -212,16 +216,14 @@ pub fn resolveShaderWithDefines(self: *AssetManager, handle: Handle.Shader, defi return self.loadShader(handle.id, permuted_asset_id, defines); } +// pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) LoadedShaderProgram { +// return self.resolveShaderProgramWithDefines(handle, &.{}); +// } +// pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) LoadedShaderProgram { - return self.resolveShaderProgramWithDefines(handle, &.{}); -} - -pub fn resolveShaderProgramWithDefines(self: *AssetManager, handle: Handle.ShaderProgram, defines: []const DefinePair) LoadedShaderProgram { if (handle.id == 0) return NullShaderProgram; - const permuted_asset_id = permuteAssetIdDefines(handle.id, defines); - - if (self.resolveAsset(permuted_asset_id)) |asset| { + if (self.resolveAsset(handle.id)) |asset| { switch (asset.*) { .shaderProgram => |shader| { return shader; @@ -230,7 +232,7 @@ pub fn resolveShaderProgramWithDefines(self: *AssetManager, handle: Handle.Shade } } - return self.loadShaderProgram(handle, permuted_asset_id, defines); + return self.loadShaderProgram(handle); } pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) LoadedMesh { @@ -307,88 +309,147 @@ pub const ShaderProgramDefinition = struct { compute: []const u8, }; -pub fn loadShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram, permuted_id: AssetId, defines: []const DefinePair) LoadedShaderProgram { - return self.loadShaderProgramErr(handle.id, permuted_id, defines) catch |err| { +pub fn loadShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) LoadedShaderProgram { + return self.loadShaderProgramErr(handle.id) catch |err| { std.log.err("Failed to load shader program {}\n", .{err}); return NullShaderProgram; }; } -fn loadShaderProgramErr(self: *AssetManager, id: AssetId, permuted_id: AssetId) !LoadedShaderProgram { +fn loadShaderProgramErr(self: *AssetManager, id: AssetId) !LoadedShaderProgram { const data = try self.loadFile(self.frame_arena, asset_manifest.getPath(id), SHADER_MAX_BYTES); - var serializer = formats.Serializer{ .write = false, .endian = formats.native_endian, .stream = .{ .const_buffer = data.bytes } }; + var serializer = formats.Serializer{ .write = false, .endian = formats.native_endian, .stream = .{ .buffer = std.io.fixedBufferStream(data.bytes) } }; var program: formats.ShaderProgram = undefined; try program.serialize(&serializer); - const prog = gl.createProgram(); - errdefer gl.deleteProgram(prog); + const pipeline = blk: { + switch (program) { + .graphics => |graphics_pipeline| { + const vertex_shader_modle = try self.gc.device.createShaderModule(&.{ + .code_size = graphics_pipeline.vertex.source.len, + .p_code = @alignCast(@ptrCast(graphics_pipeline.vertex.source.ptr)), + }, null); + defer self.gc.device.destroyShaderModule(vertex_shader_modle, null); + const fragment_shader_modle = try self.gc.device.createShaderModule(&.{ + .code_size = graphics_pipeline.fragment.source.len, + .p_code = @alignCast(@ptrCast(graphics_pipeline.fragment.source.ptr)), + }, null); + defer self.gc.device.destroyShaderModule(fragment_shader_modle, null); - switch (program) { - .graphics => |graphics_pipeline| { - const vertex_shader = try self.compileShader(graphics_pipeline.vertex.source, .vertex); - defer gl.deleteShader(vertex_shader); - const fragment_shader = try self.compileShader(graphics_pipeline.fragment.source, .fragment); - defer gl.deleteShader(fragment_shader); + const dynamic_states = [_]vk.DynamicState{ + .viewport_with_count, + .scissor_with_count, + }; - gl.attachShader(prog, vertex_shader); - defer gl.detachShader(prog, vertex_shader); - gl.attachShader(prog, fragment_shader); - defer gl.detachShader(prog, fragment_shader); + const pipeline_layout = try self.gc.device.createPipelineLayout(&.{}, null); + defer self.gc.device.destroyPipelineLayout(pipeline_layout, null); - gl.linkProgram(prog); - }, - .compute => |compute_pipeline| { - const compute_shader = try self.compileShader(compute_pipeline.compute.source, .compute); - defer gl.deleteShader(compute_shader); + var pipelines = [1]vk.Pipeline{.null_handle}; + _ = try self.gc.device.createGraphicsPipelines(self.gc.pipeline_cache, 1, &.{ + vk.GraphicsPipelineCreateInfo{ + .base_pipeline_index = 0, + .p_input_assembly_state = &vk.PipelineInputAssemblyStateCreateInfo{ + .primitive_restart_enable = vk.FALSE, + .topology = .triangle_list, + }, + .p_vertex_input_state = &vk.PipelineVertexInputStateCreateInfo{}, + .p_rasterization_state = &vk.PipelineRasterizationStateCreateInfo{ + .depth_clamp_enable = vk.FALSE, + .rasterizer_discard_enable = vk.FALSE, + .polygon_mode = .fill, + .cull_mode = .{ .back_bit = true }, + .front_face = .counter_clockwise, + .depth_bias_enable = vk.FALSE, + .depth_bias_constant_factor = 0.0, + .depth_bias_clamp = 0.0, + .depth_bias_slope_factor = 0.0, + .line_width = 1.0, + }, + .p_viewport_state = &vk.PipelineViewportStateCreateInfo{}, + .layout = pipeline_layout, + // .p_depth_stencil_state = &vk.PipelineDepthStencilStateCreateInfo{ + // .depth_test_enable = true, + // .depth_write_enable = true, + // .depth_compare_op = .less, + // .depth_bounds_test_enable = true, + // .stencil_test_enable = false, + // .front = std.mem.zeroes(vk.StencilOpState), + // .back = std.mem.zeroes(vk.StencilOpState), + // .min_depth_bounds = 0.0, + // .max_depth_bounds = 1.0, + // }, + .p_dynamic_state = &vk.PipelineDynamicStateCreateInfo{ + .dynamic_state_count = @intCast(dynamic_states.len), + .p_dynamic_states = &dynamic_states, + }, + .p_multisample_state = &vk.PipelineMultisampleStateCreateInfo{ + .sample_shading_enable = vk.FALSE, + .rasterization_samples = .{ .@"1_bit" = true }, + .min_sample_shading = 1.0, + .alpha_to_coverage_enable = vk.FALSE, + .alpha_to_one_enable = vk.FALSE, + }, + .p_color_blend_state = &vk.PipelineColorBlendStateCreateInfo{ + .logic_op_enable = vk.FALSE, + .logic_op = .copy, + .attachment_count = 0, + .blend_constants = [4]f32{ 0.0, 0.0, 0.0, 0.0 }, + }, + .stage_count = 2, + .p_stages = &.{ + vk.PipelineShaderStageCreateInfo{ + .module = vertex_shader_modle, + .stage = .{ .vertex_bit = true }, + .p_name = "main", + }, + vk.PipelineShaderStageCreateInfo{ + .module = fragment_shader_modle, + .stage = .{ .fragment_bit = true }, + .p_name = "main", + }, + }, + .subpass = 0, + }, + }, null, &pipelines); + break :blk pipelines[0]; + }, + .compute => |compute_pipeline| { + _ = compute_pipeline; // autofix + return error.ComputeNotSupportedYet; + // const compute_shader_module = try self.gc.device.createShaderModule(&.{ + // .code_size = compute_pipeline.compute.source.len, + // .p_code = compute_pipeline.compute.source.ptr, + // }, null); + // defer self.gc.device.destroyShaderModule(compute_shader_module, null); - gl.attachShader(prog, compute_shader); - defer gl.detachShader(prog, compute_shader); - - gl.linkProgram(prog); - }, - } - - var success: c_int = 0; - gl.getProgramiv(prog, gl.LINK_STATUS, &success); - - if (success == 0) { - var info_len: gl.GLint = 0; - gl.getProgramiv(prog, gl.INFO_LOG_LENGTH, &info_len); - if (info_len > 0) { - const info_log = try self.frame_arena.allocSentinel(u8, @intCast(info_len - 1), 0); - gl.getProgramInfoLog(prog, @intCast(info_log.len), null, info_log); - std.log.err("ERROR::PROGRAM::LINK_FAILED\n{s}\n", .{info_log}); - } else { - std.log.err("ERROR::PROGRAM::LINK_FAILED\nNo info log.\n", .{}); + // var pipelines = [1]vk.Pipeline{.null_handle}; + // _ = try self.gc.device.createGraphicsPipelines(self.gc.pipeline_cache, 1, &.{ + // vk.GraphicsPipelineCreateInfo{ + // .p_stages = &.{ + // vk.PipelineShaderStageCreateInfo{ + // .module = compute_shader_module, + // .stage = .{ .compute_bit = true }, + // .p_name = "main", + // }, + // }, + // .stage_count = 1, + // }, + // }, null, &pipelines); + // return pipelines[0]; + }, } - return error.ProgramLinkFailed; - } - - var program_length: gl.GLsizei = 0; - gl.getProgramiv(prog, gl.PROGRAM_BINARY_LENGTH, &program_length); - - if (program_length > 0) { - const program_binary = try self.frame_arena.allocSentinel(u8, @intCast(program_length - 1), 0); - var binary_format: gl.GLenum = gl.NONE; - var return_len: gl.GLsizei = 0; - gl.getProgramBinary(prog, program_length, &return_len, &binary_format, @ptrCast(program_binary.ptr)); - checkGLError(); - if (program_length == return_len) { - std.log.debug("Program {s} binary:\n{s}\n", .{ asset_manifest.getPath(id), program_binary[0..@intCast(return_len)] }); - } - } + }; const loaded_shader_program = LoadedShaderProgram{ - .program = prog, - .permuted_id = permuted_id, + .pipeline = pipeline, }; { self.rw_lock.lock(); defer self.rw_lock.unlock(); - try self.loaded_assets.put(self.allocator, permuted_id, .{ + try self.loaded_assets.put(self.allocator, id, .{ .shaderProgram = loaded_shader_program, }); try self.modified_times.put(self.allocator, id, data.modified); @@ -403,8 +464,7 @@ const NullShader = LoadedShader{ }; const NullShaderProgram = LoadedShaderProgram{ - .program = 0, - .permuted_id = 0, + .pipeline = .null_handle, }; const NullMesh = LoadedMesh{ @@ -693,8 +753,7 @@ const LoadedShader = struct { }; const LoadedShaderProgram = struct { - program: gl.GLuint, - permuted_id: AssetId, + pipeline: vk.Pipeline, }; pub const LoadedMesh = struct { @@ -1512,7 +1571,12 @@ fn freeAsset(self: *AssetManager, asset: *LoadedAsset) void { self.allocator.free(shader.source); }, .shaderProgram => |*program| { - gl.deleteProgram(program.program); + self.gc.queues.graphics.mu.lock(); + defer self.gc.queues.graphics.mu.unlock(); + + self.gc.device.queueWaitIdle(self.gc.queues.graphics.handle) catch @panic("Wait Idle failed"); + + self.gc.device.destroyPipeline(program.pipeline, null); }, .texture => |*texture| { gl.GL_ARB_bindless_texture.makeTextureHandleNonResidentARB(texture.handle); diff --git a/src/GraphicsContext.zig b/src/GraphicsContext.zig index 51b327c..4f65131 100644 --- a/src/GraphicsContext.zig +++ b/src/GraphicsContext.zig @@ -13,20 +13,19 @@ const apis: []const vk.ApiInfo = &.{ vk.extensions.khr_swapchain, }; +pub const Instance = vk.InstanceProxy(apis); +pub const Device = vk.DeviceProxy(apis); +pub const CommandBuffer = vk.CommandBufferProxy(apis); + const BaseDispatch = vk.BaseWrapper(apis); const InstanceDispatch = vk.InstanceWrapper(apis); -const Instance = vk.InstanceProxy(apis); -const Device = vk.DeviceProxy(apis); const DeviceDispatch = Device.Wrapper; -const CommandBuffer = vk.CommandBufferProxy(apis); const device_extensions = [_][:0]const u8{ vk.extensions.khr_swapchain.name, }; const vk_layers = [_][:0]const u8{"VK_LAYER_KHRONOS_validation"}; -const MAX_FRAME_LAG = 3; - allocator: std.mem.Allocator = undefined, window: *c.SDL_Window = undefined, vkb: BaseDispatch = undefined, @@ -41,19 +40,20 @@ swapchain: vk.SwapchainKHR = .null_handle, swapchain_extent: vk.Extent2D = .{ .width = 0, .height = 0 }, swapchain_images: []vk.Image = &.{}, -// NOTE: TEST -frame: u32 = 0, -frame_syncs: [MAX_FRAME_LAG]Sync = [1]Sync{.{}} ** MAX_FRAME_LAG, -command_pool: vk.CommandPool = .null_handle, +pipeline_cache: vk.PipelineCache = .null_handle, -const Sync = struct { - acquire_swapchain_image: vk.Semaphore = .null_handle, - draw_sema: vk.Semaphore = .null_handle, - draw_fence: vk.Fence = .null_handle, +pub const CommandPool = struct { + device: Device, + handle: vk.CommandPool, - pub fn waitForDrawAndReset(self: *Sync, gc: *GraphicsContext) !void { - _ = try gc.device.waitForFences(1, &.{self.draw_fence}, vk.TRUE, std.math.maxInt(u64)); - try gc.device.resetFences(1, &.{self.draw_fence}); + pub fn allocateCommandBuffer(self: *const CommandPool) !CommandBuffer { + var cmd_bufs = [_]vk.CommandBuffer{.null_handle}; + try self.device.allocateCommandBuffers(&.{ + .command_pool = self.handle, + .level = .primary, + .command_buffer_count = cmd_bufs.len, + }, &cmd_bufs); + return CommandBuffer.init(cmd_bufs[0], self.device.wrapper); } }; @@ -110,7 +110,10 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL const queue_config = try selectQueues(self.instance, self.device_info.physical_device); const device_create_config = vk.DeviceCreateInfo{ - .p_next = @ptrCast(&vk.PhysicalDeviceSynchronization2Features{ .synchronization_2 = vk.TRUE }), + .p_next = &vk.PhysicalDeviceVulkan13Features{ + .dynamic_rendering = vk.TRUE, + .synchronization_2 = vk.TRUE, + }, .p_queue_create_infos = &queue_config.queue_create_info, .queue_create_info_count = queue_config.queue_count, .p_enabled_features = &self.device_info.features, @@ -158,140 +161,42 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL .device_to_host = device_to_host_queue, }; - for (0..MAX_FRAME_LAG) |i| { - self.frame_syncs[i].acquire_swapchain_image = try self.device.createSemaphore(&.{}, null); - self.frame_syncs[i].draw_sema = try self.device.createSemaphore(&.{}, null); - self.frame_syncs[i].draw_fence = try self.device.createFence(&.{ .flags = .{ .signaled_bit = true } }, null); - } - self.command_pool = try self.queues.graphics.createCommandPool(.{}); + self.pipeline_cache = try self.device.createPipelineCache(&.{}, null); } -pub fn draw(self: *GraphicsContext) !void { - var cmd_bufs = [_]vk.CommandBuffer{.null_handle}; - try self.device.allocateCommandBuffers(&.{ - .command_pool = self.command_pool, - .level = .primary, - .command_buffer_count = cmd_bufs.len, - }, &cmd_bufs); - const test_cmd_buf = CommandBuffer.init(cmd_bufs[0], &self.vkd); +pub fn acquireSwapchainImage(self: *GraphicsContext, acuire_semaphore: vk.Semaphore) !u32 { + var found = false; + var swapchain_img: u32 = 0; - const sync = &self.frame_syncs[self.frame]; + try self.maybeResizeSwapchain(); - try sync.waitForDrawAndReset(self); + while (!found) { + const acquire_result = try self.device.acquireNextImageKHR(self.swapchain, std.math.maxInt(u64), acuire_semaphore, .null_handle); - // Move this out into a separate func - const swapchain_image_index: u32 = blk: { - var found = false; - var swapchain_img: u32 = 0; - - try self.maybeResizeSwapchain(); - - while (!found) { - const acquire_result = try self.device.acquireNextImageKHR(self.swapchain, std.math.maxInt(u64), sync.acquire_swapchain_image, .null_handle); - - switch (acquire_result.result) { - .success, .suboptimal_khr => { - swapchain_img = acquire_result.image_index; - found = true; - }, - .error_out_of_date_khr => { - // TODO: resize swapchain - std.debug.print("Out of date swapchain\n", .{}); - try self.maybeResizeSwapchain(); - }, - .error_surface_lost_khr => { - // TODO: recreate surface - return error.SurfaceLost; - }, - .not_ready => return error.SwapchainImageNotReady, - .timeout => return error.SwapchainImageTimeout, - else => { - std.debug.print("Unexpected value: {}\n", .{acquire_result.result}); - @panic("Unexpected"); - }, - } - } - - break :blk swapchain_img; - }; - - const current_image = self.swapchain_images[swapchain_image_index]; - - try test_cmd_buf.beginCommandBuffer(&.{}); - { - { - const img_barrier = vk.ImageMemoryBarrier2{ - .image = current_image, - .old_layout = .undefined, - .new_layout = .transfer_dst_optimal, - .src_access_mask = .{}, - .dst_access_mask = .{}, - .src_queue_family_index = self.queues.graphics.family, - .dst_queue_family_index = self.queues.graphics.family, - .subresource_range = .{ - .aspect_mask = .{ .color_bit = true }, - .base_array_layer = 0, - .base_mip_level = 0, - .layer_count = 1, - .level_count = 1, - }, - }; - test_cmd_buf.pipelineBarrier2(&.{ - .p_image_memory_barriers = &.{img_barrier}, - .image_memory_barrier_count = 1, - }); - } - test_cmd_buf.clearColorImage(current_image, .transfer_dst_optimal, &.{ .float_32 = .{ 0.8, 0.7, 0.6, 1.0 } }, 1, &.{.{ - .aspect_mask = .{ .color_bit = true }, - .base_array_layer = 0, - .base_mip_level = 0, - .layer_count = 1, - .level_count = 1, - }}); - { - const img_barrier = vk.ImageMemoryBarrier2{ - .image = current_image, - .old_layout = .transfer_dst_optimal, - .new_layout = .present_src_khr, - .src_access_mask = .{}, - .dst_access_mask = .{}, - .src_queue_family_index = self.queues.graphics.family, - .dst_queue_family_index = self.queues.graphics.family, - .subresource_range = .{ - .aspect_mask = .{ .color_bit = true }, - .base_array_layer = 0, - .base_mip_level = 0, - .layer_count = 1, - .level_count = 1, - }, - }; - test_cmd_buf.pipelineBarrier2(&.{ - .p_image_memory_barriers = &.{img_barrier}, - .image_memory_barrier_count = 1, - }); + switch (acquire_result.result) { + .success, .suboptimal_khr => { + swapchain_img = acquire_result.image_index; + found = true; + }, + .error_out_of_date_khr => { + // TODO: resize swapchain + std.debug.print("Out of date swapchain\n", .{}); + try self.maybeResizeSwapchain(); + }, + .error_surface_lost_khr => { + // TODO: recreate surface + return error.SurfaceLost; + }, + .not_ready => return error.SwapchainImageNotReady, + .timeout => return error.SwapchainImageTimeout, + else => { + std.debug.print("Unexpected value: {}\n", .{acquire_result.result}); + @panic("Unexpected"); + }, } } - try test_cmd_buf.endCommandBuffer(); - try self.queues.graphics.submit( - &SubmitInfo{ - .wait_semaphores = &.{sync.acquire_swapchain_image}, - .wait_dst_stage_mask = &.{.{ .transfer_bit = true }}, - .command_buffers = &.{test_cmd_buf.handle}, - .signal_semaphores = &.{sync.draw_sema}, - }, - sync.draw_fence, - ); - - _ = try self.device.queuePresentKHR(self.queues.graphics.handle, &.{ - .swapchain_count = 1, - .wait_semaphore_count = 1, - .p_wait_semaphores = &.{sync.draw_sema}, - .p_swapchains = &.{self.swapchain}, - .p_image_indices = &.{swapchain_image_index}, - }); - - self.frame = (self.frame + 1) % MAX_FRAME_LAG; + return swapchain_img; } fn maybeResizeSwapchain(self: *GraphicsContext) !void { @@ -353,11 +258,14 @@ pub const QueueInstance = struct { handle: vk.Queue, family: u32, - pub fn createCommandPool(self: *Self, flags: vk.CommandPoolCreateFlags) !vk.CommandPool { - return self.device.createCommandPool(&.{ - .flags = flags, - .queue_family_index = self.family, - }, null); + pub fn createCommandPool(self: *Self, flags: vk.CommandPoolCreateFlags) !CommandPool { + return .{ + .handle = try self.device.createCommandPool(&.{ + .flags = flags, + .queue_family_index = self.family, + }, null), + .device = self.device, + }; } pub fn submit(self: *Self, info: *const SubmitInfo, fence: vk.Fence) Device.QueueSubmitError!void { diff --git a/src/Render2.zig b/src/Render2.zig new file mode 100644 index 0000000..5e012eb --- /dev/null +++ b/src/Render2.zig @@ -0,0 +1,181 @@ +const std = @import("std"); +const AssetManager = @import("AssetManager.zig"); +const GraphicsContext = @import("GraphicsContext.zig"); +const vk = @import("vk"); +const a = @import("asset_manifest"); + +const Render2 = @This(); + +const MAX_FRAME_LAG = 3; + +assetman: *AssetManager, +gc: *GraphicsContext, +command_pool: GraphicsContext.CommandPool, + +// NOTE: TEST +frame: u32 = 0, +frame_syncs: [MAX_FRAME_LAG]Sync = [1]Sync{.{}} ** MAX_FRAME_LAG, + +pub fn init(assetman: *AssetManager, gc: *GraphicsContext) !Render2 { + var self = Render2{ + .assetman = assetman, + .gc = gc, + .command_pool = try gc.queues.graphics.createCommandPool(.{}), + }; + + // NOTE: TEST + for (0..MAX_FRAME_LAG) |i| { + self.frame_syncs[i].acquire_swapchain_image = try self.gc.device.createSemaphore(&.{}, null); + self.frame_syncs[i].draw_sema = try self.gc.device.createSemaphore(&.{}, null); + self.frame_syncs[i].draw_fence = try self.gc.device.createFence(&.{ .flags = .{ .signaled_bit = true } }, null); + } + return self; +} + +pub fn draw(self: *Render2) !void { + const sync = &self.frame_syncs[self.frame]; + + try sync.waitForDrawAndReset(self.gc.device); + + // Move this out into a separate func + const swapchain_image_index: u32 = try self.gc.acquireSwapchainImage(sync.acquire_swapchain_image); + + const current_image = self.gc.swapchain_images[swapchain_image_index]; + const current_image_view = try self.gc.device.createImageView(&.{ + .components = .{ .r = .r, .g = .g, .b = .b, .a = .a }, + .format = .r8g8b8a8_unorm, + .view_type = .@"2d", + .subresource_range = .{ + .aspect_mask = .{ .color_bit = true }, + .base_array_layer = 0, + .base_mip_level = 0, + .layer_count = 1, + .level_count = 1, + }, + .image = current_image, + }, null); + defer self.gc.device.destroyImageView(current_image_view, null); + + const cmds = try self.command_pool.allocateCommandBuffer(); + + try cmds.beginCommandBuffer(&.{}); + { + { + const img_barrier = vk.ImageMemoryBarrier2{ + .image = current_image, + .old_layout = .undefined, + .new_layout = .color_attachment_optimal, + .src_access_mask = .{}, + .dst_access_mask = .{ .color_attachment_write_bit = true }, + .dst_stage_mask = .{ .color_attachment_output_bit = true }, + .src_queue_family_index = self.gc.queues.graphics.family, + .dst_queue_family_index = self.gc.queues.graphics.family, + .subresource_range = .{ + .aspect_mask = .{ .color_bit = true }, + .base_array_layer = 0, + .base_mip_level = 0, + .layer_count = 1, + .level_count = 1, + }, + }; + cmds.pipelineBarrier2(&.{ + .p_image_memory_barriers = &.{img_barrier}, + .image_memory_barrier_count = 1, + }); + } + + { + cmds.beginRendering(&.{ + .render_area = vk.Rect2D{ .offset = .{ .x = 0, .y = 0 }, .extent = self.gc.swapchain_extent }, + .layer_count = 1, + .view_mask = 0, + .color_attachment_count = 1, + .p_color_attachments = &.{ + vk.RenderingAttachmentInfo{ + .clear_value = .{ .color = .{ .float_32 = .{ 0.8, 0.7, 0.6, 1.0 } } }, + .load_op = .clear, + .store_op = .store, + .image_layout = .color_attachment_optimal, + .image_view = current_image_view, + .resolve_image_layout = .color_attachment_optimal, + .resolve_mode = .{}, + }, + }, + }); + defer cmds.endRendering(); + + cmds.bindPipeline(.graphics, self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.triangle).pipeline); + + cmds.setViewportWithCount(1, &.{vk.Viewport{ + .x = 0, + .y = 0, + .width = @floatFromInt(self.gc.swapchain_extent.width), + .height = @floatFromInt(self.gc.swapchain_extent.height), + .min_depth = 0, + .max_depth = 1, + }}); + cmds.setScissorWithCount(1, &.{vk.Rect2D{ + .offset = .{ .x = 0, .y = 0 }, + .extent = self.gc.swapchain_extent, + }}); + + cmds.draw(3, 1, 0, 0); + } + + { + const img_barrier = vk.ImageMemoryBarrier2{ + .image = current_image, + .old_layout = .color_attachment_optimal, + .new_layout = .present_src_khr, + .src_access_mask = .{ .color_attachment_write_bit = true }, + .dst_access_mask = .{}, + .src_stage_mask = .{ .color_attachment_output_bit = true }, + .src_queue_family_index = self.gc.queues.graphics.family, + .dst_queue_family_index = self.gc.queues.graphics.family, + .subresource_range = .{ + .aspect_mask = .{ .color_bit = true }, + .base_array_layer = 0, + .base_mip_level = 0, + .layer_count = 1, + .level_count = 1, + }, + }; + cmds.pipelineBarrier2(&.{ + .p_image_memory_barriers = &.{img_barrier}, + .image_memory_barrier_count = 1, + }); + } + } + try cmds.endCommandBuffer(); + + try self.gc.queues.graphics.submit( + &GraphicsContext.SubmitInfo{ + .wait_semaphores = &.{sync.acquire_swapchain_image}, + .wait_dst_stage_mask = &.{.{ .transfer_bit = true }}, + .command_buffers = &.{cmds.handle}, + .signal_semaphores = &.{sync.draw_sema}, + }, + sync.draw_fence, + ); + + _ = try self.gc.device.queuePresentKHR(self.gc.queues.graphics.handle, &.{ + .swapchain_count = 1, + .wait_semaphore_count = 1, + .p_wait_semaphores = &.{sync.draw_sema}, + .p_swapchains = &.{self.gc.swapchain}, + .p_image_indices = &.{swapchain_image_index}, + }); + + self.frame = (self.frame + 1) % MAX_FRAME_LAG; +} + +const Sync = struct { + acquire_swapchain_image: vk.Semaphore = .null_handle, + draw_sema: vk.Semaphore = .null_handle, + draw_fence: vk.Fence = .null_handle, + + pub fn waitForDrawAndReset(self: *Sync, device: GraphicsContext.Device) !void { + _ = try device.waitForFences(1, &.{self.draw_fence}, vk.TRUE, std.math.maxInt(u64)); + try device.resetFences(1, &.{self.draw_fence}); + } +}; diff --git a/src/formats.zig b/src/formats.zig index 01e2e3f..eeeea61 100644 --- a/src/formats.zig +++ b/src/formats.zig @@ -153,13 +153,16 @@ pub const ShaderProgram = union(ShaderProgramPipelineType) { if (!serializer.write) { self.* = .{ .graphics = undefined }; } + try serializer.skipAlign(4); try serializer.serializeByteSlice(&self.graphics.vertex.source); + try serializer.skipAlign(4); try serializer.serializeByteSlice(&self.graphics.fragment.source); }, .compute => { if (!serializer.write) { self.* = .{ .compute = undefined }; } + try serializer.skipAlign(4); try serializer.serializeByteSlice(&self.compute.compute.source); }, } @@ -217,11 +220,23 @@ pub const Serializer = struct { } } + pub fn skipAlign(self: *Serializer, alignment: u64) !void { + const pos = try self.stream.getPos(); + const new_pos = std.mem.alignForward(u64, pos, alignment); + try self.stream.seekTo(new_pos); + } + pub fn serializeBytes(self: *Serializer, data: *[]u8) !void { if (self.write) { _ = try self.stream.write(data.*); } else { - _ = try self.stream.read(data.*); + switch (self.stream) { + .buffer => |*buf| { + data.* = buf.buffer[buf.pos .. buf.pos + data.len]; + buf.seekBy(@intCast(data.len)) catch @panic("INVALID READ STREAM SIZE"); + }, + else => @panic("UNSUPPORTED READ STREAM"), + } } } diff --git a/src/game.zig b/src/game.zig index 1265d40..a71685b 100644 --- a/src/game.zig +++ b/src/game.zig @@ -6,6 +6,7 @@ const c = @import("sdl.zig"); // const gl = @import("gl.zig"); const AssetManager = @import("AssetManager.zig"); const Render = @import("Render.zig"); +const Render2 = @import("Render2.zig"); const formats = @import("formats.zig"); const za = @import("zalgebra"); const Vec2 = za.Vec2; @@ -173,7 +174,8 @@ export fn game_init(global_allocator: *std.mem.Allocator) void { globals.g_mem.* = .{ .global_allocator = global_allocator.*, .frame_fba = std.heap.FixedBufferAllocator.init(frame_arena_buffer), - .assetman = AssetManager.init(global_allocator.*, globals.g_mem.frame_fba.allocator()), + .assetman = AssetManager.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_init.gc), + .render2 = Render2.init(&globals.g_mem.assetman, &globals.g_init.gc) catch @panic("OOM"), // .render = Render.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_mem.assetman), .world = .{ .frame_arena = globals.g_mem.frame_fba.allocator() }, }; @@ -461,7 +463,7 @@ export fn game_update() bool { } } - ginit.gc.draw() catch @panic("draw error"); + gmem.render2.draw() catch @panic("RENDER ERROR"); // Render if (false) { diff --git a/src/globals.zig b/src/globals.zig index f296315..d0ed809 100644 --- a/src/globals.zig +++ b/src/globals.zig @@ -1,6 +1,7 @@ const std = @import("std"); const c = @import("sdl.zig"); const Render = @import("Render.zig"); +const Render2 = @import("Render2.zig"); const AssetManager = @import("AssetManager.zig"); const World = @import("entity.zig").World; const GraphicsContext = @import("GraphicsContext.zig"); @@ -36,6 +37,7 @@ pub const GameMemory = struct { frame_fba: std.heap.FixedBufferAllocator, assetman: AssetManager, render: Render = undefined, + render2: Render2, performance_frequency: u64 = 0, last_frame_time: u64 = 0, delta_time: f32 = 0.0000001,