|
|
|
@ -6,66 +6,136 @@ const zm = @import("zmath"); |
|
|
|
|
const vec = zm.f32x4; |
|
|
|
|
const Mat = zm.Mat; |
|
|
|
|
|
|
|
|
|
const VertexData = struct { |
|
|
|
|
position: [3]f32, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const SceneUniformBuffer = struct { |
|
|
|
|
view_proj_matrix: zm.Mat, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const ModelUniformBuffer = struct { |
|
|
|
|
matrix: zm.Mat, |
|
|
|
|
color: [3]f32, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
pub const App = @This(); |
|
|
|
|
|
|
|
|
|
app_timer: core.Timer, |
|
|
|
|
title_timer: core.Timer, |
|
|
|
|
|
|
|
|
|
pipeline: *gpu.RenderPipeline, |
|
|
|
|
mvp_uniform_buffer: *gpu.Buffer, |
|
|
|
|
mvp_bind_group: *gpu.BindGroup, |
|
|
|
|
scene_uniform_buffer: *gpu.Buffer, |
|
|
|
|
scene_bind_group: *gpu.BindGroup, |
|
|
|
|
model_uniform_buffers: [3]*gpu.Buffer, |
|
|
|
|
model_bind_groups: [3]*gpu.BindGroup, |
|
|
|
|
vertex_count: u32, |
|
|
|
|
vertex_buffer: *gpu.Buffer, |
|
|
|
|
|
|
|
|
|
pub fn init(app: *App) !void { |
|
|
|
|
try core.init(.{}); |
|
|
|
|
|
|
|
|
|
app.app_timer = try core.Timer.start(); |
|
|
|
|
app.title_timer = try core.Timer.start(); |
|
|
|
|
|
|
|
|
|
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); |
|
|
|
|
defer shader_module.release(); |
|
|
|
|
|
|
|
|
|
const blend = gpu.BlendState{}; |
|
|
|
|
const color_target = gpu.ColorTargetState{ |
|
|
|
|
.format = core.descriptor.format, |
|
|
|
|
.blend = &blend, |
|
|
|
|
.write_mask = gpu.ColorWriteMaskFlags.all, |
|
|
|
|
}; |
|
|
|
|
const fragment = gpu.FragmentState.init(.{ |
|
|
|
|
.module = shader_module, |
|
|
|
|
.entry_point = "frag_main", |
|
|
|
|
.targets = &.{color_target}, |
|
|
|
|
}); |
|
|
|
|
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ |
|
|
|
|
.vertex = gpu.VertexState{ |
|
|
|
|
// Set up rendering pipeline. |
|
|
|
|
app.pipeline = core.device.createRenderPipeline(&.{ |
|
|
|
|
.vertex = gpu.VertexState.init(.{ |
|
|
|
|
.module = shader_module, |
|
|
|
|
.entry_point = "vertex_main", |
|
|
|
|
.buffers = &.{ |
|
|
|
|
gpu.VertexBufferLayout.init(.{ |
|
|
|
|
.array_stride = @sizeOf(VertexData), |
|
|
|
|
.step_mode = .vertex, |
|
|
|
|
.attributes = &.{ |
|
|
|
|
.{ .format = .float32x3, .shader_location = 0, .offset = @offsetOf(VertexData, "position") }, |
|
|
|
|
}, |
|
|
|
|
.fragment = &fragment, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
app.app_timer = try core.Timer.start(); |
|
|
|
|
app.title_timer = try core.Timer.start(); |
|
|
|
|
}), |
|
|
|
|
}, |
|
|
|
|
}), |
|
|
|
|
.fragment = &gpu.FragmentState.init(.{ |
|
|
|
|
.module = shader_module, |
|
|
|
|
.entry_point = "frag_main", |
|
|
|
|
.targets = &.{.{ .format = core.descriptor.format }}, |
|
|
|
|
}), |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor); |
|
|
|
|
// Set up uniform buffers and bind groups. |
|
|
|
|
|
|
|
|
|
app.mvp_uniform_buffer = core.device.createBuffer(&.{ |
|
|
|
|
// The "scene" uniform contains information for each rendered scene. |
|
|
|
|
app.scene_uniform_buffer = core.device.createBuffer(&.{ |
|
|
|
|
.usage = .{ .copy_dst = true, .uniform = true }, |
|
|
|
|
.size = @sizeOf(zm.Mat), |
|
|
|
|
.size = @sizeOf(SceneUniformBuffer), |
|
|
|
|
.mapped_at_creation = .false, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
app.mvp_bind_group = core.device.createBindGroup( |
|
|
|
|
app.scene_bind_group = core.device.createBindGroup( |
|
|
|
|
&gpu.BindGroup.Descriptor.init(.{ |
|
|
|
|
.layout = app.pipeline.getBindGroupLayout(0), |
|
|
|
|
.entries = &.{ |
|
|
|
|
gpu.BindGroup.Entry.buffer(0, app.mvp_uniform_buffer, 0, @sizeOf(zm.Mat)), |
|
|
|
|
gpu.BindGroup.Entry.buffer(0, app.scene_uniform_buffer, 0, @sizeOf(SceneUniformBuffer)), |
|
|
|
|
}, |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// The "model" uniforms contain information about how to render each model. |
|
|
|
|
for (0..3) |i| { |
|
|
|
|
app.model_uniform_buffers[i] = core.device.createBuffer(&.{ |
|
|
|
|
.usage = .{ .copy_dst = true, .uniform = true }, |
|
|
|
|
.size = @sizeOf(ModelUniformBuffer), |
|
|
|
|
.mapped_at_creation = .false, |
|
|
|
|
}); |
|
|
|
|
app.model_bind_groups[i] = core.device.createBindGroup( |
|
|
|
|
&gpu.BindGroup.Descriptor.init(.{ |
|
|
|
|
.layout = app.pipeline.getBindGroupLayout(1), |
|
|
|
|
.entries = &.{ |
|
|
|
|
gpu.BindGroup.Entry.buffer(0, app.model_uniform_buffers[i], 0, @sizeOf(ModelUniformBuffer)), |
|
|
|
|
}, |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
// Upload model render information (matrix + color) to the GPU. |
|
|
|
|
core.queue.writeBuffer(app.model_uniform_buffers[0], 0, &[_]ModelUniformBuffer{.{ |
|
|
|
|
.matrix = zm.transpose(zm.translation(-1.0, 0.25, 0.0)), |
|
|
|
|
.color = .{ 1.0, 0.0, 0.0 }, |
|
|
|
|
}}); |
|
|
|
|
core.queue.writeBuffer(app.model_uniform_buffers[1], 0, &[_]ModelUniformBuffer{.{ |
|
|
|
|
.matrix = zm.transpose(zm.translation(0.0, -0.25, 0.0)), |
|
|
|
|
.color = .{ 0.0, 1.0, 0.0 }, |
|
|
|
|
}}); |
|
|
|
|
core.queue.writeBuffer(app.model_uniform_buffers[2], 0, &[_]ModelUniformBuffer{.{ |
|
|
|
|
.matrix = zm.transpose(zm.translation(1.0, 0.0, 0.0)), |
|
|
|
|
.color = .{ 0.0, 0.0, 1.0 }, |
|
|
|
|
}}); |
|
|
|
|
|
|
|
|
|
// Set up vertex buffer, containing the vertex data we want to draw. |
|
|
|
|
const vertices = [_]VertexData{ |
|
|
|
|
.{ .position = .{ 0.0, 0.5, 0.0 } }, |
|
|
|
|
.{ .position = .{ -0.5, -0.5, 0.0 } }, |
|
|
|
|
.{ .position = .{ 0.5, -0.5, 0.0 } }, |
|
|
|
|
}; |
|
|
|
|
app.vertex_count = vertices.len; |
|
|
|
|
app.vertex_buffer = core.device.createBuffer(&.{ |
|
|
|
|
.size = app.vertex_count * @sizeOf(VertexData), |
|
|
|
|
.usage = .{ .vertex = true, .copy_dst = true }, |
|
|
|
|
.mapped_at_creation = .false, |
|
|
|
|
}); |
|
|
|
|
// Upload vertex buffer to the GPU. |
|
|
|
|
core.queue.writeBuffer(app.vertex_buffer, 0, &vertices); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn deinit(app: *App) void { |
|
|
|
|
// Using `defer` here, so we can specify them |
|
|
|
|
// in the order they were created in `init`. |
|
|
|
|
defer core.deinit(); |
|
|
|
|
defer app.pipeline.release(); |
|
|
|
|
defer app.mvp_uniform_buffer.release(); |
|
|
|
|
defer app.mvp_bind_group.release(); |
|
|
|
|
defer app.scene_uniform_buffer.release(); |
|
|
|
|
defer app.scene_bind_group.release(); |
|
|
|
|
defer for (app.model_uniform_buffers) |b| b.release(); |
|
|
|
|
defer for (app.model_bind_groups) |g| g.release(); |
|
|
|
|
defer app.vertex_buffer.release(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn update(app: *App) !bool { |
|
|
|
@ -101,21 +171,25 @@ pub fn update(app: *App) !bool { |
|
|
|
|
// Once rendering is done (hence `defer`), swap back buffer to the front to display. |
|
|
|
|
defer core.swap_chain.present(); |
|
|
|
|
|
|
|
|
|
const color_attachment = gpu.RenderPassColorAttachment{ |
|
|
|
|
const render_pass_info = gpu.RenderPassDescriptor.init(.{ |
|
|
|
|
.color_attachments = &.{.{ |
|
|
|
|
.view = back_buffer_view, |
|
|
|
|
.clear_value = std.mem.zeroes(gpu.Color), |
|
|
|
|
.load_op = .clear, |
|
|
|
|
.store_op = .store, |
|
|
|
|
}; |
|
|
|
|
const render_pass_info = gpu.RenderPassDescriptor.init(.{ |
|
|
|
|
.color_attachments = &.{color_attachment}, |
|
|
|
|
}}, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// Create a `WGPUCommandEncoder` which provides an interface for recording GPU commands. |
|
|
|
|
const encoder = core.device.createCommandEncoder(null); |
|
|
|
|
defer encoder.release(); |
|
|
|
|
|
|
|
|
|
encoder.writeBuffer(app.mvp_uniform_buffer, 0, &[_]zm.Mat{zm.transpose(view_proj_matrix)}); |
|
|
|
|
// Write to the scene uniform buffer for this set of commands. |
|
|
|
|
encoder.writeBuffer(app.scene_uniform_buffer, 0, &[_]SceneUniformBuffer{.{ |
|
|
|
|
// All matrices the GPU has to work with need to be transposed, |
|
|
|
|
// because WebGPU uses column-major matrices while zmath is row-major. |
|
|
|
|
.view_proj_matrix = zm.transpose(view_proj_matrix), |
|
|
|
|
}}); |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
const pass = encoder.beginRenderPass(&render_pass_info); |
|
|
|
@ -123,10 +197,15 @@ pub fn update(app: *App) !bool { |
|
|
|
|
defer pass.end(); |
|
|
|
|
|
|
|
|
|
pass.setPipeline(app.pipeline); |
|
|
|
|
pass.setBindGroup(0, app.mvp_bind_group, &.{}); |
|
|
|
|
|
|
|
|
|
// Draw three triangles with the help of a specialized shader. |
|
|
|
|
pass.draw(9, 1, 0, 0); |
|
|
|
|
pass.setBindGroup(0, app.scene_bind_group, &.{}); |
|
|
|
|
pass.setVertexBuffer(0, app.vertex_buffer, 0, app.vertex_count * @sizeOf(VertexData)); |
|
|
|
|
|
|
|
|
|
for (app.model_bind_groups) |model_bind_group| { |
|
|
|
|
// Set the model bind group for a specific model we want to render. |
|
|
|
|
pass.setBindGroup(1, model_bind_group, &.{}); |
|
|
|
|
// Draw the vertices in `vertex_buffer`. |
|
|
|
|
pass.draw(app.vertex_count, 1, 0, 0); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Finish recording commands, creating a `WGPUCommandBuffer`. |
|
|
|
|