mach/examples/two-cubes/main.zig
iddev5 3bb45c75a1 mach: introduce cross platform Timer abstraction
This Timer uses std.time.Timer as backing timer in native platforms, and
will use custom timers for special platforms (wasm, android?, ios?).

Unlike std.time.Timer, its primary API is focused on floats. Also meant
to provides some convenient functions alongside base ones.

Follows std.time.Timer API, but methods by default return f32 i.e
non-precise variant with precise variants available returning u64.
2022-05-17 23:56:41 -07:00

244 lines
7.8 KiB
Zig
Executable file

const std = @import("std");
const mach = @import("mach");
const gpu = @import("gpu");
const glfw = @import("glfw");
const zm = @import("zmath");
const Vertex = @import("cube_mesh.zig").Vertex;
const vertices = @import("cube_mesh.zig").vertices;
const UniformBufferObject = struct {
mat: zm.Mat,
};
var timer: mach.Timer = undefined;
pipeline: gpu.RenderPipeline,
queue: gpu.Queue,
vertex_buffer: gpu.Buffer,
uniform_buffer: gpu.Buffer,
bind_group1: gpu.BindGroup,
bind_group2: gpu.BindGroup,
const App = @This();
pub fn init(app: *App, engine: *mach.Engine) !void {
timer = try mach.Timer.start();
engine.core.setKeyCallback(struct {
fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void {
if (action == .press) {
switch (key) {
.space => eng.core.setShouldClose(true),
else => {},
}
}
}
}.callback);
try engine.core.setSizeLimits(.{ .width = 20, .height = 20 }, .{ .width = null, .height = null });
const vs_module = engine.gpu_driver.device.createShaderModule(&.{
.label = "my vertex shader",
.code = .{ .wgsl = @embedFile("vert.wgsl") },
});
const vertex_attributes = [_]gpu.VertexAttribute{
.{ .format = .float32x4, .offset = @offsetOf(Vertex, "pos"), .shader_location = 0 },
.{ .format = .float32x2, .offset = @offsetOf(Vertex, "uv"), .shader_location = 1 },
};
const vertex_buffer_layout = gpu.VertexBufferLayout{
.array_stride = @sizeOf(Vertex),
.step_mode = .vertex,
.attribute_count = vertex_attributes.len,
.attributes = &vertex_attributes,
};
const fs_module = engine.gpu_driver.device.createShaderModule(&.{
.label = "my fragment shader",
.code = .{ .wgsl = @embedFile("frag.wgsl") },
});
const blend = gpu.BlendState{
.color = .{
.operation = .add,
.src_factor = .one,
.dst_factor = .zero,
},
.alpha = .{
.operation = .add,
.src_factor = .one,
.dst_factor = .zero,
},
};
const color_target = gpu.ColorTargetState{
.format = engine.gpu_driver.swap_chain_format,
.blend = &blend,
.write_mask = gpu.ColorWriteMask.all,
};
const fragment = gpu.FragmentState{
.module = fs_module,
.entry_point = "main",
.targets = &.{color_target},
.constants = null,
};
const bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
const bgl = engine.gpu_driver.device.createBindGroupLayout(
&gpu.BindGroupLayout.Descriptor{
.entries = &.{bgle},
},
);
const bind_group_layouts = [_]gpu.BindGroupLayout{bgl};
const pipeline_layout = engine.gpu_driver.device.createPipelineLayout(&.{
.bind_group_layouts = &bind_group_layouts,
});
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
.fragment = &fragment,
.layout = pipeline_layout,
.depth_stencil = null,
.vertex = .{
.module = vs_module,
.entry_point = "main",
.buffers = &.{vertex_buffer_layout},
},
.multisample = .{
.count = 1,
.mask = 0xFFFFFFFF,
.alpha_to_coverage_enabled = false,
},
.primitive = .{
.front_face = .ccw,
.cull_mode = .back,
.topology = .triangle_list,
.strip_index_format = .none,
},
};
const queue = engine.gpu_driver.device.getQueue();
const vertex_buffer = engine.gpu_driver.device.createBuffer(&.{
.usage = .{ .vertex = true },
.size = @sizeOf(Vertex) * vertices.len,
.mapped_at_creation = true,
});
var vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
std.mem.copy(Vertex, vertex_mapped, vertices[0..]);
vertex_buffer.unmap();
// uniformBindGroup offset must be 256-byte aligned
const uniform_offset = 256;
const uniform_buffer = engine.gpu_driver.device.createBuffer(&.{
.usage = .{ .uniform = true, .copy_dst = true },
.size = @sizeOf(UniformBufferObject) + uniform_offset,
.mapped_at_creation = false,
});
const bind_group1 = engine.gpu_driver.device.createBindGroup(
&gpu.BindGroup.Descriptor{
.layout = bgl,
.entries = &.{
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject)),
},
},
);
const bind_group2 = engine.gpu_driver.device.createBindGroup(
&gpu.BindGroup.Descriptor{
.layout = bgl,
.entries = &.{
gpu.BindGroup.Entry.buffer(0, uniform_buffer, uniform_offset, @sizeOf(UniformBufferObject)),
},
},
);
app.pipeline = engine.gpu_driver.device.createRenderPipeline(&pipeline_descriptor);
app.queue = queue;
app.vertex_buffer = vertex_buffer;
app.uniform_buffer = uniform_buffer;
app.bind_group1 = bind_group1;
app.bind_group2 = bind_group2;
vs_module.release();
fs_module.release();
pipeline_layout.release();
bgl.release();
}
pub fn deinit(app: *App, _: *mach.Engine) void {
app.vertex_buffer.release();
app.uniform_buffer.release();
app.bind_group1.release();
app.bind_group2.release();
}
pub fn update(app: *App, engine: *mach.Engine) !bool {
const back_buffer_view = engine.gpu_driver.swap_chain.?.getCurrentTextureView();
const color_attachment = gpu.RenderPassColorAttachment{
.view = back_buffer_view,
.resolve_target = null,
.clear_value = std.mem.zeroes(gpu.Color),
.load_op = .clear,
.store_op = .store,
};
const encoder = engine.gpu_driver.device.createCommandEncoder(null);
const render_pass_info = gpu.RenderPassEncoder.Descriptor{
.color_attachments = &.{color_attachment},
.depth_stencil_attachment = null,
};
{
const time = timer.read();
const rotation1 = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
const rotation2 = zm.mul(zm.rotationZ(time * (std.math.pi / 2.0)), zm.rotationX(time * (std.math.pi / 2.0)));
const model1 = zm.mul(rotation1, zm.translation(-2, 0, 0));
const model2 = zm.mul(rotation2, zm.translation(2, 0, 0));
const view = zm.lookAtRh(
zm.f32x4(0, -4, 2, 1),
zm.f32x4(0, 0, 0, 1),
zm.f32x4(0, 0, 1, 0),
);
const proj = zm.perspectiveFovRh(
(2.0 * std.math.pi / 5.0),
@intToFloat(f32, engine.gpu_driver.current_desc.width) / @intToFloat(f32, engine.gpu_driver.current_desc.height),
1,
100,
);
const mvp1 = zm.mul(zm.mul(model1, view), proj);
const mvp2 = zm.mul(zm.mul(model2, view), proj);
const ubo1 = UniformBufferObject{
.mat = zm.transpose(mvp1),
};
const ubo2 = UniformBufferObject{
.mat = zm.transpose(mvp2),
};
encoder.writeBuffer(app.uniform_buffer, 0, UniformBufferObject, &.{ubo1});
// bind_group2 offset
encoder.writeBuffer(app.uniform_buffer, 256, UniformBufferObject, &.{ubo2});
}
const pass = encoder.beginRenderPass(&render_pass_info);
pass.setPipeline(app.pipeline);
pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
pass.setBindGroup(0, app.bind_group1, &.{0});
pass.draw(vertices.len, 1, 0, 0);
pass.setBindGroup(0, app.bind_group2, &.{0});
pass.draw(vertices.len, 1, 0, 0);
pass.end();
pass.release();
var command = encoder.finish(null);
encoder.release();
app.queue.submit(&.{command});
command.release();
engine.gpu_driver.swap_chain.?.present();
back_buffer_view.release();
return true;
}