Vulkan TRIANGLE!!!

This commit is contained in:
sergeypdev 2024-09-25 10:59:06 +04:00
parent f0c9e04887
commit fe69fa1f51
7 changed files with 410 additions and 227 deletions

View File

@ -1,12 +1,21 @@
#if VERTEX_SHADER #if VERTEX_SHADER
vec2 positions[3] = vec2[]( 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.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() { void main() {
VertexColor = colors[gl_VertexIndex];
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
} }
@ -14,10 +23,12 @@ void main() {
#if FRAGMENT_SHADER #if FRAGMENT_SHADER
layout(location = 0) in vec3 VertexColor;
layout(location = 0) out vec4 FragColor; layout(location = 0) out vec4 FragColor;
void main() { void main() {
FragColor = vec4(0.6, 0.8, 1.0, 1.0); FragColor = vec4(VertexColor, 1.0);
} }
#endif #endif

View File

@ -27,6 +27,8 @@ const Vec3 = @import("zalgebra").Vec3;
const Mat4 = @import("zalgebra").Mat4; const Mat4 = @import("zalgebra").Mat4;
const sdl = @import("sdl.zig"); const sdl = @import("sdl.zig");
const tracy = @import("tracy"); const tracy = @import("tracy");
const vk = @import("vk");
const GraphicsContext = @import("GraphicsContext.zig");
pub const AssetId = assets.AssetId; pub const AssetId = assets.AssetId;
pub const Handle = assets.Handle; pub const Handle = assets.Handle;
@ -56,6 +58,7 @@ rw_lock: std.Thread.RwLock.DefaultRwLock = .{},
asset_watcher: AssetWatcher = undefined, asset_watcher: AssetWatcher = undefined,
vertex_heap: VertexBufferHeap, vertex_heap: VertexBufferHeap,
gc: *GraphicsContext,
const AssetWatcher = struct { const AssetWatcher = struct {
assetman: *AssetManager, 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; 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_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"); 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, .frame_arena = frame_arena,
.exe_dir = exe_dir, .exe_dir = exe_dir,
.vertex_heap = VertexBufferHeap.init(allocator) catch @panic("OOM"), .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); 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 { 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; if (handle.id == 0) return NullShaderProgram;
const permuted_asset_id = permuteAssetIdDefines(handle.id, defines); if (self.resolveAsset(handle.id)) |asset| {
if (self.resolveAsset(permuted_asset_id)) |asset| {
switch (asset.*) { switch (asset.*) {
.shaderProgram => |shader| { .shaderProgram => |shader| {
return 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 { pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) LoadedMesh {
@ -307,88 +309,147 @@ pub const ShaderProgramDefinition = struct {
compute: []const u8, compute: []const u8,
}; };
pub fn loadShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram, permuted_id: AssetId, defines: []const DefinePair) LoadedShaderProgram { pub fn loadShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) LoadedShaderProgram {
return self.loadShaderProgramErr(handle.id, permuted_id, defines) catch |err| { return self.loadShaderProgramErr(handle.id) catch |err| {
std.log.err("Failed to load shader program {}\n", .{err}); std.log.err("Failed to load shader program {}\n", .{err});
return NullShaderProgram; 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); 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; var program: formats.ShaderProgram = undefined;
try program.serialize(&serializer); try program.serialize(&serializer);
const prog = gl.createProgram(); const pipeline = blk: {
errdefer gl.deleteProgram(prog); 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) { const dynamic_states = [_]vk.DynamicState{
.graphics => |graphics_pipeline| { .viewport_with_count,
const vertex_shader = try self.compileShader(graphics_pipeline.vertex.source, .vertex); .scissor_with_count,
defer gl.deleteShader(vertex_shader); };
const fragment_shader = try self.compileShader(graphics_pipeline.fragment.source, .fragment);
defer gl.deleteShader(fragment_shader);
gl.attachShader(prog, vertex_shader); const pipeline_layout = try self.gc.device.createPipelineLayout(&.{}, null);
defer gl.detachShader(prog, vertex_shader); defer self.gc.device.destroyPipelineLayout(pipeline_layout, null);
gl.attachShader(prog, fragment_shader);
defer gl.detachShader(prog, fragment_shader);
gl.linkProgram(prog); var pipelines = [1]vk.Pipeline{.null_handle};
}, _ = try self.gc.device.createGraphicsPipelines(self.gc.pipeline_cache, 1, &.{
.compute => |compute_pipeline| { vk.GraphicsPipelineCreateInfo{
const compute_shader = try self.compileShader(compute_pipeline.compute.source, .compute); .base_pipeline_index = 0,
defer gl.deleteShader(compute_shader); .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); // var pipelines = [1]vk.Pipeline{.null_handle};
defer gl.detachShader(prog, compute_shader); // _ = try self.gc.device.createGraphicsPipelines(self.gc.pipeline_cache, 1, &.{
// vk.GraphicsPipelineCreateInfo{
gl.linkProgram(prog); // .p_stages = &.{
}, // vk.PipelineShaderStageCreateInfo{
} // .module = compute_shader_module,
// .stage = .{ .compute_bit = true },
var success: c_int = 0; // .p_name = "main",
gl.getProgramiv(prog, gl.LINK_STATUS, &success); // },
// },
if (success == 0) { // .stage_count = 1,
var info_len: gl.GLint = 0; // },
gl.getProgramiv(prog, gl.INFO_LOG_LENGTH, &info_len); // }, null, &pipelines);
if (info_len > 0) { // return pipelines[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", .{});
} }
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{ const loaded_shader_program = LoadedShaderProgram{
.program = prog, .pipeline = pipeline,
.permuted_id = permuted_id,
}; };
{ {
self.rw_lock.lock(); self.rw_lock.lock();
defer self.rw_lock.unlock(); 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, .shaderProgram = loaded_shader_program,
}); });
try self.modified_times.put(self.allocator, id, data.modified); try self.modified_times.put(self.allocator, id, data.modified);
@ -403,8 +464,7 @@ const NullShader = LoadedShader{
}; };
const NullShaderProgram = LoadedShaderProgram{ const NullShaderProgram = LoadedShaderProgram{
.program = 0, .pipeline = .null_handle,
.permuted_id = 0,
}; };
const NullMesh = LoadedMesh{ const NullMesh = LoadedMesh{
@ -693,8 +753,7 @@ const LoadedShader = struct {
}; };
const LoadedShaderProgram = struct { const LoadedShaderProgram = struct {
program: gl.GLuint, pipeline: vk.Pipeline,
permuted_id: AssetId,
}; };
pub const LoadedMesh = struct { pub const LoadedMesh = struct {
@ -1512,7 +1571,12 @@ fn freeAsset(self: *AssetManager, asset: *LoadedAsset) void {
self.allocator.free(shader.source); self.allocator.free(shader.source);
}, },
.shaderProgram => |*program| { .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| { .texture => |*texture| {
gl.GL_ARB_bindless_texture.makeTextureHandleNonResidentARB(texture.handle); gl.GL_ARB_bindless_texture.makeTextureHandleNonResidentARB(texture.handle);

View File

@ -13,20 +13,19 @@ const apis: []const vk.ApiInfo = &.{
vk.extensions.khr_swapchain, 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 BaseDispatch = vk.BaseWrapper(apis);
const InstanceDispatch = vk.InstanceWrapper(apis); const InstanceDispatch = vk.InstanceWrapper(apis);
const Instance = vk.InstanceProxy(apis);
const Device = vk.DeviceProxy(apis);
const DeviceDispatch = Device.Wrapper; const DeviceDispatch = Device.Wrapper;
const CommandBuffer = vk.CommandBufferProxy(apis);
const device_extensions = [_][:0]const u8{ const device_extensions = [_][:0]const u8{
vk.extensions.khr_swapchain.name, vk.extensions.khr_swapchain.name,
}; };
const vk_layers = [_][:0]const u8{"VK_LAYER_KHRONOS_validation"}; const vk_layers = [_][:0]const u8{"VK_LAYER_KHRONOS_validation"};
const MAX_FRAME_LAG = 3;
allocator: std.mem.Allocator = undefined, allocator: std.mem.Allocator = undefined,
window: *c.SDL_Window = undefined, window: *c.SDL_Window = undefined,
vkb: BaseDispatch = undefined, vkb: BaseDispatch = undefined,
@ -41,19 +40,20 @@ swapchain: vk.SwapchainKHR = .null_handle,
swapchain_extent: vk.Extent2D = .{ .width = 0, .height = 0 }, swapchain_extent: vk.Extent2D = .{ .width = 0, .height = 0 },
swapchain_images: []vk.Image = &.{}, swapchain_images: []vk.Image = &.{},
// NOTE: TEST pipeline_cache: vk.PipelineCache = .null_handle,
frame: u32 = 0,
frame_syncs: [MAX_FRAME_LAG]Sync = [1]Sync{.{}} ** MAX_FRAME_LAG,
command_pool: vk.CommandPool = .null_handle,
const Sync = struct { pub const CommandPool = struct {
acquire_swapchain_image: vk.Semaphore = .null_handle, device: Device,
draw_sema: vk.Semaphore = .null_handle, handle: vk.CommandPool,
draw_fence: vk.Fence = .null_handle,
pub fn waitForDrawAndReset(self: *Sync, gc: *GraphicsContext) !void { pub fn allocateCommandBuffer(self: *const CommandPool) !CommandBuffer {
_ = try gc.device.waitForFences(1, &.{self.draw_fence}, vk.TRUE, std.math.maxInt(u64)); var cmd_bufs = [_]vk.CommandBuffer{.null_handle};
try gc.device.resetFences(1, &.{self.draw_fence}); 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 queue_config = try selectQueues(self.instance, self.device_info.physical_device);
const device_create_config = vk.DeviceCreateInfo{ 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, .p_queue_create_infos = &queue_config.queue_create_info,
.queue_create_info_count = queue_config.queue_count, .queue_create_info_count = queue_config.queue_count,
.p_enabled_features = &self.device_info.features, .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, .device_to_host = device_to_host_queue,
}; };
for (0..MAX_FRAME_LAG) |i| { self.pipeline_cache = try self.device.createPipelineCache(&.{}, null);
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(.{});
} }
pub fn draw(self: *GraphicsContext) !void { pub fn acquireSwapchainImage(self: *GraphicsContext, acuire_semaphore: vk.Semaphore) !u32 {
var cmd_bufs = [_]vk.CommandBuffer{.null_handle}; var found = false;
try self.device.allocateCommandBuffers(&.{ var swapchain_img: u32 = 0;
.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);
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 switch (acquire_result.result) {
const swapchain_image_index: u32 = blk: { .success, .suboptimal_khr => {
var found = false; swapchain_img = acquire_result.image_index;
var swapchain_img: u32 = 0; found = true;
},
try self.maybeResizeSwapchain(); .error_out_of_date_khr => {
// TODO: resize swapchain
while (!found) { std.debug.print("Out of date swapchain\n", .{});
const acquire_result = try self.device.acquireNextImageKHR(self.swapchain, std.math.maxInt(u64), sync.acquire_swapchain_image, .null_handle); try self.maybeResizeSwapchain();
},
switch (acquire_result.result) { .error_surface_lost_khr => {
.success, .suboptimal_khr => { // TODO: recreate surface
swapchain_img = acquire_result.image_index; return error.SurfaceLost;
found = true; },
}, .not_ready => return error.SwapchainImageNotReady,
.error_out_of_date_khr => { .timeout => return error.SwapchainImageTimeout,
// TODO: resize swapchain else => {
std.debug.print("Out of date swapchain\n", .{}); std.debug.print("Unexpected value: {}\n", .{acquire_result.result});
try self.maybeResizeSwapchain(); @panic("Unexpected");
}, },
.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,
});
} }
} }
try test_cmd_buf.endCommandBuffer();
try self.queues.graphics.submit( return swapchain_img;
&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;
} }
fn maybeResizeSwapchain(self: *GraphicsContext) !void { fn maybeResizeSwapchain(self: *GraphicsContext) !void {
@ -353,11 +258,14 @@ pub const QueueInstance = struct {
handle: vk.Queue, handle: vk.Queue,
family: u32, family: u32,
pub fn createCommandPool(self: *Self, flags: vk.CommandPoolCreateFlags) !vk.CommandPool { pub fn createCommandPool(self: *Self, flags: vk.CommandPoolCreateFlags) !CommandPool {
return self.device.createCommandPool(&.{ return .{
.flags = flags, .handle = try self.device.createCommandPool(&.{
.queue_family_index = self.family, .flags = flags,
}, null); .queue_family_index = self.family,
}, null),
.device = self.device,
};
} }
pub fn submit(self: *Self, info: *const SubmitInfo, fence: vk.Fence) Device.QueueSubmitError!void { pub fn submit(self: *Self, info: *const SubmitInfo, fence: vk.Fence) Device.QueueSubmitError!void {

181
src/Render2.zig Normal file
View File

@ -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});
}
};

View File

@ -153,13 +153,16 @@ pub const ShaderProgram = union(ShaderProgramPipelineType) {
if (!serializer.write) { if (!serializer.write) {
self.* = .{ .graphics = undefined }; self.* = .{ .graphics = undefined };
} }
try serializer.skipAlign(4);
try serializer.serializeByteSlice(&self.graphics.vertex.source); try serializer.serializeByteSlice(&self.graphics.vertex.source);
try serializer.skipAlign(4);
try serializer.serializeByteSlice(&self.graphics.fragment.source); try serializer.serializeByteSlice(&self.graphics.fragment.source);
}, },
.compute => { .compute => {
if (!serializer.write) { if (!serializer.write) {
self.* = .{ .compute = undefined }; self.* = .{ .compute = undefined };
} }
try serializer.skipAlign(4);
try serializer.serializeByteSlice(&self.compute.compute.source); 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 { pub fn serializeBytes(self: *Serializer, data: *[]u8) !void {
if (self.write) { if (self.write) {
_ = try self.stream.write(data.*); _ = try self.stream.write(data.*);
} else { } 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"),
}
} }
} }

View File

@ -6,6 +6,7 @@ const c = @import("sdl.zig");
// const gl = @import("gl.zig"); // const gl = @import("gl.zig");
const AssetManager = @import("AssetManager.zig"); const AssetManager = @import("AssetManager.zig");
const Render = @import("Render.zig"); const Render = @import("Render.zig");
const Render2 = @import("Render2.zig");
const formats = @import("formats.zig"); const formats = @import("formats.zig");
const za = @import("zalgebra"); const za = @import("zalgebra");
const Vec2 = za.Vec2; const Vec2 = za.Vec2;
@ -173,7 +174,8 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
globals.g_mem.* = .{ globals.g_mem.* = .{
.global_allocator = global_allocator.*, .global_allocator = global_allocator.*,
.frame_fba = std.heap.FixedBufferAllocator.init(frame_arena_buffer), .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), // .render = Render.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_mem.assetman),
.world = .{ .frame_arena = globals.g_mem.frame_fba.allocator() }, .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 // Render
if (false) { if (false) {

View File

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const c = @import("sdl.zig"); const c = @import("sdl.zig");
const Render = @import("Render.zig"); const Render = @import("Render.zig");
const Render2 = @import("Render2.zig");
const AssetManager = @import("AssetManager.zig"); const AssetManager = @import("AssetManager.zig");
const World = @import("entity.zig").World; const World = @import("entity.zig").World;
const GraphicsContext = @import("GraphicsContext.zig"); const GraphicsContext = @import("GraphicsContext.zig");
@ -36,6 +37,7 @@ pub const GameMemory = struct {
frame_fba: std.heap.FixedBufferAllocator, frame_fba: std.heap.FixedBufferAllocator,
assetman: AssetManager, assetman: AssetManager,
render: Render = undefined, render: Render = undefined,
render2: Render2,
performance_frequency: u64 = 0, performance_frequency: u64 = 0,
last_frame_time: u64 = 0, last_frame_time: u64 = 0,
delta_time: f32 = 0.0000001, delta_time: f32 = 0.0000001,