Renderer code

This commit is contained in:
2025-02-16 22:01:42 +01:00
parent e101346d4f
commit 439d8d3b25
3 changed files with 409 additions and 18 deletions

53
src/StorageBuffer.zig Normal file
View File

@@ -0,0 +1,53 @@
const zgpu = @import("zgpu");
const main = @import("main.zig");
pub fn StorageBuffer(comptime T: type) type {
return struct {
storage_buffer_handle: zgpu.BufferHandle,
pub fn init(desired_capacity: usize) @This() {
// NOTE For some reason, the method function call syntax doesn't compile
const storage_buffer_handle = zgpu.GraphicsContext.createBuffer(main.gctx, .{
.usage = .{ .copy_dst = true, .storage = true },
.size = desired_capacity * @sizeOf(T),
});
return .{
.storage_buffer_handle = storage_buffer_handle,
};
}
pub fn deinit(self: *@This()) void {
main.gctx.releaseResource(self.storage_buffer_handle);
self.* = undefined;
}
pub fn capacity(self: @This()) usize {
return @divExact(self.info().size, @sizeOf(T));
}
pub fn write(self: @This(), offset: usize, data: []const T) void {
main.gctx.queue.writeBuffer(self.obj(), offset, T, data);
}
pub fn ensureCapacityDiscard(self: *@This(), desired_capacity: usize) void {
if (self.capacity() < desired_capacity) {
main.gctx.releaseResource(self.storage_buffer_handle);
// NOTE For some reason, the method function call syntax doesn't compile
self.storage_buffer_handle = zgpu.GraphicsContext.createBuffer(main.gctx, .{
.usage = .{ .copy_dst = true, .storage = true },
.size = desired_capacity * @sizeOf(T),
});
}
}
pub fn info(self: @This()) zgpu.BufferInfo {
return main.gctx.lookupResourceInfo(self.vertex_buffer_handle).?;
}
pub fn obj(self: @This()) zgpu.wgpu.Buffer {
return main.gctx.lookupResource(self.vertex_buffer_handle).?;
}
};
}

View File

@@ -2,38 +2,239 @@ const std = @import("std");
const zglfw = @import("zglfw");
const zgui = @import("zgui");
const zgpu = @import("zgpu");
const main = @import("main.zig");
var show_console: bool = false;
var show_demo_window: bool = false;
const IndexBuffer = @import("IndexBuffer.zig");
const Samplers = @import("Samplers.zig");
const StorageBuffer = @import("StorageBuffer.zig").StorageBuffer;
const Texture = @import("Texture.zig");
const VertexBuffer = @import("VertexBuffer.zig").VertexBuffer;
const Vertex = extern struct {
position: [3]f32,
position_os: [3]f32,
tex_coord: [2]f32,
normal: [3]f32,
tangent: [4]f32,
normal_os: [3]f32,
tangent_os: [4]f32,
pub fn init(x: f32, y: f32, z: f32, u: f32, v: f32, nx: f32, ny: f32, nz: f32, tx: f32, ty: f32, tz: f32, tw: f32) Vertex {
return .{
.position = .{ x, y, z },
.position_os = .{ x, y, z },
.tex_coord = .{ u, v },
.normal = .{ nx, ny, nz },
.tangent = .{ tx, ty, tw, tz },
.normal_os = .{ nx, ny, nz },
.tangent_os = .{ tx, ty, tw, tz },
};
}
};
const vertex_buffer = [_]Vertex{
const PointLight = extern struct {
position_ws: [3]f32,
color: [3]f32,
};
const DirectionalLight = extern struct {
direction_ws: [3]f32,
color: [3]f32,
};
const GlobalUniforms = extern struct {
matrix_ws_to_vs: [16]f32,
matrix_vs_to_cs: [16]f32,
ambient_light: [3]f32,
point_light_count: u32,
directional_light_count: u32,
};
const ObjectUniforms = extern struct {
matrix_os_to_ws: [16]f32,
matrix_os_to_ws_normal: [16]f32,
};
var show_console: bool = false;
var show_demo_window: bool = false;
var global_bind_group_layout: zgpu.BindGroupLayoutHandle = undefined;
var per_material_bind_group_layout: zgpu.BindGroupLayoutHandle = undefined;
var per_object_bind_group_layout: zgpu.BindGroupLayoutHandle = undefined;
var vertex_buffer: VertexBuffer(Vertex) = undefined;
var index_buffer: IndexBuffer = undefined;
var block_pipeline: zgpu.RenderPipelineHandle = undefined;
var point_light_buffer: StorageBuffer(PointLight) = undefined;
var directional_light_buffer: StorageBuffer(DirectionalLight) = undefined;
var sampler: zgpu.SamplerHandle = undefined;
var base_color_texture: Texture = undefined;
var occlusion_roughness_metallic_texture: Texture = undefined;
var normal_texture: Texture = undefined;
var global_bind_group: zgpu.BindGroupHandle = undefined;
var per_material_bind_group: zgpu.BindGroupHandle = undefined;
var per_object_bind_group: zgpu.BindGroupHandle = undefined;
pub fn init() !void {
global_bind_group_layout = main.gctx.createBindGroupLayout(&.{
zgpu.bufferEntry(0, .{ .vertex = true, .fragment = true }, .uniform, true, 0),
zgpu.bufferEntry(1, .{ .fragment = true }, .read_only_storage, false, 0),
zgpu.bufferEntry(2, .{ .fragment = true }, .read_only_storage, false, 0),
zgpu.samplerEntry(3, .{ .fragment = true }, .filtering),
zgpu.textureEntry(4, .{ .fragment = true }, .float, .tvdim_2d_array, false),
zgpu.textureEntry(5, .{ .fragment = true }, .float, .tvdim_2d_array, false),
zgpu.textureEntry(6, .{ .fragment = true }, .float, .tvdim_2d_array, false),
});
errdefer main.gctx.releaseResource(global_bind_group_layout);
per_material_bind_group_layout = main.gctx.createBindGroupLayout(&.{
zgpu.bufferEntry(0, .{ .fragment = true }, .uniform, true, 0),
});
errdefer main.gctx.releaseResource(per_material_bind_group_layout);
per_object_bind_group_layout = main.gctx.createBindGroupLayout(&.{
zgpu.bufferEntry(0, .{ .fragment = true }, .uniform, true, 0),
});
errdefer main.gctx.releaseResource(per_object_bind_group_layout);
vertex_buffer = VertexBuffer(Vertex).init(4);
vertex_buffer.write(0, &[_]Vertex{
Vertex.init(-0.5, -0.5, 0, 0, 1, 0, 0, 1, 1, 0, 0, -1),
Vertex.init(0.5, -0.5, 0, 1, 1, 0, 0, 1, 1, 0, 0, -1),
Vertex.init(-0.5, 0.5, 0, 0, 0, 0, 0, 1, 1, 0, 0, -1),
Vertex.init(0.5, 0.5, 0, 1, 0, 0, 0, 1, 1, 0, 0, -1),
};
});
errdefer vertex_buffer.deinit();
const index_buffer = [_]u16{ 0, 1, 2, 2, 1, 3 };
index_buffer = IndexBuffer.init(6);
index_buffer.write(0, &[_]u16{ 0, 1, 2, 2, 1, 3 });
errdefer index_buffer.deinit();
pub fn init() void {}
block_pipeline = block_pipeline: {
const pipeline_layout = main.gctx.createPipelineLayout(&.{
global_bind_group_layout,
per_material_bind_group_layout,
per_object_bind_group_layout,
});
defer main.gctx.releaseResource(pipeline_layout);
const module = zgpu.createWgslShaderModule(main.gctx.device, @embedFile("../shaders/block.wgsl"), null);
defer module.release();
const vertex_buffers = [_]zgpu.wgpu.VertexBufferLayout{
vertexBufferLayoutFromStruct(Vertex),
};
const targets = [_]zgpu.wgpu.ColorTargetState{.{
.format = zgpu.GraphicsContext.swapchain_format,
.blend = &.{
.color = .{
.operation = .add,
.src_factor = .one,
.dst_factor = .one_minus_src_alpha,
},
.alpha = .{
.operation = .add,
.src_factor = .one,
.dst_factor = .one_minus_src_alpha,
},
},
}};
const pipeline_descriptor = zgpu.wgpu.RenderPipelineDescriptor{
.vertex = .{
.module = module,
.entry_point = "vert",
.buffer_count = vertex_buffers.len,
.buffers = &vertex_buffers,
},
.fragment = .{
.module = module,
.entry_point = "frag",
.target_count = targets.len,
.targets = &targets,
},
.primitive = .{
.cull_mode = .back,
},
};
break :block_pipeline main.gctx.createRenderPipeline(pipeline_layout, pipeline_descriptor);
};
errdefer main.gctx.releaseResource(block_pipeline);
point_light_buffer = StorageBuffer(PointLight).init(4);
errdefer point_light_buffer.deinit();
directional_light_buffer = StorageBuffer(DirectionalLight).init(4);
errdefer directional_light_buffer.deinit();
sampler = main.gctx.createSampler(.{
.address_mode_u = .repeat,
.address_mode_v = .repeat,
.address_mode_w = .repeat,
.mag_filter = .linear,
.min_filter = .linear,
.mipmap_filter = .linear,
});
errdefer main.gctx.releaseResource(sampler);
const tile_count = 16;
const base_color_texture_data = @embedFile("../library/textures/BaseColor");
const base_color_tile_size = std.math.sqrt(@divExact(base_color_texture_data.len, 4 * tile_count));
base_color_texture = Texture.init2DArray(base_color_tile_size, base_color_tile_size, tile_count);
errdefer base_color_texture.deinit();
main.gctx.queue.writeTexture(
.{ .texture = base_color_texture.texObj() },
.{ .bytes_per_row = 4 * base_color_tile_size, .rows_per_image = base_color_tile_size },
.{ .width = base_color_tile_size, .height = base_color_tile_size, .depth_or_array_layers = tile_count },
u8,
base_color_texture_data,
);
const occlusion_roughness_metallic_texture_data = @embedFile("../library/textures/OcclusionRoughnessMetallic");
const occlusion_roughness_metallic_tile_size = std.math.sqrt(@divExact(occlusion_roughness_metallic_texture_data.len, 4 * tile_count));
occlusion_roughness_metallic_texture = Texture.init2DArray(occlusion_roughness_metallic_tile_size, occlusion_roughness_metallic_tile_size, tile_count);
errdefer occlusion_roughness_metallic_texture.deinit();
main.gctx.queue.writeTexture(
.{ .texture = occlusion_roughness_metallic_texture.texObj() },
.{ .bytes_per_row = 4 * occlusion_roughness_metallic_tile_size, .rows_per_image = occlusion_roughness_metallic_tile_size },
.{ .width = occlusion_roughness_metallic_tile_size, .height = occlusion_roughness_metallic_tile_size, .depth_or_array_layers = tile_count },
u8,
occlusion_roughness_metallic_texture_data,
);
const normal_texture_data = @embedFile("../library/textures/Normal");
const normal_tile_size = std.math.sqrt(@divExact(normal_texture_data.len, 4 * tile_count));
normal_texture = Texture.init2DArray(normal_tile_size, normal_tile_size, tile_count);
errdefer normal_texture.deinit();
main.gctx.queue.writeTexture(
.{ .texture = normal_texture.texObj() },
.{ .bytes_per_row = 4 * normal_tile_size, .rows_per_image = normal_tile_size },
.{ .width = normal_tile_size, .height = normal_tile_size, .depth_or_array_layers = tile_count },
u8,
normal_texture_data,
);
global_bind_group = main.gctx.createBindGroup(global_bind_group_layout, &.{
.{ .binding = 0, .buffer_handle = main.gctx.uniforms.buffer, .size = @sizeOf(GlobalUniforms) },
.{ .binding = 1, .buffer_handle = point_light_buffer.storage_buffer_handle, .size = point_light_buffer.info().size },
.{ .binding = 2, .buffer_handle = directional_light_buffer.storage_buffer_handle, .size = directional_light_buffer.info().size },
.{ .binding = 3, .sampler_handle = sampler },
.{ .binding = 4, .texture_view_handle = base_color_texture.texture_view_handle },
.{ .binding = 5, .texture_view_handle = occlusion_roughness_metallic_texture.texture_view_handle },
.{ .binding = 6, .texture_view_handle = normal_texture.texture_view_handle },
});
errdefer main.gctx.releaseResource(global_bind_group);
per_material_bind_group = main.gctx.createBindGroup(per_material_bind_group, &.{
.{ .binding = 0, .buffer_handle = main.gctx.uniforms.buffer, .size = @sizeOf(u32) },
});
errdefer main.gctx.releaseResource(per_material_bind_group);
per_object_bind_group = main.gctx.createBindGroup(per_object_bind_group, &.{
.{ .binding = 0, .buffer_handle = main.gctx.uniforms.buffer, .size = @sizeOf(ObjectUniforms) },
});
errdefer main.gctx.releaseResource(per_object_bind_group);
}
pub fn update(dt: f32) void {
_ = dt;
@@ -45,6 +246,68 @@ pub fn update(dt: f32) void {
if (show_demo_window) {
zgui.showDemoWindow(&show_demo_window);
}
const back_buffer_view = main.gctx.swapchain.getCurrentTextureView();
defer back_buffer_view.release();
const commands = commands: {
const encoder = main.gctx.device.createCommandEncoder(null);
defer encoder.release();
{
const color_attachments = [_]zgpu.wgpu.RenderPassColorAttachment{.{
.view = back_buffer_view,
.load_op = .clear,
.store_op = .store,
.clear_value = .{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0 },
}};
const pass = encoder.beginRenderPass(.{
.color_attachment_count = color_attachments.len,
.color_attachments = &color_attachments,
});
defer {
pass.end();
pass.release();
}
const block_pipeline_obj = main.gctx.lookupResource(block_pipeline).?;
const global_bind_group_obj = main.gctx.lookupResource(global_bind_group).?;
const per_material_bind_group_obj = main.gctx.lookupResource(per_material_bind_group).?;
const per_object_bind_group_obj = main.gctx.lookupResource(per_object_bind_group).?;
pass.setPipeline(block_pipeline_obj);
const vb = vertex_buffer.info();
const ib = index_buffer.info();
pass.setVertexBuffer(0, vb.gpuobj.?, 0, vb.size);
pass.setIndexBuffer(ib.gpuobj.?, .uint16, 0, ib.size);
const global_uniforms_offsets = allocateUniform(GlobalUniforms{
.matrix_ws_to_vs = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
.matrix_vs_to_cs = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
.ambient_light = .{ 0, 0, 0 },
.point_light_count = 0,
.directional_light_count = 0,
});
pass.setBindGroup(0, global_bind_group_obj, &global_uniforms_offsets);
const per_material_offsets = allocateUniform(@as(u32, 0));
pass.setBindGroup(1, per_material_bind_group_obj, &per_material_offsets);
const per_object_offsets = allocateUniform(ObjectUniforms{
.matrix_os_to_ws = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
.matrix_os_to_ws_normal = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
});
pass.setBindGroup(2, per_object_bind_group_obj, &per_object_offsets);
pass.drawIndexed(@intCast(@divExact(ib.size, @sizeOf(u16))), 1, 0, 0, 0);
}
break :commands encoder.finish(null);
};
defer commands.release();
main.gctx.submit(&.{commands});
}
pub fn charCallback(
@@ -103,7 +366,9 @@ pub fn scrollCallback(
_ = yoffset;
}
pub fn deinit() void {}
pub fn deinit() void {
Samplers.deinit();
}
fn showConsole() void {
const display_size = zgui.io.getDisplaySize();
@@ -133,3 +398,76 @@ fn showConsole() void {
}
zgui.end();
}
fn allocateUniform(value: anytype) [1]u32 {
const mem = main.gctx.uniformsAllocate(@TypeOf(value), 1);
mem.slice[0] = value;
return [1]u32{mem.offset};
}
fn vertexAttributesFromStruct(comptime T: type) [structFieldCount(T)]zgpu.wgpu.VertexAttribute {
const struct_info = @typeInfo(T).Struct;
if (struct_info.layout != .Extern) {
@compileError("Vertex struct " ++ @typeName(T) ++ " does not have extern layout");
}
comptime var ret: [structFieldCount(T)]zgpu.wgpu.VertexAttribute = undefined;
inline for (struct_info.fields, 0..) |struct_field, i| {
const vertex_format: zgpu.wgpu.VertexFormat = switch (struct_field.type) {
[2]u8 => .uint8x2,
[4]u8 => .uint8x4,
[2]i8 => .sint8x2,
[4]i8 => .sint8x4,
[2]u16 => .uint16x2,
[4]u16 => .uint16x4,
[2]i16 => .sint16x2,
[4]i16 => .sint16x4,
[2]f16 => .float16x2,
[4]f16 => .float16x4,
f32 => .float32,
[1]f32 => .float32,
[2]f32 => .float32x2,
[3]f32 => .float32x3,
[4]f32 => .float32x4,
u32 => .uint32,
[1]u32 => .uint32,
[2]u32 => .uint32x2,
[3]u32 => .uint32x3,
[4]u32 => .uint32x4,
i32 => .sint32,
[1]i32 => .sint32,
[2]i32 => .sint32x2,
[3]i32 => .sint32x3,
[4]i32 => .sint32x4,
else => @compileError("Vertex attribute of type " ++ @typeName(struct_field.type) ++ " not supported"),
};
ret[i] = .{
.format = vertex_format,
.offset = @offsetOf(T, struct_field.name),
.shader_location = i,
};
}
return ret;
}
fn vertexBufferLayoutFromStruct(comptime T: type) zgpu.wgpu.VertexBufferLayout {
return .{
.array_stride = @sizeOf(T),
.step_mode = .vertex,
.attribute_count = structFieldCount(T),
.attributes = comptime &vertexAttributesFromStruct(T),
};
}
fn structFieldCount(comptime T: type) comptime_int {
return @typeInfo(T).Struct.fields.len;
}

View File

@@ -129,7 +129,7 @@ pub fn main() !void {
const zone_init_game = ztracy.ZoneN(@src(), "Init game");
game.init();
try game.init();
defer game.deinit();
zone_init_game.End();