Create descriptor set layout, pipeline, vertex buffer and index buffer

This commit is contained in:
2025-11-22 17:00:17 +01:00
parent c3efeaf452
commit d6a4b8c1fe
11 changed files with 395 additions and 62 deletions

View File

@@ -2,6 +2,7 @@ const Game = @This();
const std = @import("std");
const glfw = @import("zglfw");
const vk = @import("vulkan");
const math = @import("math.zig");
@@ -9,14 +10,32 @@ const Atoms = @import("assets/Atoms.zig");
const Textures = @import("assets/Textures.zig");
const Materials = @import("assets/Materials.zig");
const Swapchain = @import("engine/Swapchain.zig");
const VertexBuffer = @import("engine/VertexBuffer.zig");
const IndexBuffer = @import("engine/IndexBuffer.zig");
const Iterator3 = math.Iterator3;
const Matrix4x4 = math.Matrix4x4;
const Quaternion = math.Quaternion;
const Vector2 = math.Vector2;
const Vector3 = math.Vector3;
const Vertex = extern struct {
positionOS: [3]f32,
texCoord: [2]u16,
normalOS: [3]i8,
tangentOS: [4]i8,
};
const main_vert_spv align(4) = @embedFile("shaders/main_vert.spv").*;
const main_frag_spv align(4) = @embedFile("shaders/main_frag.spv").*;
allocator: std.mem.Allocator,
swapchain: *Swapchain,
global_descriptor_set_layout: vk.DescriptorSetLayout,
per_object_descriptor_set_layout: vk.DescriptorSetLayout,
pipeline_layout: vk.PipelineLayout,
pipeline: vk.Pipeline,
vertex_buffer: VertexBuffer,
index_buffer: IndexBuffer,
atoms: Atoms,
materials: Materials,
@@ -52,6 +71,271 @@ pub fn init(allocator: std.mem.Allocator, swapchain: *Swapchain) !Game {
errdefer textures.deinit(swapchain.engine);
_ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Bricks.json"));
_ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Dirt.json"));
_ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Gold.json"));
_ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Stone.json"));
const device = swapchain.engine.device;
const interface = &swapchain.engine.vk_allocator.interface;
const global_descriptor_set_layout_bindings = [_]vk.DescriptorSetLayoutBinding{
.{
.binding = 0,
.descriptor_type = .uniform_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 1,
.descriptor_type = .storage_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 2,
.descriptor_type = .storage_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 3,
.descriptor_type = .storage_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 4,
.descriptor_type = .sampler,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 5,
.descriptor_type = .sampled_image,
.descriptor_count = 1024,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
};
const global_descriptor_set_layout_bindings_flags = [_]vk.DescriptorBindingFlags{
.{},
.{},
.{},
.{},
.{},
.{ .variable_descriptor_count_bit = true },
};
std.debug.assert(global_descriptor_set_layout_bindings.len == global_descriptor_set_layout_bindings_flags.len);
const global_descriptor_set_layout = try device.createDescriptorSetLayout(&.{
.p_next = &vk.DescriptorSetLayoutBindingFlagsCreateInfo{
.binding_count = global_descriptor_set_layout_bindings_flags.len,
.p_binding_flags = &global_descriptor_set_layout_bindings_flags,
},
.binding_count = global_descriptor_set_layout_bindings.len,
.p_bindings = &global_descriptor_set_layout_bindings,
}, interface);
errdefer device.destroyDescriptorSetLayout(global_descriptor_set_layout, interface);
const per_object_descriptor_set_layout_bindings = [_]vk.DescriptorSetLayoutBinding{
.{
.binding = 0,
.descriptor_type = .uniform_buffer_dynamic,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
};
const per_object_descriptor_set_layout = try device.createDescriptorSetLayout(&.{
.binding_count = per_object_descriptor_set_layout_bindings.len,
.p_bindings = &per_object_descriptor_set_layout_bindings,
}, interface);
errdefer device.destroyDescriptorSetLayout(per_object_descriptor_set_layout, interface);
const descriptor_set_layouts = [_]vk.DescriptorSetLayout{
global_descriptor_set_layout,
per_object_descriptor_set_layout,
};
const pipeline_layout = try device.createPipelineLayout(&.{
.set_layout_count = descriptor_set_layouts.len,
.p_set_layouts = &descriptor_set_layouts,
}, interface);
errdefer device.destroyPipelineLayout(pipeline_layout, interface);
const vertex_shader = try device.createShaderModule(&.{
.code_size = main_vert_spv.len,
.p_code = @ptrCast(&main_vert_spv),
}, interface);
defer device.destroyShaderModule(vertex_shader, interface);
const fragment_shader = try device.createShaderModule(&.{
.code_size = main_frag_spv.len,
.p_code = @ptrCast(&main_frag_spv),
}, interface);
defer device.destroyShaderModule(fragment_shader, interface);
var vertex_buffer: VertexBuffer = try .init(swapchain.engine, Vertex, 4);
errdefer vertex_buffer.deinit(swapchain.engine);
try vertex_buffer.write(Vertex, swapchain.engine, &.{
.{
.positionOS = .{ -0.5, -0.5, 0 },
.texCoord = .{ 0, 65535 },
.normalOS = .{ 127, 0, 0 },
.tangentOS = .{ 127, 0, 0, -127 },
},
.{
.positionOS = .{ 0.5, -0.5, 0 },
.texCoord = .{ 65535, 65535 },
.normalOS = .{ 127, 0, 0 },
.tangentOS = .{ 127, 0, 0, -127 },
},
.{
.positionOS = .{ -0.5, 0.5, 0 },
.texCoord = .{ 0, 0 },
.normalOS = .{ 127, 0, 0 },
.tangentOS = .{ 127, 0, 0, -127 },
},
.{
.positionOS = .{ 0.5, 0.5, 0 },
.texCoord = .{ 65535, 0 },
.normalOS = .{ 127, 0, 0 },
.tangentOS = .{ 127, 0, 0, -127 },
},
});
var index_buffer: IndexBuffer = try .init(swapchain.engine, 6);
errdefer index_buffer.deinit(swapchain.engine);
try index_buffer.write(swapchain.engine, &.{ 0, 1, 2, 2, 1, 3 });
const pipeline_shader_stages = [_]vk.PipelineShaderStageCreateInfo{
.{
.stage = .{ .vertex_bit = true },
.module = vertex_shader,
.p_name = "main",
},
.{
.stage = .{ .fragment_bit = true },
.module = fragment_shader,
.p_name = "main",
},
};
const vertex_input_binding_descriptions = [_]vk.VertexInputBindingDescription{
.{
.binding = 0,
.stride = @sizeOf(Vertex),
.input_rate = .vertex,
},
};
const vertex_input_attribute_descriptions = [_]vk.VertexInputAttributeDescription{
.{
.location = 0,
.binding = 0,
.format = .r32g32b32_sfloat,
.offset = @offsetOf(Vertex, "positionOS"),
},
.{
.location = 1,
.binding = 0,
.format = .r16g16_unorm,
.offset = @offsetOf(Vertex, "texCoord"),
},
.{
.location = 2,
.binding = 0,
.format = .r8g8b8_snorm,
.offset = @offsetOf(Vertex, "normalOS"),
},
.{
.location = 3,
.binding = 0,
.format = .r8g8b8a8_snorm,
.offset = @offsetOf(Vertex, "tangentOS"),
},
};
const pipeline_color_blend_attachment_states = [_]vk.PipelineColorBlendAttachmentState{
.{
.blend_enable = .false,
.src_color_blend_factor = .one,
.dst_color_blend_factor = .zero,
.color_blend_op = .add,
.src_alpha_blend_factor = .one,
.dst_alpha_blend_factor = .zero,
.alpha_blend_op = .add,
.color_write_mask = .{
.r_bit = true,
.g_bit = true,
.b_bit = true,
.a_bit = true,
},
},
};
const dynamic_states = [_]vk.DynamicState{ .viewport, .scissor };
var pipeline: vk.Pipeline = undefined;
_ = try device.createGraphicsPipelines(.null_handle, 1, &.{
.{
.stage_count = pipeline_shader_stages.len,
.p_stages = &pipeline_shader_stages,
.p_vertex_input_state = &.{
.vertex_binding_description_count = vertex_input_binding_descriptions.len,
.p_vertex_binding_descriptions = &vertex_input_binding_descriptions,
.vertex_attribute_description_count = vertex_input_attribute_descriptions.len,
.p_vertex_attribute_descriptions = &vertex_input_attribute_descriptions,
},
.p_input_assembly_state = &.{
.topology = .triangle_list,
.primitive_restart_enable = .false,
},
.p_viewport_state = &.{
.viewport_count = 1,
.p_viewports = undefined,
.scissor_count = 1,
.p_scissors = undefined,
},
.p_rasterization_state = &.{
.depth_clamp_enable = .false,
.rasterizer_discard_enable = .false,
.polygon_mode = .fill,
.cull_mode = .{ .back_bit = true },
.front_face = .counter_clockwise,
.depth_bias_enable = .false,
.depth_bias_constant_factor = 0,
.depth_bias_clamp = 0,
.depth_bias_slope_factor = 0,
.line_width = 1,
},
.p_multisample_state = &.{
.rasterization_samples = .{ .@"1_bit" = true },
.sample_shading_enable = .false,
.min_sample_shading = 1,
.alpha_to_coverage_enable = .false,
.alpha_to_one_enable = .false,
},
.p_depth_stencil_state = null,
.p_color_blend_state = &.{
.logic_op_enable = .false,
.logic_op = .copy,
.attachment_count = pipeline_color_blend_attachment_states.len,
.p_attachments = &pipeline_color_blend_attachment_states,
.blend_constants = .{ 0, 0, 0, 0 },
},
.p_dynamic_state = &.{
.dynamic_state_count = dynamic_states.len,
.p_dynamic_states = &dynamic_states,
},
.layout = pipeline_layout,
.render_pass = swapchain.render_pass,
.subpass = 0,
.base_pipeline_index = -1,
},
}, interface, @ptrCast(&pipeline));
errdefer device.destroyPipeline(pipeline, interface);
return .{
.allocator = allocator,
@@ -59,10 +343,27 @@ pub fn init(allocator: std.mem.Allocator, swapchain: *Swapchain) !Game {
.atoms = atoms,
.materials = materials,
.textures = textures,
.global_descriptor_set_layout = global_descriptor_set_layout,
.per_object_descriptor_set_layout = per_object_descriptor_set_layout,
.pipeline_layout = pipeline_layout,
.pipeline = pipeline,
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
};
}
pub fn deinit(self: *Game) void {
const device = self.swapchain.engine.device;
const interface = &self.swapchain.engine.vk_allocator.interface;
self.vertex_buffer.deinit(self.swapchain.engine);
self.index_buffer.deinit(self.swapchain.engine);
device.destroyPipeline(self.pipeline, interface);
device.destroyPipelineLayout(self.pipeline_layout, interface);
device.destroyDescriptorSetLayout(self.per_object_descriptor_set_layout, interface);
device.destroyDescriptorSetLayout(self.global_descriptor_set_layout, interface);
self.textures.deinit(self.swapchain.engine);
self.materials.deinit(self.swapchain.engine);
self.atoms.deinit();
@@ -76,6 +377,10 @@ pub fn update(self: *Game, dt: f32) void {
).rotate(self.camera_yaw).mulScalar(player_speed * dt);
self.camera_position = Vector3.add(self.camera_position, camera_d.asVector3(0));
self.render() catch |err| {
std.log.err("Failed to render: {s}", .{@errorName(err)});
};
}
pub fn onKeyDown(self: *Game, key_code: glfw.Key, mods: glfw.Mods) void {
@@ -127,3 +432,62 @@ pub fn onMouseMove(self: *Game, dx: f32, dy: f32) void {
self.camera_pitch = std.math.clamp(self.camera_pitch, -0.5 * std.math.pi, 0.5 * std.math.pi);
self.camera_yaw = @mod(self.camera_yaw, 2 * std.math.pi);
}
fn render(self: *Game) !void {
const engine = self.swapchain.engine;
const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface);
defer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
const command_buffer = try engine.allocateGraphicsCommandBuffer();
defer engine.freeGraphicsCommandBuffer(command_buffer);
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
const viewports = [_]vk.Viewport{
.{
.x = 0,
.y = 0,
.width = 0,
.height = 0,
.min_depth = 0,
.max_depth = 1,
},
};
command_buffer.setViewport(0, viewports.len, &viewports);
const scissors = [_]vk.Rect2D{
.{
.offset = .{ .x = 0, .y = 0 },
.extent = .{ .width = 0, .height = 0 },
},
};
command_buffer.setScissor(0, scissors.len, &scissors);
{
const clear_values = [_]vk.ClearValue{
.{ .color = .{ .float_32 = .{ 0, 0, 0, 1 } } },
};
command_buffer.beginRenderPass(&.{
.render_pass = self.swapchain.render_pass,
.framebuffer = self.swapchain.swapchain_images[0].framebuffer,
.render_area = .{
.offset = .{ .x = 0, .y = 0 },
.extent = .{ .width = 0, .height = 0 },
},
.clear_value_count = clear_values.len,
.p_clear_values = &clear_values,
}, .@"inline");
defer command_buffer.endRenderPass();
command_buffer.bindPipeline(.graphics, self.pipeline);
command_buffer.bindVertexBuffers(0, 1, @ptrCast(&self.vertex_buffer.buffer), &.{0});
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
command_buffer.drawIndexed(@intCast(self.index_buffer.index_count), 1, 0, 0, 0);
}
try command_buffer.endCommandBuffer();
try engine.submitGraphicsCommandBuffer(command_buffer, fence);
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
}