commit 25834a22302c617d1de9bc0529fd77efdfdf87d1
parent 0fd02f7226880b8e349ce4c58b490ee885715921
Author: Christian Ermann <christianermann@gmail.com>
Date: Wed, 15 May 2024 16:49:57 -0400
Move render pipelines to separate file
Diffstat:
M | src/main.zig | | | 292 | +++---------------------------------------------------------------------------- |
A | src/render_pipeline.zig | | | 280 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 291 insertions(+), 281 deletions(-)
diff --git a/src/main.zig b/src/main.zig
@@ -3,6 +3,9 @@ const glfw = @import("mach_glfw");
const gpu = @import("mach_gpu");
const builtin = @import("builtin");
+const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
+const MeshRenderPipeline = @import("render_pipeline.zig").MeshRenderPipeline;
+
const Camera = @import("camera.zig");
const input = @import("input.zig");
@@ -46,7 +49,7 @@ fn glfwDetectBackendOptions() glfw.BackendOptions {
};
}
-const App = struct {
+pub const App = struct {
window: glfw.Window,
instance: *gpu.Instance,
surface: *gpu.Surface,
@@ -297,97 +300,15 @@ const AppSwapChain = struct {
target_descriptor: gpu.SwapChain.Descriptor,
};
-const RenderPipeline = struct {
- ptr: *anyopaque,
- frameFn: *const fn (ptr: *anyopaque, pass: *gpu.RenderPassEncoder) void,
-
- fn frame(self: *const RenderPipeline, pass: *gpu.RenderPassEncoder) void {
- return self.frameFn(self.ptr, pass);
- }
-};
-
-const DefaultRenderPipeline = struct {
- gpu_pipeline: *gpu.RenderPipeline,
-
- pub fn init(app: *App) DefaultRenderPipeline {
- const vs =
- \\ @vertex fn main(
- \\ @builtin(vertex_index) VertexIndex : u32
- \\ ) -> @builtin(position) vec4<f32> {
- \\ var pos = array<vec2<f32>, 3>(
- \\ vec2<f32>( 0.0, 0.5),
- \\ vec2<f32>(-0.5, -0.5),
- \\ vec2<f32>( 0.5, -0.5)
- \\ );
- \\ return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
- \\ }
- ;
- const vs_module = app.device.createShaderModuleWGSL("default vertex shader", vs);
- defer vs_module.release();
-
- const vertex = gpu.VertexState{
- .module = vs_module,
- .entry_point = "main",
- };
-
- const fs =
- \\ @fragment fn main() -> @location(0) vec4<f32> {
- \\ return vec4<f32>(1.0, 0.0, 0.0, 1.0);
- \\ }
- ;
- const fs_module = app.device.createShaderModuleWGSL("default fragment shader", fs);
- defer fs_module.release();
-
- const blend: gpu.BlendState = .{};
- const color_target = gpu.ColorTargetState{
- .format = app.swap_chain.format,
- .blend = &blend,
- .write_mask = gpu.ColorWriteMaskFlags.all,
- };
- const fragment = gpu.FragmentState.init(.{
- .module = fs_module,
- .entry_point = "main",
- .targets = &.{color_target},
- });
-
- const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
- .label = "default render pipeline",
- .fragment = &fragment,
- .layout = null,
- .depth_stencil = null,
- .vertex = vertex,
- .multisample = .{},
- .primitive = .{},
- };
-
- return .{
- .gpu_pipeline = app.device.createRenderPipeline(&pipeline_descriptor),
- };
- }
-
- pub fn frame(ptr: *anyopaque, pass: *gpu.RenderPassEncoder) void {
- const self: *DefaultRenderPipeline = @ptrCast(@alignCast(ptr));
- pass.setPipeline(self.gpu_pipeline);
- pass.draw(3, 1, 0, 0);
- }
-
- pub fn pipeline(self: *DefaultRenderPipeline) RenderPipeline {
- return .{
- .ptr = self,
- .frameFn = frame,
- };
- }
-};
-
-const f32x3 = @Vector(3, f32);
-const f32x4 = @Vector(4, f32);
-const mat4 = [4]@Vector(4, f32);
+pub const f32x3 = @Vector(3, f32);
+pub const f32x4 = @Vector(4, f32);
+pub const mat4 = [4]@Vector(4, f32);
const InstanceData = struct {
model_matrix: mat4,
};
-const UniformData = struct {
+pub const UniformData = struct {
view_matrix: mat4,
proj_matrix: mat4,
};
@@ -605,197 +526,6 @@ pub const Mesh = struct {
}
};
-const UnlitRenderPipeline = struct {
- gpu_pipeline: *gpu.RenderPipeline,
- mesh_buffer: *const MeshBuffer,
- bind_group: *gpu.BindGroup,
- uniform_buffer: *gpu.Buffer,
-
- pub fn init(app: *App, mesh_buffer: *const MeshBuffer, _: std.mem.Allocator) !UnlitRenderPipeline {
- const vs =
- \\ struct VertexInput {
- \\ @location(0) position: vec4<f32>,
- \\ @location(1) normal: vec3<f32>,
- \\ };
- \\
- \\ struct VertexOutput {
- \\ @builtin(position) clip_position: vec4<f32>,
- \\ @location(0) normal: vec3<f32>,
- \\ };
- \\
- \\ @group(0) @binding(0)
- \\ var<storage, read> instances: array<Instance>;
- \\ struct Instance {
- \\ model_matrix: mat4x4<f32>,
- \\ };
- \\
- \\ @group(0) @binding(1)
- \\ var<uniform> uniform_data: UniformData;
- \\ struct UniformData {
- \\ view_matrix: mat4x4<f32>,
- \\ proj_matrix: mat4x4<f32>,
- \\ };
- \\
- \\ @vertex fn main(
- \\ in: VertexInput,
- \\ @builtin(instance_index) idx: u32,
- \\ ) -> VertexOutput {
- \\ let model_matrix = instances[idx].model_matrix;
- \\ let camera_matrix = uniform_data.proj_matrix * uniform_data.view_matrix;
- \\ let matrix = camera_matrix * model_matrix;
- \\ return VertexOutput(matrix * in.position, in.normal);
- \\ }
- ;
- const vs_module = app.device.createShaderModuleWGSL("default vertex shader", vs);
- defer vs_module.release();
-
- const layouts = UnlitRenderPipeline.bufferLayouts();
- const vertex = gpu.VertexState.init(.{
- .module = vs_module,
- .entry_point = "main",
- .buffers = &layouts,
- });
-
- const fs =
- \\ struct VertexOutput {
- \\ @builtin(position) position: vec4<f32>,
- \\ @location(0) normal: vec3<f32>,
- \\ };
- \\
- \\ @fragment fn main(in: VertexOutput) -> @location(0) vec4<f32> {
- \\ let color = (in.normal + 1.0) * 0.5;
- \\ return vec4<f32>(color, 1.0);
- \\ }
- ;
- const fs_module = app.device.createShaderModuleWGSL("default fragment shader", fs);
- defer fs_module.release();
-
- const blend: gpu.BlendState = .{};
- const color_target = gpu.ColorTargetState{
- .format = app.swap_chain.format,
- .blend = &blend,
- .write_mask = gpu.ColorWriteMaskFlags.all,
- };
- const fragment = gpu.FragmentState.init(.{
- .module = fs_module,
- .entry_point = "main",
- .targets = &.{color_target},
- });
-
- const buffer_descriptor = gpu.Buffer.Descriptor{
- .size = @sizeOf(UniformData),
- .usage = .{ .uniform = true, .copy_dst = true },
- .mapped_at_creation = .false,
- };
- const uniform_buffer = app.device.createBuffer(&buffer_descriptor);
-
- const bind_group_layout = UnlitRenderPipeline.bindGroupLayout(app.device);
- const bind_group_descriptor = gpu.BindGroup.Descriptor.init(.{
- .layout = bind_group_layout,
- .entries = &.{
- gpu.BindGroup.Entry.buffer(0, mesh_buffer.instances.data, 0, mesh_buffer.instances.size),
- gpu.BindGroup.Entry.buffer(1, uniform_buffer, 0, @sizeOf(UniformData)),
- },
- });
- const bind_group = app.device.createBindGroup(&bind_group_descriptor);
-
- const pipeline_layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
- .bind_group_layouts = &.{bind_group_layout},
- });
- const pipeline_layout = app.device.createPipelineLayout(&pipeline_layout_descriptor);
- defer pipeline_layout.release();
-
- const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
- .label = "default render pipeline",
- .fragment = &fragment,
- .layout = pipeline_layout,
- .depth_stencil = &.{
- .format = .depth24_plus,
- .depth_write_enabled = .true,
- .depth_compare = .less,
- },
- .vertex = vertex,
- .multisample = .{},
- .primitive = .{
- .topology = .triangle_list,
- .front_face = .ccw,
- .cull_mode = .back,
- },
- };
-
- return .{
- .gpu_pipeline = app.device.createRenderPipeline(&pipeline_descriptor),
- .mesh_buffer = mesh_buffer,
- .bind_group = bind_group,
- .uniform_buffer = uniform_buffer,
- };
- }
-
- fn bufferLayouts() [3]gpu.VertexBufferLayout {
- const positions = gpu.VertexBufferLayout.init(.{
- .array_stride = @sizeOf(f32x4),
- .step_mode = .vertex,
- .attributes = &.{
- .{ .format = .float32x4, .shader_location = 0, .offset = 0 },
- },
- });
- const normals = gpu.VertexBufferLayout.init(.{
- .array_stride = @sizeOf(f32x4),
- .step_mode = .vertex,
- .attributes = &.{
- .{ .format = .float32x3, .shader_location = 1, .offset = 0 },
- },
- });
- const tex_coords = gpu.VertexBufferLayout.init(.{
- .array_stride = @sizeOf(f32x4),
- .step_mode = .vertex,
- .attributes = &.{
- .{ .format = .float32x3, .shader_location = 2, .offset = 0 },
- },
- });
- return .{ positions, normals, tex_coords };
- }
-
- fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
- const descriptor = gpu.BindGroupLayout.Descriptor.init(.{
- .entries = &.{
- gpu.BindGroupLayout.Entry.buffer(0, .{
- .vertex = true,
- .fragment = false,
- }, .read_only_storage, false, 0),
- gpu.BindGroupLayout.Entry.buffer(1, .{
- .vertex = true,
- .fragment = false,
- }, .uniform, false, 0),
- },
- });
- return device.createBindGroupLayout(&descriptor);
- }
-
- pub fn frame(ptr: *anyopaque, pass: *gpu.RenderPassEncoder) void {
- const self: *UnlitRenderPipeline = @ptrCast(@alignCast(ptr));
- pass.setPipeline(self.gpu_pipeline);
-
- pass.setVertexBuffer(0, self.mesh_buffer.positions.data, 0, self.mesh_buffer.positions.size);
- pass.setVertexBuffer(1, self.mesh_buffer.normals.data, 0, self.mesh_buffer.normals.size);
- pass.setVertexBuffer(2, self.mesh_buffer.tex_coords.data, 0, self.mesh_buffer.tex_coords.size);
- pass.setBindGroup(0, self.bind_group, null);
- pass.setIndexBuffer(self.mesh_buffer.indices.data, .uint32, 0, self.mesh_buffer.indices.size);
-
- for (0..self.mesh_buffer.meshes.items.len) |mesh_idx| {
- const offset = mesh_idx * 5 * @sizeOf(u32);
- pass.drawIndexedIndirect(self.mesh_buffer.indirect.data, offset);
- }
- }
-
- pub fn pipeline(self: *UnlitRenderPipeline) RenderPipeline {
- return .{
- .ptr = self,
- .frameFn = frame,
- };
- }
-};
-
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
@@ -830,9 +560,9 @@ pub fn main() !void {
.offset = .{ 0, -0.5, 0 },
});
- var drp = try UnlitRenderPipeline.init(&app, &mesh_buffer, allocator);
+ var rp = try MeshRenderPipeline.init(&app, &mesh_buffer, allocator);
- const pipelines = [_]RenderPipeline{drp.pipeline()};
+ const pipelines = [_]RenderPipeline{rp.pipeline()};
try app.inputs.append(camera.input.userInput());
@@ -846,7 +576,7 @@ pub fn main() !void {
camera.update(0.01);
camera.view(&uniform_data.view_matrix);
camera.proj(&uniform_data.proj_matrix);
- app.queue.writeBuffer(drp.uniform_buffer, 0, &[1]UniformData{uniform_data});
+ app.queue.writeBuffer(rp.uniform_buffer, 0, &[1]UniformData{uniform_data});
try app.frame(&pipelines);
}
diff --git a/src/render_pipeline.zig b/src/render_pipeline.zig
@@ -0,0 +1,280 @@
+const std = @import("std");
+const gpu = @import("mach_gpu");
+
+const App = @import("main.zig").App;
+const MeshBuffer = @import("main.zig").MeshBuffer;
+const UniformData = @import("main.zig").UniformData;
+const f32x4 = @import("main.zig").f32x4;
+
+pub const RenderPipeline = struct {
+ ptr: *anyopaque,
+ frameFn: *const fn (ptr: *anyopaque, pass: *gpu.RenderPassEncoder) void,
+
+ pub fn frame(self: *const RenderPipeline, pass: *gpu.RenderPassEncoder) void {
+ return self.frameFn(self.ptr, pass);
+ }
+};
+
+pub const TriangleRenderPipeline = struct {
+ gpu_pipeline: *gpu.RenderPipeline,
+
+ pub fn init(app: *App) TriangleRenderPipeline {
+ const vs =
+ \\ @vertex fn main(
+ \\ @builtin(vertex_index) VertexIndex : u32
+ \\ ) -> @builtin(position) vec4<f32> {
+ \\ var pos = array<vec2<f32>, 3>(
+ \\ vec2<f32>( 0.0, 0.5),
+ \\ vec2<f32>(-0.5, -0.5),
+ \\ vec2<f32>( 0.5, -0.5)
+ \\ );
+ \\ return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+ \\ }
+ ;
+ const vs_module = app.device.createShaderModuleWGSL("example vertex shader", vs);
+ defer vs_module.release();
+
+ const vertex = gpu.VertexState{
+ .module = vs_module,
+ .entry_point = "main",
+ };
+
+ const fs =
+ \\ @fragment fn main() -> @location(0) vec4<f32> {
+ \\ return vec4<f32>(1.0, 0.0, 0.0, 1.0);
+ \\ }
+ ;
+ const fs_module = app.device.createShaderModuleWGSL("example fragment shader", fs);
+ defer fs_module.release();
+
+ const blend: gpu.BlendState = .{};
+ const color_target = gpu.ColorTargetState{
+ .format = app.swap_chain.format,
+ .blend = &blend,
+ .write_mask = gpu.ColorWriteMaskFlags.all,
+ };
+ const fragment = gpu.FragmentState.init(.{
+ .module = fs_module,
+ .entry_point = "main",
+ .targets = &.{color_target},
+ });
+
+ const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
+ .label = "example render pipeline",
+ .fragment = &fragment,
+ .layout = null,
+ .depth_stencil = null,
+ .vertex = vertex,
+ .multisample = .{},
+ .primitive = .{},
+ };
+
+ return .{
+ .gpu_pipeline = app.device.createRenderPipeline(&pipeline_descriptor),
+ };
+ }
+
+ pub fn frame(ptr: *anyopaque, pass: *gpu.RenderPassEncoder) void {
+ const self: *TriangleRenderPipeline = @ptrCast(@alignCast(ptr));
+ pass.setPipeline(self.gpu_pipeline);
+ pass.draw(3, 1, 0, 0);
+ }
+
+ pub fn pipeline(self: *TriangleRenderPipeline) RenderPipeline {
+ return .{
+ .ptr = self,
+ .frameFn = frame,
+ };
+ }
+};
+
+pub const MeshRenderPipeline = struct {
+ gpu_pipeline: *gpu.RenderPipeline,
+ mesh_buffer: *const MeshBuffer,
+ bind_group: *gpu.BindGroup,
+ uniform_buffer: *gpu.Buffer,
+
+ pub fn init(app: *App, mesh_buffer: *const MeshBuffer, _: std.mem.Allocator) !MeshRenderPipeline {
+ const vs =
+ \\ struct VertexInput {
+ \\ @location(0) position: vec4<f32>,
+ \\ @location(1) normal: vec3<f32>,
+ \\ };
+ \\
+ \\ struct VertexOutput {
+ \\ @builtin(position) clip_position: vec4<f32>,
+ \\ @location(0) normal: vec3<f32>,
+ \\ };
+ \\
+ \\ @group(0) @binding(0)
+ \\ var<storage, read> instances: array<Instance>;
+ \\ struct Instance {
+ \\ model_matrix: mat4x4<f32>,
+ \\ };
+ \\
+ \\ @group(0) @binding(1)
+ \\ var<uniform> uniform_data: UniformData;
+ \\ struct UniformData {
+ \\ view_matrix: mat4x4<f32>,
+ \\ proj_matrix: mat4x4<f32>,
+ \\ };
+ \\
+ \\ @vertex fn main(
+ \\ in: VertexInput,
+ \\ @builtin(instance_index) idx: u32,
+ \\ ) -> VertexOutput {
+ \\ let model_matrix = instances[idx].model_matrix;
+ \\ let camera_matrix = uniform_data.proj_matrix * uniform_data.view_matrix;
+ \\ let matrix = camera_matrix * model_matrix;
+ \\ return VertexOutput(matrix * in.position, in.normal);
+ \\ }
+ ;
+ const vs_module = app.device.createShaderModuleWGSL("default vertex shader", vs);
+ defer vs_module.release();
+
+ const layouts = MeshRenderPipeline.bufferLayouts();
+ const vertex = gpu.VertexState.init(.{
+ .module = vs_module,
+ .entry_point = "main",
+ .buffers = &layouts,
+ });
+
+ const fs =
+ \\ struct VertexOutput {
+ \\ @builtin(position) position: vec4<f32>,
+ \\ @location(0) normal: vec3<f32>,
+ \\ };
+ \\
+ \\ @fragment fn main(in: VertexOutput) -> @location(0) vec4<f32> {
+ \\ let color = (in.normal + 1.0) * 0.5;
+ \\ return vec4<f32>(color, 1.0);
+ \\ }
+ ;
+ const fs_module = app.device.createShaderModuleWGSL("default fragment shader", fs);
+ defer fs_module.release();
+
+ const blend: gpu.BlendState = .{};
+ const color_target = gpu.ColorTargetState{
+ .format = app.swap_chain.format,
+ .blend = &blend,
+ .write_mask = gpu.ColorWriteMaskFlags.all,
+ };
+ const fragment = gpu.FragmentState.init(.{
+ .module = fs_module,
+ .entry_point = "main",
+ .targets = &.{color_target},
+ });
+
+ const buffer_descriptor = gpu.Buffer.Descriptor{
+ .size = @sizeOf(UniformData),
+ .usage = .{ .uniform = true, .copy_dst = true },
+ .mapped_at_creation = .false,
+ };
+ const uniform_buffer = app.device.createBuffer(&buffer_descriptor);
+
+ const bind_group_layout = MeshRenderPipeline.bindGroupLayout(app.device);
+ const bind_group_descriptor = gpu.BindGroup.Descriptor.init(.{
+ .layout = bind_group_layout,
+ .entries = &.{
+ gpu.BindGroup.Entry.buffer(0, mesh_buffer.instances.data, 0, mesh_buffer.instances.size),
+ gpu.BindGroup.Entry.buffer(1, uniform_buffer, 0, @sizeOf(UniformData)),
+ },
+ });
+ const bind_group = app.device.createBindGroup(&bind_group_descriptor);
+
+ const pipeline_layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
+ .bind_group_layouts = &.{bind_group_layout},
+ });
+ const pipeline_layout = app.device.createPipelineLayout(&pipeline_layout_descriptor);
+ defer pipeline_layout.release();
+
+ const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
+ .label = "default render pipeline",
+ .fragment = &fragment,
+ .layout = pipeline_layout,
+ .depth_stencil = &.{
+ .format = .depth24_plus,
+ .depth_write_enabled = .true,
+ .depth_compare = .less,
+ },
+ .vertex = vertex,
+ .multisample = .{},
+ .primitive = .{
+ .topology = .triangle_list,
+ .front_face = .ccw,
+ .cull_mode = .back,
+ },
+ };
+
+ return .{
+ .gpu_pipeline = app.device.createRenderPipeline(&pipeline_descriptor),
+ .mesh_buffer = mesh_buffer,
+ .bind_group = bind_group,
+ .uniform_buffer = uniform_buffer,
+ };
+ }
+
+ fn bufferLayouts() [3]gpu.VertexBufferLayout {
+ const positions = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x4, .shader_location = 0, .offset = 0 },
+ },
+ });
+ const normals = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x3, .shader_location = 1, .offset = 0 },
+ },
+ });
+ const tex_coords = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x3, .shader_location = 2, .offset = 0 },
+ },
+ });
+ return .{ positions, normals, tex_coords };
+ }
+
+ fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
+ const descriptor = gpu.BindGroupLayout.Descriptor.init(.{
+ .entries = &.{
+ gpu.BindGroupLayout.Entry.buffer(0, .{
+ .vertex = true,
+ .fragment = false,
+ }, .read_only_storage, false, 0),
+ gpu.BindGroupLayout.Entry.buffer(1, .{
+ .vertex = true,
+ .fragment = false,
+ }, .uniform, false, 0),
+ },
+ });
+ return device.createBindGroupLayout(&descriptor);
+ }
+
+ pub fn frame(ptr: *anyopaque, pass: *gpu.RenderPassEncoder) void {
+ const self: *MeshRenderPipeline = @ptrCast(@alignCast(ptr));
+ pass.setPipeline(self.gpu_pipeline);
+
+ pass.setVertexBuffer(0, self.mesh_buffer.positions.data, 0, self.mesh_buffer.positions.size);
+ pass.setVertexBuffer(1, self.mesh_buffer.normals.data, 0, self.mesh_buffer.normals.size);
+ pass.setVertexBuffer(2, self.mesh_buffer.tex_coords.data, 0, self.mesh_buffer.tex_coords.size);
+ pass.setBindGroup(0, self.bind_group, null);
+ pass.setIndexBuffer(self.mesh_buffer.indices.data, .uint32, 0, self.mesh_buffer.indices.size);
+
+ for (0..self.mesh_buffer.meshes.items.len) |mesh_idx| {
+ const offset = mesh_idx * 5 * @sizeOf(u32);
+ pass.drawIndexedIndirect(self.mesh_buffer.indirect.data, offset);
+ }
+ }
+
+ pub fn pipeline(self: *MeshRenderPipeline) RenderPipeline {
+ return .{
+ .ptr = self,
+ .frameFn = frame,
+ };
+ }
+};