Renderer code
This commit is contained in:
53
src/StorageBuffer.zig
Normal file
53
src/StorageBuffer.zig
Normal 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).?;
|
||||
}
|
||||
};
|
||||
}
|
||||
372
src/game.zig
372
src/game.zig
@@ -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{
|
||||
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),
|
||||
const PointLight = extern struct {
|
||||
position_ws: [3]f32,
|
||||
color: [3]f32,
|
||||
};
|
||||
|
||||
const index_buffer = [_]u16{ 0, 1, 2, 2, 1, 3 };
|
||||
const DirectionalLight = extern struct {
|
||||
direction_ws: [3]f32,
|
||||
color: [3]f32,
|
||||
};
|
||||
|
||||
pub fn init() void {}
|
||||
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();
|
||||
|
||||
index_buffer = IndexBuffer.init(6);
|
||||
index_buffer.write(0, &[_]u16{ 0, 1, 2, 2, 1, 3 });
|
||||
errdefer index_buffer.deinit();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user